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