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