provision: Add idmap database handle to the result of provision
[idra/samba.git] / source4 / scripting / python / samba / provision / __init__.py
1
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 __docformat__ = "restructuredText"
29
30 from base64 import b64encode
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import shutil
41 import string
42
43 import ldb
44
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49     Ldb,
50     check_all_substituted,
51     read_and_sub_file,
52     setup_file,
53     substitute_var,
54     valid_netbios_name,
55     version,
56     )
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
59     SEC_CHAN_BDC,
60     SEC_CHAN_WKSTA,
61     )
62 from samba.dsdb import (
63     DS_DOMAIN_FUNCTION_2003,
64     DS_DOMAIN_FUNCTION_2008_R2,
65     ENC_ALL_TYPES,
66     )
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
72     ExistingBackend,
73     FDSBackend,
74     LDBBackend,
75     OpenLDAPBackend,
76     )
77 from samba.provision.sambadns import setup_ad_dns
78
79 import samba.param
80 import samba.registry
81 from samba.schema import Schema
82 from samba.samdb import SamDB
83 from samba.dbchecker import dbcheck
84
85
86 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
87 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
88 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
89 DEFAULTSITE = "Default-First-Site-Name"
90 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
91
92
93 def setup_path(file):
94     """Return an absolute path to the provision tempate file specified by file"""
95     return os.path.join(samba.param.setup_dir(), file)
96
97 # Descriptors of naming contexts and other important objects
98
99 # "get_schema_descriptor" is located in "schema.py"
100
101 def get_config_descriptor(domain_sid):
102     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
109            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
110            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
111            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
112            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
116            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
117     sec = security.descriptor.from_sddl(sddl, domain_sid)
118     return ndr_pack(sec)
119
120
121 def get_domain_descriptor(domain_sid):
122     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123         "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143     "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147     "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148     "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149     "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151     "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152     "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158     "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159     "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160     "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
161     "(A;;RPRC;;;RU)" \
162     "(A;CI;LC;;;RU)" \
163     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
164     "(A;;RP;;;WD)" \
165     "(A;;RPLCLORC;;;ED)" \
166     "(A;;RPLCLORC;;;AU)" \
167     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168     "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171     sec = security.descriptor.from_sddl(sddl, domain_sid)
172     return ndr_pack(sec)
173
174
175 class ProvisionPaths(object):
176
177     def __init__(self):
178         self.shareconf = None
179         self.hklm = None
180         self.hkcu = None
181         self.hkcr = None
182         self.hku = None
183         self.hkpd = None
184         self.hkpt = None
185         self.samdb = None
186         self.idmapdb = None
187         self.secrets = None
188         self.keytab = None
189         self.dns_keytab = None
190         self.dns = None
191         self.winsdb = None
192         self.private_dir = None
193
194
195 class ProvisionNames(object):
196
197     def __init__(self):
198         self.rootdn = None
199         self.domaindn = None
200         self.configdn = None
201         self.schemadn = None
202         self.ldapmanagerdn = None
203         self.dnsdomain = None
204         self.realm = None
205         self.netbiosname = None
206         self.domain = None
207         self.hostname = None
208         self.sitename = None
209         self.smbconf = None
210
211 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
212     """Get key provision parameters (realm, domain, ...) from a given provision
213
214     :param samdb: An LDB object connected to the sam.ldb file
215     :param secretsdb: An LDB object connected to the secrets.ldb file
216     :param idmapdb: An LDB object connected to the idmap.ldb file
217     :param paths: A list of path to provision object
218     :param smbconf: Path to the smb.conf file
219     :param lp: A LoadParm object
220     :return: A list of key provision parameters
221     """
222     names = ProvisionNames()
223     names.adminpass = None
224
225     # NT domain, kerberos realm, root dn, domain dn, domain dns name
226     names.domain = string.upper(lp.get("workgroup"))
227     names.realm = lp.get("realm")
228     basedn = "DC=" + names.realm.replace(".",",DC=")
229     names.dnsdomain = names.realm.lower()
230     names.realm = string.upper(names.realm)
231     # netbiosname
232     # Get the netbiosname first (could be obtained from smb.conf in theory)
233     res = secretsdb.search(expression="(flatname=%s)" %
234                             names.domain,base="CN=Primary Domains",
235                             scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
236     names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
237
238     names.smbconf = smbconf
239
240     # That's a bit simplistic but it's ok as long as we have only 3
241     # partitions
242     current = samdb.search(expression="(objectClass=*)",
243         base="", scope=ldb.SCOPE_BASE,
244         attrs=["defaultNamingContext", "schemaNamingContext",
245                "configurationNamingContext","rootDomainNamingContext"])
246
247     names.configdn = current[0]["configurationNamingContext"]
248     configdn = str(names.configdn)
249     names.schemadn = current[0]["schemaNamingContext"]
250     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
251                                        current[0]["defaultNamingContext"][0]))):
252         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
253                                  "is not the same ..." % (paths.samdb,
254                                     str(current[0]["defaultNamingContext"][0]),
255                                     paths.smbconf, basedn)))
256
257     names.domaindn=current[0]["defaultNamingContext"]
258     names.rootdn=current[0]["rootDomainNamingContext"]
259     # default site name
260     res3 = samdb.search(expression="(objectClass=site)",
261         base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
262     names.sitename = str(res3[0]["cn"])
263
264     # dns hostname and server dn
265     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
266                             base="OU=Domain Controllers,%s" % basedn,
267                             scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
268     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
269
270     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
271                                 attrs=[], base=configdn)
272     names.serverdn = server_res[0].dn
273
274     # invocation id/objectguid
275     res5 = samdb.search(expression="(objectClass=*)",
276             base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
277             attrs=["invocationID", "objectGUID"])
278     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
279     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
280
281     # domain guid/sid
282     res6 = samdb.search(expression="(objectClass=*)", base=basedn,
283             scope=ldb.SCOPE_BASE, attrs=["objectGUID",
284                 "objectSid","msDS-Behavior-Version" ])
285     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
286     names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
287     if res6[0].get("msDS-Behavior-Version") is None or \
288         int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
289         names.domainlevel = DS_DOMAIN_FUNCTION_2000
290     else:
291         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
292
293     # policy guid
294     res7 = samdb.search(expression="(displayName=Default Domain Policy)",
295                         base="CN=Policies,CN=System," + basedn,
296                         scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
297     names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
298     # dc policy guid
299     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
300                                    " Policy)",
301                             base="CN=Policies,CN=System," + basedn,
302                             scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
303     if len(res8) == 1:
304         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
305     else:
306         names.policyid_dc = None
307     res9 = idmapdb.search(expression="(cn=%s)" %
308                             (security.SID_BUILTIN_ADMINISTRATORS),
309                             attrs=["xidNumber"])
310     if len(res9) == 1:
311         names.wheel_gid = res9[0]["xidNumber"]
312     else:
313         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
314     return names
315
316 def update_provision_usn(samdb, low, high, id, replace=False):
317     """Update the field provisionUSN in sam.ldb
318
319     This field is used to track range of USN modified by provision and
320     upgradeprovision.
321     This value is used afterward by next provision to figure out if
322     the field have been modified since last provision.
323
324     :param samdb: An LDB object connect to sam.ldb
325     :param low: The lowest USN modified by this upgrade
326     :param high: The highest USN modified by this upgrade
327     :param id: The invocation id of the samba's dc
328     :param replace: A boolean indicating if the range should replace any
329                     existing one or appended (default)
330     """
331
332     tab = []
333     if not replace:
334         entry = samdb.search(base="@PROVISION",
335                              scope=ldb.SCOPE_BASE,
336                              attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
337         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
338             if not re.search(';', e):
339                 e = "%s;%s" % (e, id)
340             tab.append(str(e))
341
342     tab.append("%s-%s;%s" % (low, high, id))
343     delta = ldb.Message()
344     delta.dn = ldb.Dn(samdb, "@PROVISION")
345     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
346         ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
347     entry = samdb.search(expression='provisionnerID=*',
348                          base="@PROVISION", scope=ldb.SCOPE_BASE,
349                          attrs=["provisionnerID"])
350     if len(entry) == 0 or len(entry[0]) == 0:
351         delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
352     samdb.modify(delta)
353
354
355 def set_provision_usn(samdb, low, high, id):
356     """Set the field provisionUSN in sam.ldb
357     This field is used to track range of USN modified by provision and
358     upgradeprovision.
359     This value is used afterward by next provision to figure out if
360     the field have been modified since last provision.
361
362     :param samdb: An LDB object connect to sam.ldb
363     :param low: The lowest USN modified by this upgrade
364     :param high: The highest USN modified by this upgrade
365     :param id: The invocationId of the provision"""
366
367     tab = []
368     tab.append("%s-%s;%s" % (low, high, id))
369
370     delta = ldb.Message()
371     delta.dn = ldb.Dn(samdb, "@PROVISION")
372     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
373         ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
374     samdb.add(delta)
375
376
377 def get_max_usn(samdb,basedn):
378     """ This function return the biggest USN present in the provision
379
380     :param samdb: A LDB object pointing to the sam.ldb
381     :param basedn: A string containing the base DN of the provision
382                     (ie. DC=foo, DC=bar)
383     :return: The biggest USN in the provision"""
384
385     res = samdb.search(expression="objectClass=*",base=basedn,
386                          scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
387                          controls=["search_options:1:2",
388                                    "server_sort:1:1:uSNChanged",
389                                    "paged_results:1:1"])
390     return res[0]["uSNChanged"]
391
392
393 def get_last_provision_usn(sam):
394     """Get USNs ranges modified by a provision or an upgradeprovision
395
396     :param sam: An LDB object pointing to the sam.ldb
397     :return: a dictionnary which keys are invocation id and values are an array
398              of integer representing the different ranges
399     """
400     try:
401         entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
402                            base="@PROVISION", scope=ldb.SCOPE_BASE,
403                            attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
404     except ldb.LdbError, (ecode, emsg):
405         if ecode == ldb.ERR_NO_SUCH_OBJECT:
406             return None
407         raise
408     if len(entry):
409         myids = []
410         range = {}
411         p = re.compile(r'-')
412         if entry[0].get("provisionnerID"):
413             for e in entry[0]["provisionnerID"]:
414                 myids.append(str(e))
415         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
416             tab1 = str(r).split(';')
417             if len(tab1) == 2:
418                 id = tab1[1]
419             else:
420                 id = "default"
421             if (len(myids) > 0 and id not in myids):
422                 continue
423             tab2 = p.split(tab1[0])
424             if range.get(id) == None:
425                 range[id] = []
426             range[id].append(tab2[0])
427             range[id].append(tab2[1])
428         return range
429     else:
430         return None
431
432
433 class ProvisionResult(object):
434
435     def __init__(self):
436         self.paths = None
437         self.domaindn = None
438         self.lp = None
439         self.samdb = None
440         self.idmap = None
441
442
443 def check_install(lp, session_info, credentials):
444     """Check whether the current install seems ok.
445
446     :param lp: Loadparm context
447     :param session_info: Session information
448     :param credentials: Credentials
449     """
450     if lp.get("realm") == "":
451         raise Exception("Realm empty")
452     samdb = Ldb(lp.samdb_url(), session_info=session_info,
453             credentials=credentials, lp=lp)
454     if len(samdb.search("(cn=Administrator)")) != 1:
455         raise ProvisioningError("No administrator account found")
456
457
458 def findnss(nssfn, names):
459     """Find a user or group from a list of possibilities.
460
461     :param nssfn: NSS Function to try (should raise KeyError if not found)
462     :param names: Names to check.
463     :return: Value return by first names list.
464     """
465     for name in names:
466         try:
467             return nssfn(name)
468         except KeyError:
469             pass
470     raise KeyError("Unable to find user/group in %r" % names)
471
472
473 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
474 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
475
476
477 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
478     """Setup a ldb in the private dir.
479
480     :param ldb: LDB file to import data into
481     :param ldif_path: Path of the LDIF file to load
482     :param subst_vars: Optional variables to subsitute in LDIF.
483     :param nocontrols: Optional list of controls, can be None for no controls
484     """
485     assert isinstance(ldif_path, str)
486     data = read_and_sub_file(ldif_path, subst_vars)
487     ldb.add_ldif(data, controls)
488
489
490 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
491     """Modify a ldb in the private dir.
492
493     :param ldb: LDB object.
494     :param ldif_path: LDIF file path.
495     :param subst_vars: Optional dictionary with substitution variables.
496     """
497     data = read_and_sub_file(ldif_path, subst_vars)
498     ldb.modify_ldif(data, controls)
499
500
501 def setup_ldb(ldb, ldif_path, subst_vars):
502     """Import a LDIF a file into a LDB handle, optionally substituting
503     variables.
504
505     :note: Either all LDIF data will be added or none (using transactions).
506
507     :param ldb: LDB file to import into.
508     :param ldif_path: Path to the LDIF file.
509     :param subst_vars: Dictionary with substitution variables.
510     """
511     assert ldb is not None
512     ldb.transaction_start()
513     try:
514         setup_add_ldif(ldb, ldif_path, subst_vars)
515     except Exception:
516         ldb.transaction_cancel()
517         raise
518     else:
519         ldb.transaction_commit()
520
521
522 def provision_paths_from_lp(lp, dnsdomain):
523     """Set the default paths for provisioning.
524
525     :param lp: Loadparm context.
526     :param dnsdomain: DNS Domain name
527     """
528     paths = ProvisionPaths()
529     paths.private_dir = lp.get("private dir")
530
531     # This is stored without path prefix for the "privateKeytab" attribute in
532     # "secrets_dns.ldif".
533     paths.dns_keytab = "dns.keytab"
534     paths.keytab = "secrets.keytab"
535
536     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
537     paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
538     paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
539     paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
540     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
541     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
542     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
543     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
544     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
545     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
546     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
547     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
548     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
549     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
550     paths.phpldapadminconfig = os.path.join(paths.private_dir,
551                                             "phpldapadmin-config.php")
552     paths.hklm = "hklm.ldb"
553     paths.hkcr = "hkcr.ldb"
554     paths.hkcu = "hkcu.ldb"
555     paths.hku = "hku.ldb"
556     paths.hkpd = "hkpd.ldb"
557     paths.hkpt = "hkpt.ldb"
558     paths.sysvol = lp.get("path", "sysvol")
559     paths.netlogon = lp.get("path", "netlogon")
560     paths.smbconf = lp.configfile
561     return paths
562
563
564 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
565                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
566                 schemadn=None, serverdn=None, sitename=None):
567     """Guess configuration settings to use."""
568
569     if hostname is None:
570         hostname = socket.gethostname().split(".")[0]
571
572     netbiosname = lp.get("netbios name")
573     if netbiosname is None:
574         netbiosname = hostname
575         # remove forbidden chars
576         newnbname = ""
577         for x in netbiosname:
578             if x.isalnum() or x in VALID_NETBIOS_CHARS:
579                 newnbname = "%s%c" % (newnbname, x)
580         # force the length to be <16
581         netbiosname = newnbname[0:15]
582     assert netbiosname is not None
583     netbiosname = netbiosname.upper()
584     if not valid_netbios_name(netbiosname):
585         raise InvalidNetbiosName(netbiosname)
586
587     if dnsdomain is None:
588         dnsdomain = lp.get("realm")
589         if dnsdomain is None or dnsdomain == "":
590             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
591
592     dnsdomain = dnsdomain.lower()
593
594     if serverrole is None:
595         serverrole = lp.get("server role")
596         if serverrole is None:
597             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
598
599     serverrole = serverrole.lower()
600
601     realm = dnsdomain.upper()
602
603     if lp.get("realm") == "":
604         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
605
606     if lp.get("realm").upper() != realm:
607         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
608
609     if lp.get("server role").lower() != serverrole:
610         raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
611
612     if serverrole == "domain controller":
613         if domain is None:
614             # This will, for better or worse, default to 'WORKGROUP'
615             domain = lp.get("workgroup")
616         domain = domain.upper()
617
618         if lp.get("workgroup").upper() != domain:
619             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
620
621         if domaindn is None:
622             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
623
624         if domain == netbiosname:
625             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
626     else:
627         domain = netbiosname
628         if domaindn is None:
629             domaindn = "DC=" + netbiosname
630
631     if not valid_netbios_name(domain):
632         raise InvalidNetbiosName(domain)
633
634     if hostname.upper() == realm:
635         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
636     if netbiosname.upper() == realm:
637         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
638     if domain == realm:
639         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
640
641     if rootdn is None:
642        rootdn = domaindn
643
644     if configdn is None:
645         configdn = "CN=Configuration," + rootdn
646     if schemadn is None:
647         schemadn = "CN=Schema," + configdn
648
649     if sitename is None:
650         sitename=DEFAULTSITE
651
652     names = ProvisionNames()
653     names.rootdn = rootdn
654     names.domaindn = domaindn
655     names.configdn = configdn
656     names.schemadn = schemadn
657     names.ldapmanagerdn = "CN=Manager," + rootdn
658     names.dnsdomain = dnsdomain
659     names.domain = domain
660     names.realm = realm
661     names.netbiosname = netbiosname
662     names.hostname = hostname
663     names.sitename = sitename
664     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
665         netbiosname, sitename, configdn)
666
667     return names
668
669
670 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
671                  targetdir, sid_generator="internal", eadb=False, lp=None):
672     """Create a new smb.conf file based on a couple of basic settings.
673     """
674     assert smbconf is not None
675     if hostname is None:
676         hostname = socket.gethostname().split(".")[0]
677         netbiosname = hostname.upper()
678         # remove forbidden chars
679         newnbname = ""
680         for x in netbiosname:
681             if x.isalnum() or x in VALID_NETBIOS_CHARS:
682                 newnbname = "%s%c" % (newnbname, x)
683         #force the length to be <16
684         netbiosname = newnbname[0:15]
685     else:
686         netbiosname = hostname.upper()
687
688     if serverrole is None:
689         serverrole = "standalone"
690
691     assert serverrole in ("domain controller", "member server", "standalone")
692     if serverrole == "domain controller":
693         smbconfsuffix = "dc"
694     elif serverrole == "member server":
695         smbconfsuffix = "member"
696     elif serverrole == "standalone":
697         smbconfsuffix = "standalone"
698
699     if sid_generator is None:
700         sid_generator = "internal"
701
702     assert domain is not None
703     domain = domain.upper()
704
705     assert realm is not None
706     realm = realm.upper()
707
708     if lp is None:
709         lp = samba.param.LoadParm()
710     #Load non-existant file
711     if os.path.exists(smbconf):
712         lp.load(smbconf)
713     if eadb and not lp.get("posix:eadb"):
714         if targetdir is not None:
715             privdir = os.path.join(targetdir, "private")
716         else:
717             privdir = lp.get("private dir")
718         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
719
720     if targetdir is not None:
721         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
722         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
723         statedir_line = "state directory = " + os.path.abspath(targetdir)
724         cachedir_line = "cache directory = " + os.path.abspath(targetdir)
725
726         lp.set("lock dir", os.path.abspath(targetdir))
727         lp.set("state directory", os.path.abspath(targetdir))
728         lp.set("cache directory", os.path.abspath(targetdir))
729     else:
730         privatedir_line = ""
731         lockdir_line = ""
732         statedir_line = ""
733         cachedir_line = ""
734
735     sysvol = os.path.join(lp.get("state directory"), "sysvol")
736     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
737
738     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
739                smbconf, {
740             "NETBIOS_NAME": netbiosname,
741             "DOMAIN": domain,
742             "REALM": realm,
743             "SERVERROLE": serverrole,
744             "NETLOGONPATH": netlogon,
745             "SYSVOLPATH": sysvol,
746             "PRIVATEDIR_LINE": privatedir_line,
747             "LOCKDIR_LINE": lockdir_line,
748             "STATEDIR_LINE": statedir_line,
749             "CACHEDIR_LINE": cachedir_line
750             })
751
752     # reload the smb.conf
753     lp.load(smbconf)
754
755     # and dump it without any values that are the default
756     # this ensures that any smb.conf parameters that were set
757     # on the provision/join command line are set in the resulting smb.conf
758     f = open(smbconf, mode='w')
759     lp.dump(f, False)
760     f.close()
761
762
763
764 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
765                         users_gid, wheel_gid):
766     """setup reasonable name mappings for sam names to unix names.
767
768     :param samdb: SamDB object.
769     :param idmap: IDmap db object.
770     :param sid: The domain sid.
771     :param domaindn: The domain DN.
772     :param root_uid: uid of the UNIX root user.
773     :param nobody_uid: uid of the UNIX nobody user.
774     :param users_gid: gid of the UNIX users group.
775     :param wheel_gid: gid of the UNIX wheel group.
776     """
777     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
778     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
779
780     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
781     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
782
783
784 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
785                            provision_backend, names, schema, serverrole,
786                            erase=False):
787     """Setup the partitions for the SAM database.
788
789     Alternatively, provision() may call this, and then populate the database.
790
791     :note: This will wipe the Sam Database!
792
793     :note: This function always removes the local SAM LDB file. The erase
794         parameter controls whether to erase the existing data, which
795         may not be stored locally but in LDAP.
796
797     """
798     assert session_info is not None
799
800     # We use options=["modules:"] to stop the modules loading - we
801     # just want to wipe and re-initialise the database, not start it up
802
803     try:
804         os.unlink(samdb_path)
805     except OSError:
806         pass
807
808     samdb = Ldb(url=samdb_path, session_info=session_info,
809                 lp=lp, options=["modules:"])
810
811     ldap_backend_line = "# No LDAP backend"
812     if provision_backend.type is not "ldb":
813         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
814
815     samdb.transaction_start()
816     try:
817         logger.info("Setting up sam.ldb partitions and settings")
818         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
819                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
820                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
821                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
822                 "LDAP_BACKEND_LINE": ldap_backend_line,
823         })
824
825
826         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
827                 "BACKEND_TYPE": provision_backend.type,
828                 "SERVER_ROLE": serverrole
829                 })
830
831         logger.info("Setting up sam.ldb rootDSE")
832         setup_samdb_rootdse(samdb, names)
833     except Exception:
834         samdb.transaction_cancel()
835         raise
836     else:
837         samdb.transaction_commit()
838
839
840 def secretsdb_self_join(secretsdb, domain,
841                         netbiosname, machinepass, domainsid=None,
842                         realm=None, dnsdomain=None,
843                         keytab_path=None,
844                         key_version_number=1,
845                         secure_channel_type=SEC_CHAN_WKSTA):
846     """Add domain join-specific bits to a secrets database.
847
848     :param secretsdb: Ldb Handle to the secrets database
849     :param machinepass: Machine password
850     """
851     attrs = ["whenChanged",
852            "secret",
853            "priorSecret",
854            "priorChanged",
855            "krb5Keytab",
856            "privateKeytab"]
857
858     if realm is not None:
859         if dnsdomain is None:
860             dnsdomain = realm.lower()
861         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
862     else:
863         dnsname = None
864     shortname = netbiosname.lower()
865
866     # We don't need to set msg["flatname"] here, because rdn_name will handle
867     # it, and it causes problems for modifies anyway
868     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
869     msg["secureChannelType"] = [str(secure_channel_type)]
870     msg["objectClass"] = ["top", "primaryDomain"]
871     if dnsname is not None:
872         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
873         msg["realm"] = [realm]
874         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
875         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
876         msg["privateKeytab"] = ["secrets.keytab"]
877
878     msg["secret"] = [machinepass]
879     msg["samAccountName"] = ["%s$" % netbiosname]
880     msg["secureChannelType"] = [str(secure_channel_type)]
881     if domainsid is not None:
882         msg["objectSid"] = [ndr_pack(domainsid)]
883
884     # This complex expression tries to ensure that we don't have more
885     # than one record for this SID, realm or netbios domain at a time,
886     # but we don't delete the old record that we are about to modify,
887     # because that would delete the keytab and previous password.
888     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
889         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
890         scope=ldb.SCOPE_ONELEVEL)
891
892     for del_msg in res:
893         secretsdb.delete(del_msg.dn)
894
895     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
896
897     if len(res) == 1:
898         msg["priorSecret"] = [res[0]["secret"][0]]
899         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
900
901         try:
902             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
903         except KeyError:
904             pass
905
906         try:
907             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
908         except KeyError:
909             pass
910
911         for el in msg:
912             if el != 'dn':
913                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
914         secretsdb.modify(msg)
915         secretsdb.rename(res[0].dn, msg.dn)
916     else:
917         spn = [ 'HOST/%s' % shortname ]
918         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
919             # we are a domain controller then we add servicePrincipalName
920             # entries for the keytab code to update.
921             spn.extend([ 'HOST/%s' % dnsname ])
922         msg["servicePrincipalName"] = spn
923
924         secretsdb.add(msg)
925
926
927 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
928                         dnsdomain, dns_keytab_path, dnspass):
929     """Add DNS specific bits to a secrets database.
930
931     :param secretsdb: Ldb Handle to the secrets database
932     :param machinepass: Machine password
933     """
934     try:
935         os.unlink(os.path.join(private_dir, dns_keytab_path))
936     except OSError:
937         pass
938
939     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
940             "REALM": realm,
941             "DNSDOMAIN": dnsdomain,
942             "DNS_KEYTAB": dns_keytab_path,
943             "DNSPASS_B64": b64encode(dnspass),
944             "HOSTNAME": names.hostname,
945             "DNSNAME" : '%s.%s' % (
946                 names.netbiosname.lower(), names.dnsdomain.lower())
947             })
948
949
950 def setup_secretsdb(paths, session_info, backend_credentials, lp):
951     """Setup the secrets database.
952
953    :note: This function does not handle exceptions and transaction on purpose,
954        it's up to the caller to do this job.
955
956     :param path: Path to the secrets database.
957     :param session_info: Session info.
958     :param credentials: Credentials
959     :param lp: Loadparm context
960     :return: LDB handle for the created secrets database
961     """
962     if os.path.exists(paths.secrets):
963         os.unlink(paths.secrets)
964
965     keytab_path = os.path.join(paths.private_dir, paths.keytab)
966     if os.path.exists(keytab_path):
967         os.unlink(keytab_path)
968
969     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
970     if os.path.exists(dns_keytab_path):
971         os.unlink(dns_keytab_path)
972
973     path = paths.secrets
974
975     secrets_ldb = Ldb(path, session_info=session_info,
976                       lp=lp)
977     secrets_ldb.erase()
978     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
979     secrets_ldb = Ldb(path, session_info=session_info,
980                       lp=lp)
981     secrets_ldb.transaction_start()
982     try:
983         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
984
985         if (backend_credentials is not None and
986             backend_credentials.authentication_requested()):
987             if backend_credentials.get_bind_dn() is not None:
988                 setup_add_ldif(secrets_ldb,
989                     setup_path("secrets_simple_ldap.ldif"), {
990                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
991                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
992                         })
993             else:
994                 setup_add_ldif(secrets_ldb,
995                     setup_path("secrets_sasl_ldap.ldif"), {
996                         "LDAPADMINUSER": backend_credentials.get_username(),
997                         "LDAPADMINREALM": backend_credentials.get_realm(),
998                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
999                         })
1000
1001         return secrets_ldb
1002     except Exception:
1003         secrets_ldb.transaction_cancel()
1004         raise
1005
1006
1007 def setup_privileges(path, session_info, lp):
1008     """Setup the privileges database.
1009
1010     :param path: Path to the privileges database.
1011     :param session_info: Session info.
1012     :param credentials: Credentials
1013     :param lp: Loadparm context
1014     :return: LDB handle for the created secrets database
1015     """
1016     if os.path.exists(path):
1017         os.unlink(path)
1018     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1019     privilege_ldb.erase()
1020     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1021
1022
1023 def setup_registry(path, session_info, lp):
1024     """Setup the registry.
1025
1026     :param path: Path to the registry database
1027     :param session_info: Session information
1028     :param credentials: Credentials
1029     :param lp: Loadparm context
1030     """
1031     reg = samba.registry.Registry()
1032     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1033     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1034     provision_reg = setup_path("provision.reg")
1035     assert os.path.exists(provision_reg)
1036     reg.diff_apply(provision_reg)
1037
1038
1039 def setup_idmapdb(path, session_info, lp):
1040     """Setup the idmap database.
1041
1042     :param path: path to the idmap database
1043     :param session_info: Session information
1044     :param credentials: Credentials
1045     :param lp: Loadparm context
1046     """
1047     if os.path.exists(path):
1048         os.unlink(path)
1049
1050     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1051     idmap_ldb.erase()
1052     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1053     return idmap_ldb
1054
1055
1056 def setup_samdb_rootdse(samdb, names):
1057     """Setup the SamDB rootdse.
1058
1059     :param samdb: Sam Database handle
1060     """
1061     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1062         "SCHEMADN": names.schemadn,
1063         "DOMAINDN": names.domaindn,
1064         "ROOTDN": names.rootdn,
1065         "CONFIGDN": names.configdn,
1066         "SERVERDN": names.serverdn,
1067         })
1068
1069
1070 def setup_self_join(samdb, names, machinepass, dnspass,
1071                     domainsid, next_rid, invocationid,
1072                     policyguid, policyguid_dc, domainControllerFunctionality,
1073                     ntdsguid, dc_rid=None):
1074     """Join a host to its own domain."""
1075     assert isinstance(invocationid, str)
1076     if ntdsguid is not None:
1077         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1078     else:
1079         ntdsguid_line = ""
1080
1081     if dc_rid is None:
1082         dc_rid = next_rid
1083
1084     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1085               "CONFIGDN": names.configdn,
1086               "SCHEMADN": names.schemadn,
1087               "DOMAINDN": names.domaindn,
1088               "SERVERDN": names.serverdn,
1089               "INVOCATIONID": invocationid,
1090               "NETBIOSNAME": names.netbiosname,
1091               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1092               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1093               "DOMAINSID": str(domainsid),
1094               "DCRID": str(dc_rid),
1095               "SAMBA_VERSION_STRING": version,
1096               "NTDSGUID": ntdsguid_line,
1097               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1098                   domainControllerFunctionality)})
1099
1100     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1101               "POLICYGUID": policyguid,
1102               "POLICYGUID_DC": policyguid_dc,
1103               "DNSDOMAIN": names.dnsdomain,
1104               "DOMAINDN": names.domaindn})
1105
1106     # add the NTDSGUID based SPNs
1107     ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1108     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1109                                      expression="", scope=ldb.SCOPE_BASE)
1110     assert isinstance(names.ntdsguid, str)
1111
1112     # Setup fSMORoleOwner entries to point at the newly created DC entry
1113     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1114               "DOMAINDN": names.domaindn,
1115               "CONFIGDN": names.configdn,
1116               "SCHEMADN": names.schemadn,
1117               "DEFAULTSITE": names.sitename,
1118               "SERVERDN": names.serverdn,
1119               "NETBIOSNAME": names.netbiosname,
1120               "RIDALLOCATIONSTART": str(next_rid + 100),
1121               "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1122               })
1123
1124     # This is Samba4 specific and should be replaced by the correct
1125     # DNS AD-style setup
1126     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1127               "DNSDOMAIN": names.dnsdomain,
1128               "DOMAINDN": names.domaindn,
1129               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1130               "HOSTNAME" : names.hostname,
1131               "DNSNAME" : '%s.%s' % (
1132                   names.netbiosname.lower(), names.dnsdomain.lower())
1133               })
1134
1135
1136 def getpolicypath(sysvolpath, dnsdomain, guid):
1137     """Return the physical path of policy given its guid.
1138
1139     :param sysvolpath: Path to the sysvol folder
1140     :param dnsdomain: DNS name of the AD domain
1141     :param guid: The GUID of the policy
1142     :return: A string with the complete path to the policy folder
1143     """
1144
1145     if guid[0] != "{":
1146         guid = "{%s}" % guid
1147     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1148     return policy_path
1149
1150
1151 def create_gpo_struct(policy_path):
1152     if not os.path.exists(policy_path):
1153         os.makedirs(policy_path, 0775)
1154     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1155                       "[General]\r\nVersion=0")
1156     p = os.path.join(policy_path, "MACHINE")
1157     if not os.path.exists(p):
1158         os.makedirs(p, 0775)
1159     p = os.path.join(policy_path, "USER")
1160     if not os.path.exists(p):
1161         os.makedirs(p, 0775)
1162
1163
1164 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1165     """Create the default GPO for a domain
1166
1167     :param sysvolpath: Physical path for the sysvol folder
1168     :param dnsdomain: DNS domain name of the AD domain
1169     :param policyguid: GUID of the default domain policy
1170     :param policyguid_dc: GUID of the default domain controler policy
1171     """
1172     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1173     create_gpo_struct(policy_path)
1174
1175     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1176     create_gpo_struct(policy_path)
1177
1178
1179 def setup_samdb(path, session_info, provision_backend, lp, names,
1180         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1181         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1182         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1183         next_rid=None, dc_rid=None):
1184     """Setup a complete SAM Database.
1185
1186     :note: This will wipe the main SAM database file!
1187     """
1188
1189     if next_rid is None:
1190         next_rid = 1000
1191
1192     # Provision does not make much sense values larger than 1000000000
1193     # as the upper range of the rIDAvailablePool is 1073741823 and
1194     # we don't want to create a domain that cannot allocate rids.
1195     if next_rid < 1000 or next_rid > 1000000000:
1196         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1197         error += "the valid range is %u-%u. The default is %u." % (
1198             1000, 1000000000, 1000)
1199         raise ProvisioningError(error)
1200
1201     # ATTENTION: Do NOT change these default values without discussion with the
1202     # team and/or release manager. They have a big impact on the whole program!
1203     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1204
1205     if dom_for_fun_level is None:
1206         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1207
1208     if dom_for_fun_level > domainControllerFunctionality:
1209         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1210
1211     domainFunctionality = dom_for_fun_level
1212     forestFunctionality = dom_for_fun_level
1213
1214     # Also wipes the database
1215     setup_samdb_partitions(path, logger=logger, lp=lp,
1216         provision_backend=provision_backend, session_info=session_info,
1217         names=names, serverrole=serverrole, schema=schema)
1218
1219     if schema is None:
1220         schema = Schema(domainsid, schemadn=names.schemadn)
1221
1222     # Load the database, but don's load the global schema and don't connect
1223     # quite yet
1224     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1225                   credentials=provision_backend.credentials, lp=lp,
1226                   global_schema=False, am_rodc=am_rodc)
1227
1228     logger.info("Pre-loading the Samba 4 and AD schema")
1229
1230     # Load the schema from the one we computed earlier
1231     samdb.set_schema(schema)
1232
1233     # Set the NTDS settings DN manually - in order to have it already around
1234     # before the provisioned tree exists and we connect
1235     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1236
1237     # And now we can connect to the DB - the schema won't be loaded from the
1238     # DB
1239     samdb.connect(path)
1240
1241     if fill == FILL_DRS:
1242         return samdb
1243
1244     samdb.transaction_start()
1245     try:
1246         # Set the domain functionality levels onto the database.
1247         # Various module (the password_hash module in particular) need
1248         # to know what level of AD we are emulating.
1249
1250         # These will be fixed into the database via the database
1251         # modifictions below, but we need them set from the start.
1252         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1253         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1254         samdb.set_opaque_integer("domainControllerFunctionality",
1255             domainControllerFunctionality)
1256
1257         samdb.set_domain_sid(str(domainsid))
1258         samdb.set_invocation_id(invocationid)
1259
1260         logger.info("Adding DomainDN: %s" % names.domaindn)
1261
1262         # impersonate domain admin
1263         admin_session_info = admin_session(lp, str(domainsid))
1264         samdb.set_session_info(admin_session_info)
1265         if domainguid is not None:
1266             domainguid_line = "objectGUID: %s\n-" % domainguid
1267         else:
1268             domainguid_line = ""
1269
1270         descr = b64encode(get_domain_descriptor(domainsid))
1271         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1272                 "DOMAINDN": names.domaindn,
1273                 "DOMAINSID": str(domainsid),
1274                 "DESCRIPTOR": descr,
1275                 "DOMAINGUID": domainguid_line
1276                 })
1277
1278         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1279             "DOMAINDN": names.domaindn,
1280             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1281             "NEXTRID": str(next_rid),
1282             "DEFAULTSITE": names.sitename,
1283             "CONFIGDN": names.configdn,
1284             "POLICYGUID": policyguid,
1285             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1286             "SAMBA_VERSION_STRING": version
1287             })
1288
1289         logger.info("Adding configuration container")
1290         descr = b64encode(get_config_descriptor(domainsid))
1291         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1292             "CONFIGDN": names.configdn,
1293             "DESCRIPTOR": descr,
1294             })
1295
1296         # Now register this container in the root of the forest
1297         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1298         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1299                     "subRefs")
1300
1301         # The LDIF here was created when the Schema object was constructed
1302         logger.info("Setting up sam.ldb schema")
1303         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1304         samdb.modify_ldif(schema.schema_dn_modify)
1305         samdb.write_prefixes_from_schema()
1306         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1307         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1308                        {"SCHEMADN": names.schemadn})
1309
1310         logger.info("Reopening sam.ldb with new schema")
1311     except Exception:
1312         samdb.transaction_cancel()
1313         raise
1314     else:
1315         samdb.transaction_commit()
1316
1317     samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1318                 credentials=provision_backend.credentials, lp=lp,
1319                 global_schema=False, am_rodc=am_rodc)
1320
1321     # Set the NTDS settings DN manually - in order to have it already around
1322     # before the provisioned tree exists and we connect
1323     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1324     samdb.connect(path)
1325
1326     samdb.transaction_start()
1327     try:
1328         samdb.invocation_id = invocationid
1329
1330         logger.info("Setting up sam.ldb configuration data")
1331         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1332             "CONFIGDN": names.configdn,
1333             "NETBIOSNAME": names.netbiosname,
1334             "DEFAULTSITE": names.sitename,
1335             "DNSDOMAIN": names.dnsdomain,
1336             "DOMAIN": names.domain,
1337             "SCHEMADN": names.schemadn,
1338             "DOMAINDN": names.domaindn,
1339             "SERVERDN": names.serverdn,
1340             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1341             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1342             })
1343
1344         logger.info("Setting up display specifiers")
1345         display_specifiers_ldif = read_ms_ldif(
1346             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1347         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1348             {"CONFIGDN": names.configdn})
1349         check_all_substituted(display_specifiers_ldif)
1350         samdb.add_ldif(display_specifiers_ldif)
1351
1352         logger.info("Adding users container")
1353         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1354                 "DOMAINDN": names.domaindn})
1355         logger.info("Modifying users container")
1356         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1357                 "DOMAINDN": names.domaindn})
1358         logger.info("Adding computers container")
1359         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1360                 "DOMAINDN": names.domaindn})
1361         logger.info("Modifying computers container")
1362         setup_modify_ldif(samdb,
1363             setup_path("provision_computers_modify.ldif"), {
1364                 "DOMAINDN": names.domaindn})
1365         logger.info("Setting up sam.ldb data")
1366         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1367             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1368             "DOMAINDN": names.domaindn,
1369             "NETBIOSNAME": names.netbiosname,
1370             "DEFAULTSITE": names.sitename,
1371             "CONFIGDN": names.configdn,
1372             "SERVERDN": names.serverdn,
1373             "RIDAVAILABLESTART": str(next_rid + 600),
1374             "POLICYGUID_DC": policyguid_dc
1375             })
1376
1377         setup_modify_ldif(samdb,
1378             setup_path("provision_basedn_references.ldif"), {
1379                 "DOMAINDN": names.domaindn})
1380
1381         setup_modify_ldif(samdb,
1382             setup_path("provision_configuration_references.ldif"), {
1383                 "CONFIGDN": names.configdn,
1384                 "SCHEMADN": names.schemadn})
1385         if fill == FILL_FULL:
1386             logger.info("Setting up sam.ldb users and groups")
1387             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1388                 "DOMAINDN": names.domaindn,
1389                 "DOMAINSID": str(domainsid),
1390                 "CONFIGDN": names.configdn,
1391                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1392                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1393                 })
1394
1395             logger.info("Setting up self join")
1396             setup_self_join(samdb, names=names, invocationid=invocationid,
1397                             dnspass=dnspass,
1398                             machinepass=machinepass,
1399                             domainsid=domainsid,
1400                             next_rid=next_rid,
1401                             dc_rid=dc_rid,
1402                             policyguid=policyguid,
1403                             policyguid_dc=policyguid_dc,
1404                             domainControllerFunctionality=domainControllerFunctionality,
1405                             ntdsguid=ntdsguid)
1406
1407             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1408             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1409                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1410             assert isinstance(names.ntdsguid, str)
1411     except Exception:
1412         samdb.transaction_cancel()
1413         raise
1414     else:
1415         samdb.transaction_commit()
1416         return samdb
1417
1418
1419 FILL_FULL = "FULL"
1420 FILL_NT4SYNC = "NT4SYNC"
1421 FILL_DRS = "DRS"
1422 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1423 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1424
1425
1426 def set_dir_acl(path, acl, lp, domsid):
1427     setntacl(lp, path, acl, domsid)
1428     for root, dirs, files in os.walk(path, topdown=False):
1429         for name in files:
1430             setntacl(lp, os.path.join(root, name), acl, domsid)
1431         for name in dirs:
1432             setntacl(lp, os.path.join(root, name), acl, domsid)
1433
1434
1435 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1436     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1437     folders beneath.
1438
1439     :param sysvol: Physical path for the sysvol folder
1440     :param dnsdomain: The DNS name of the domain
1441     :param domainsid: The SID of the domain
1442     :param domaindn: The DN of the domain (ie. DC=...)
1443     :param samdb: An LDB object on the SAM db
1444     :param lp: an LP object
1445     """
1446
1447     # Set ACL for GPO root folder
1448     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1449     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1450
1451     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1452                         attrs=["cn", "nTSecurityDescriptor"],
1453                         expression="", scope=ldb.SCOPE_ONELEVEL)
1454
1455     for policy in res:
1456         acl = ndr_unpack(security.descriptor,
1457                          str(policy["nTSecurityDescriptor"])).as_sddl()
1458         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1459         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1460                     str(domainsid))
1461
1462
1463 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1464     lp):
1465     """Set the ACL for the sysvol share and the subfolders
1466
1467     :param samdb: An LDB object on the SAM db
1468     :param netlogon: Physical path for the netlogon folder
1469     :param sysvol: Physical path for the sysvol folder
1470     :param gid: The GID of the "Domain adminstrators" group
1471     :param domainsid: The SID of the domain
1472     :param dnsdomain: The DNS name of the domain
1473     :param domaindn: The DN of the domain (ie. DC=...)
1474     """
1475
1476     try:
1477         os.chown(sysvol, -1, gid)
1478     except OSError:
1479         canchown = False
1480     else:
1481         canchown = True
1482
1483     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1484     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1485     for root, dirs, files in os.walk(sysvol, topdown=False):
1486         for name in files:
1487             if canchown:
1488                 os.chown(os.path.join(root, name), -1, gid)
1489             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1490         for name in dirs:
1491             if canchown:
1492                 os.chown(os.path.join(root, name), -1, gid)
1493             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1494
1495     # Set acls on Policy folder and policies folders
1496     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1497
1498
1499 def interface_ips_v4(lp):
1500     '''return only IPv4 IPs'''
1501     ips = samba.interface_ips(lp, False)
1502     ret = []
1503     for i in ips:
1504         if i.find(':') == -1:
1505             ret.append(i)
1506     return ret
1507
1508 def interface_ips_v6(lp, linklocal=False):
1509     '''return only IPv6 IPs'''
1510     ips = samba.interface_ips(lp, False)
1511     ret = []
1512     for i in ips:
1513         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1514             ret.append(i)
1515     return ret
1516
1517
1518 def provision(logger, session_info, credentials, smbconf=None,
1519         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1520         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1521         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1522         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1523         domainguid=None, policyguid=None, policyguid_dc=None,
1524         invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1525         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1526         serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1527         ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1528         ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1529         nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1530         lp=None):
1531     """Provision samba4
1532
1533     :note: caution, this wipes all existing data!
1534     """
1535
1536     if domainsid is None:
1537         domainsid = security.random_sid()
1538     else:
1539         domainsid = security.dom_sid(domainsid)
1540
1541     # create/adapt the group policy GUIDs
1542     # Default GUID for default policy are described at
1543     # "How Core Group Policy Works"
1544     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1545     if policyguid is None:
1546         policyguid = DEFAULT_POLICY_GUID
1547     policyguid = policyguid.upper()
1548     if policyguid_dc is None:
1549         policyguid_dc = DEFAULT_DC_POLICY_GUID
1550     policyguid_dc = policyguid_dc.upper()
1551
1552     if adminpass is None:
1553         adminpass = samba.generate_random_password(12, 32)
1554     if krbtgtpass is None:
1555         krbtgtpass = samba.generate_random_password(128, 255)
1556     if machinepass is None:
1557         machinepass  = samba.generate_random_password(128, 255)
1558     if dnspass is None:
1559         dnspass = samba.generate_random_password(128, 255)
1560     if ldapadminpass is None:
1561         # Make a new, random password between Samba and it's LDAP server
1562         ldapadminpass=samba.generate_random_password(128, 255)
1563
1564     if backend_type is None:
1565         backend_type = "ldb"
1566
1567     sid_generator = "internal"
1568     if backend_type == "fedora-ds":
1569         sid_generator = "backend"
1570
1571     root_uid = findnss_uid([root or "root"])
1572     nobody_uid = findnss_uid([nobody or "nobody"])
1573     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1574     if wheel is None:
1575         wheel_gid = findnss_gid(["wheel", "adm"])
1576     else:
1577         wheel_gid = findnss_gid([wheel])
1578     try:
1579         bind_gid = findnss_gid(["bind", "named"])
1580     except KeyError:
1581         bind_gid = None
1582
1583     if targetdir is not None:
1584         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1585     elif smbconf is None:
1586         smbconf = samba.param.default_path()
1587     if not os.path.exists(os.path.dirname(smbconf)):
1588         os.makedirs(os.path.dirname(smbconf))
1589
1590     # only install a new smb.conf if there isn't one there already
1591     if os.path.exists(smbconf):
1592         # if Samba Team members can't figure out the weird errors
1593         # loading an empty smb.conf gives, then we need to be smarter.
1594         # Pretend it just didn't exist --abartlet
1595         data = open(smbconf, 'r').read()
1596         data = data.lstrip()
1597         if data is None or data == "":
1598             make_smbconf(smbconf, hostname, domain, realm,
1599                          serverrole, targetdir, sid_generator, useeadb,
1600                          lp=lp)
1601     else:
1602         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1603                      targetdir, sid_generator, useeadb, lp=lp)
1604
1605     if lp is None:
1606         lp = samba.param.LoadParm()
1607     lp.load(smbconf)
1608     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1609         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1610         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1611         sitename=sitename)
1612     paths = provision_paths_from_lp(lp, names.dnsdomain)
1613
1614     paths.bind_gid = bind_gid
1615
1616     if hostip is None:
1617         logger.info("Looking up IPv4 addresses")
1618         hostips = interface_ips_v4(lp)
1619         if len(hostips) > 0:
1620             hostip = hostips[0]
1621             if len(hostips) > 1:
1622                 logger.warning("More than one IPv4 address found. Using %s",
1623                     hostip)
1624     if hostip == "127.0.0.1":
1625         hostip = None
1626     if hostip is None:
1627         logger.warning("No IPv4 address will be assigned")
1628
1629     if hostip6 is None:
1630         logger.info("Looking up IPv6 addresses")
1631         hostips = interface_ips_v6(lp, linklocal=False)
1632         if hostips:
1633             hostip6 = hostips[0]
1634         if len(hostips) > 1:
1635             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1636     if hostip6 is None:
1637         logger.warning("No IPv6 address will be assigned")
1638
1639     if serverrole is None:
1640         serverrole = lp.get("server role")
1641
1642     assert serverrole in ("domain controller", "member server", "standalone")
1643     if invocationid is None:
1644         invocationid = str(uuid.uuid4())
1645
1646     if not os.path.exists(paths.private_dir):
1647         os.mkdir(paths.private_dir)
1648     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1649         os.mkdir(os.path.join(paths.private_dir, "tls"))
1650
1651     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1652
1653     schema = Schema(domainsid, invocationid=invocationid,
1654         schemadn=names.schemadn)
1655
1656     if backend_type == "ldb":
1657         provision_backend = LDBBackend(backend_type, paths=paths,
1658             lp=lp, credentials=credentials,
1659             names=names, logger=logger)
1660     elif backend_type == "existing":
1661         provision_backend = ExistingBackend(backend_type, paths=paths,
1662             lp=lp, credentials=credentials,
1663             names=names, logger=logger,
1664             ldap_backend_forced_uri=ldap_backend_forced_uri)
1665     elif backend_type == "fedora-ds":
1666         provision_backend = FDSBackend(backend_type, paths=paths,
1667             lp=lp, credentials=credentials,
1668             names=names, logger=logger, domainsid=domainsid,
1669             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1670             slapd_path=slapd_path,
1671             ldap_backend_extra_port=ldap_backend_extra_port,
1672             ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1673             setup_ds_path=setup_ds_path,
1674             ldap_backend_forced_uri=ldap_backend_forced_uri)
1675     elif backend_type == "openldap":
1676         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1677             lp=lp, credentials=credentials,
1678             names=names, logger=logger, domainsid=domainsid,
1679             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1680             slapd_path=slapd_path,
1681             ldap_backend_extra_port=ldap_backend_extra_port,
1682             ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1683             nosync=nosync,
1684             ldap_backend_forced_uri=ldap_backend_forced_uri)
1685     else:
1686         raise ValueError("Unknown LDAP backend type selected")
1687
1688     provision_backend.init()
1689     provision_backend.start()
1690
1691     # only install a new shares config db if there is none
1692     if not os.path.exists(paths.shareconf):
1693         logger.info("Setting up share.ldb")
1694         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1695                         lp=lp)
1696         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1697
1698     logger.info("Setting up secrets.ldb")
1699     secrets_ldb = setup_secretsdb(paths,
1700         session_info=session_info,
1701         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1702
1703     try:
1704         logger.info("Setting up the registry")
1705         setup_registry(paths.hklm, session_info,
1706                        lp=lp)
1707
1708         logger.info("Setting up the privileges database")
1709         setup_privileges(paths.privilege, session_info, lp=lp)
1710
1711         logger.info("Setting up idmap db")
1712         idmap = setup_idmapdb(paths.idmapdb,
1713             session_info=session_info, lp=lp)
1714
1715         logger.info("Setting up SAM db")
1716         samdb = setup_samdb(paths.samdb, session_info,
1717             provision_backend, lp, names, logger=logger,
1718             domainsid=domainsid, schema=schema, domainguid=domainguid,
1719             policyguid=policyguid, policyguid_dc=policyguid_dc,
1720             fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1721             invocationid=invocationid, machinepass=machinepass,
1722             dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1723             dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1724             next_rid=next_rid, dc_rid=dc_rid)
1725
1726         if serverrole == "domain controller":
1727             if paths.netlogon is None:
1728                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1729                 logger.info("Please either remove %s or see the template at %s" %
1730                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1731                 assert paths.netlogon is not None
1732
1733             if paths.sysvol is None:
1734                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1735                         " are configuring a DC.")
1736                 logger.info("Please either remove %s or see the template at %s" %
1737                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1738                 assert paths.sysvol is not None
1739
1740             if not os.path.isdir(paths.netlogon):
1741                 os.makedirs(paths.netlogon, 0755)
1742
1743         if samdb_fill == FILL_FULL:
1744             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1745                                 root_uid=root_uid, nobody_uid=nobody_uid,
1746                                 users_gid=users_gid, wheel_gid=wheel_gid)
1747
1748             if serverrole == "domain controller":
1749                 # Set up group policies (domain policy and domain controller
1750                 # policy)
1751                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1752                     policyguid_dc)
1753                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1754                     domainsid, names.dnsdomain, names.domaindn, lp)
1755
1756             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1757             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1758                               { 'NTDSGUID' : names.ntdsguid })
1759
1760             secretsdb_self_join(secrets_ldb, domain=names.domain,
1761                 realm=names.realm, dnsdomain=names.dnsdomain,
1762                 netbiosname=names.netbiosname, domainsid=domainsid,
1763                 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1764
1765             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1766             # In future, this might be determined from some configuration
1767             kerberos_enctypes = str(ENC_ALL_TYPES)
1768
1769             try:
1770                 msg = ldb.Message(ldb.Dn(samdb,
1771                     samdb.searchone("distinguishedName",
1772                         expression="samAccountName=%s$" % names.netbiosname,
1773                         scope=ldb.SCOPE_SUBTREE)))
1774                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1775                     elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1776                     name="msDS-SupportedEncryptionTypes")
1777                 samdb.modify(msg)
1778             except ldb.LdbError, (enum, estr):
1779                 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1780                     # It might be that this attribute does not exist in this schema
1781                     raise
1782
1783             if serverrole == "domain controller":
1784                 secretsdb_setup_dns(secrets_ldb, names,
1785                     paths.private_dir, realm=names.realm,
1786                     dnsdomain=names.dnsdomain,
1787                     dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1788
1789                 setup_ad_dns(samdb, names=names, hostip=hostip, hostip6=hostip6)
1790
1791                 domainguid = samdb.searchone(basedn=domaindn,
1792                     attribute="objectGUID")
1793                 assert isinstance(domainguid, str)
1794
1795                 # Only make a zone file on the first DC, it should be
1796                 # replicated with DNS replication
1797                 create_zone_file(lp, logger, paths, targetdir,
1798                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1799                     hostname=names.hostname, realm=names.realm,
1800                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1801
1802                 create_named_conf(paths, realm=names.realm,
1803                     dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1804
1805                 create_named_txt(paths.namedtxt,
1806                     realm=names.realm, dnsdomain=names.dnsdomain,
1807                     dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1808                     private_dir=paths.private_dir,
1809                     keytab_name=paths.dns_keytab)
1810                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1811                 logger.info("and %s for further documentation required for secure DNS "
1812                         "updates", paths.namedtxt)
1813
1814             lastProvisionUSNs = get_last_provision_usn(samdb)
1815             maxUSN = get_max_usn(samdb, str(names.rootdn))
1816             if lastProvisionUSNs is not None:
1817                 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1818             else:
1819                 set_provision_usn(samdb, 0, maxUSN, invocationid)
1820
1821         create_krb5_conf(paths.krb5conf,
1822                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1823                          realm=names.realm)
1824         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1825                     "generated at %s", paths.krb5conf)
1826
1827         if serverrole == "domain controller":
1828             create_dns_update_list(lp, logger, paths)
1829
1830         provision_backend.post_setup()
1831         provision_backend.shutdown()
1832
1833         create_phpldapadmin_config(paths.phpldapadminconfig,
1834                                    ldapi_url)
1835     except Exception:
1836         secrets_ldb.transaction_cancel()
1837         raise
1838
1839     # Now commit the secrets.ldb to disk
1840     secrets_ldb.transaction_commit()
1841
1842     # the commit creates the dns.keytab, now chown it
1843     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1844     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1845         try:
1846             os.chmod(dns_keytab_path, 0640)
1847             os.chown(dns_keytab_path, -1, paths.bind_gid)
1848         except OSError:
1849             if not os.environ.has_key('SAMBA_SELFTEST'):
1850                 logger.info("Failed to chown %s to bind gid %u",
1851                             dns_keytab_path, paths.bind_gid)
1852
1853     if samdb_fill != FILL_DRS:
1854         # fix any dangling GUIDs from the provision
1855         logger.info("Fixing provision GUIDs")
1856         chk = dbcheck(samdb, samdb_schema=samdb,  verbose=False, fix=True, yes=True, quiet=True)
1857         samdb.transaction_start()
1858         # a small number of GUIDs are missing because of ordering issues in the
1859         # provision code
1860         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1861             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1862                                scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1863         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1864                            scope=ldb.SCOPE_ONELEVEL,
1865                            attrs=['ipsecOwnersReference',
1866                                   'ipsecFilterReference',
1867                                   'ipsecISAKMPReference',
1868                                   'ipsecNegotiationPolicyReference',
1869                                   'ipsecNFAReference'])
1870         samdb.transaction_commit()
1871
1872
1873     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1874             paths.phpldapadminconfig)
1875
1876     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1877     logger.info("Server Role:           %s" % serverrole)
1878     logger.info("Hostname:              %s" % names.hostname)
1879     logger.info("NetBIOS Domain:        %s" % names.domain)
1880     logger.info("DNS Domain:            %s" % names.dnsdomain)
1881     logger.info("DOMAIN SID:            %s" % str(domainsid))
1882     if samdb_fill == FILL_FULL:
1883         logger.info("Admin password:        %s" % adminpass)
1884     if provision_backend.type is not "ldb":
1885         if provision_backend.credentials.get_bind_dn() is not None:
1886             logger.info("LDAP Backend Admin DN: %s" %
1887                 provision_backend.credentials.get_bind_dn())
1888         else:
1889             logger.info("LDAP Admin User:       %s" %
1890                 provision_backend.credentials.get_username())
1891
1892         logger.info("LDAP Admin Password:   %s" %
1893             provision_backend.credentials.get_password())
1894
1895         if provision_backend.slapd_command_escaped is not None:
1896             # now display slapd_command_file.txt to show how slapd must be
1897             # started next time
1898             logger.info("Use later the following commandline to start slapd, then Samba:")
1899             logger.info(provision_backend.slapd_command_escaped)
1900             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1901                     provision_backend.ldapdir)
1902
1903     result = ProvisionResult()
1904     result.domaindn = domaindn
1905     result.paths = paths
1906     result.lp = lp
1907     result.samdb = samdb
1908     result.idmap = idmap
1909     return result
1910
1911
1912 def provision_become_dc(smbconf=None, targetdir=None,
1913         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1914         serverdn=None, domain=None, hostname=None, domainsid=None,
1915         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1916         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1917         root=None, nobody=None, users=None, wheel=None, backup=None,
1918         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1919         sitename=None, debuglevel=1):
1920
1921     logger = logging.getLogger("provision")
1922     samba.set_debug_level(debuglevel)
1923
1924     res = provision(logger, system_session(), None,
1925         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1926         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1927         configdn=configdn, serverdn=serverdn, domain=domain,
1928         hostname=hostname, hostip=None, domainsid=domainsid,
1929         machinepass=machinepass, serverrole="domain controller",
1930         sitename=sitename)
1931     res.lp.set("debuglevel", str(debuglevel))
1932     return res
1933
1934
1935 def create_phpldapadmin_config(path, ldapi_uri):
1936     """Create a PHP LDAP admin configuration file.
1937
1938     :param path: Path to write the configuration to.
1939     """
1940     setup_file(setup_path("phpldapadmin-config.php"), path,
1941             {"S4_LDAPI_URI": ldapi_uri})
1942
1943
1944 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1945                      hostip, hostip6, hostname, realm, domainguid,
1946                      ntdsguid):
1947     """Write out a DNS zone file, from the info in the current database.
1948
1949     :param paths: paths object
1950     :param dnsdomain: DNS Domain name
1951     :param domaindn: DN of the Domain
1952     :param hostip: Local IPv4 IP
1953     :param hostip6: Local IPv6 IP
1954     :param hostname: Local hostname
1955     :param realm: Realm name
1956     :param domainguid: GUID of the domain.
1957     :param ntdsguid: GUID of the hosts nTDSDSA record.
1958     """
1959     assert isinstance(domainguid, str)
1960
1961     if hostip6 is not None:
1962         hostip6_base_line = "            IN AAAA    " + hostip6
1963         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1964         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1965     else:
1966         hostip6_base_line = ""
1967         hostip6_host_line = ""
1968         gc_msdcs_ip6_line = ""
1969
1970     if hostip is not None:
1971         hostip_base_line = "            IN A    " + hostip
1972         hostip_host_line = hostname + "        IN A    " + hostip
1973         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1974     else:
1975         hostip_base_line = ""
1976         hostip_host_line = ""
1977         gc_msdcs_ip_line = ""
1978
1979     dns_dir = os.path.dirname(paths.dns)
1980
1981     try:
1982         shutil.rmtree(dns_dir, True)
1983     except OSError:
1984         pass
1985
1986     os.mkdir(dns_dir, 0775)
1987
1988     # we need to freeze the zone while we update the contents
1989     if targetdir is None:
1990         rndc = ' '.join(lp.get("rndc command"))
1991         os.system(rndc + " freeze " + lp.get("realm"))
1992
1993     setup_file(setup_path("provision.zone"), paths.dns, {
1994             "HOSTNAME": hostname,
1995             "DNSDOMAIN": dnsdomain,
1996             "REALM": realm,
1997             "HOSTIP_BASE_LINE": hostip_base_line,
1998             "HOSTIP_HOST_LINE": hostip_host_line,
1999             "DOMAINGUID": domainguid,
2000             "DATESTRING": time.strftime("%Y%m%d%H"),
2001             "DEFAULTSITE": DEFAULTSITE,
2002             "NTDSGUID": ntdsguid,
2003             "HOSTIP6_BASE_LINE": hostip6_base_line,
2004             "HOSTIP6_HOST_LINE": hostip6_host_line,
2005             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2006             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2007         })
2008
2009     # note that we use no variable substitution on this file
2010     # the substitution is done at runtime by samba_dnsupdate
2011     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2012
2013     # and the SPN update list
2014     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2015
2016     if paths.bind_gid is not None:
2017         try:
2018             os.chown(dns_dir, -1, paths.bind_gid)
2019             os.chown(paths.dns, -1, paths.bind_gid)
2020             # chmod needed to cope with umask
2021             os.chmod(dns_dir, 0775)
2022             os.chmod(paths.dns, 0664)
2023         except OSError:
2024             if not os.environ.has_key('SAMBA_SELFTEST'):
2025                 logger.error("Failed to chown %s to bind gid %u" % (
2026                     dns_dir, paths.bind_gid))
2027
2028     if targetdir is None:
2029         os.system(rndc + " unfreeze " + lp.get("realm"))
2030
2031
2032 def create_dns_update_list(lp, logger, paths):
2033     """Write out a dns_update_list file"""
2034     # note that we use no variable substitution on this file
2035     # the substitution is done at runtime by samba_dnsupdate
2036     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2037     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2038
2039
2040 def create_named_conf(paths, realm, dnsdomain,
2041                       private_dir):
2042     """Write out a file containing zone statements suitable for inclusion in a
2043     named.conf file (including GSS-TSIG configuration).
2044
2045     :param paths: all paths
2046     :param realm: Realm name
2047     :param dnsdomain: DNS Domain name
2048     :param private_dir: Path to private directory
2049     :param keytab_name: File name of DNS keytab file
2050     """
2051
2052     setup_file(setup_path("named.conf"), paths.namedconf, {
2053             "DNSDOMAIN": dnsdomain,
2054             "REALM": realm,
2055             "ZONE_FILE": paths.dns,
2056             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2057             "NAMED_CONF": paths.namedconf,
2058             "NAMED_CONF_UPDATE": paths.namedconf_update
2059             })
2060
2061     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2062
2063
2064 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2065     keytab_name):
2066     """Write out a file containing zone statements suitable for inclusion in a
2067     named.conf file (including GSS-TSIG configuration).
2068
2069     :param path: Path of the new named.conf file.
2070     :param realm: Realm name
2071     :param dnsdomain: DNS Domain name
2072     :param private_dir: Path to private directory
2073     :param keytab_name: File name of DNS keytab file
2074     """
2075     setup_file(setup_path("named.txt"), path, {
2076             "DNSDOMAIN": dnsdomain,
2077             "DNSNAME" : dnsname, 
2078             "REALM": realm,
2079             "DNS_KEYTAB": keytab_name,
2080             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2081             "PRIVATE_DIR": private_dir
2082         })
2083
2084
2085 def create_krb5_conf(path, dnsdomain, hostname, realm):
2086     """Write out a file containing zone statements suitable for inclusion in a
2087     named.conf file (including GSS-TSIG configuration).
2088
2089     :param path: Path of the new named.conf file.
2090     :param dnsdomain: DNS Domain name
2091     :param hostname: Local hostname
2092     :param realm: Realm name
2093     """
2094     setup_file(setup_path("krb5.conf"), path, {
2095             "DNSDOMAIN": dnsdomain,
2096             "HOSTNAME": hostname,
2097             "REALM": realm,
2098         })
2099
2100
2101 class ProvisioningError(Exception):
2102     """A generic provision error."""
2103
2104     def __init__(self, value):
2105         self.value = value
2106
2107     def __str__(self):
2108         return "ProvisioningError: " + self.value
2109
2110
2111 class InvalidNetbiosName(Exception):
2112     """A specified name was not a valid NetBIOS name."""
2113     def __init__(self, name):
2114         super(InvalidNetbiosName, self).__init__(
2115             "The name '%r' is not a valid NetBIOS name" % name)