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