s4/dsdb: Set schemaInfo attribute value during provisioning
[kamenim/samba.git] / source4 / scripting / python / samba / provision.py
1
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #   
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #   
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 from base64 import b64encode
29 import os
30 import 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.namedconf = os.path.join(paths.private_dir, "named.conf")
297     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
298     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
299     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
300     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
301     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
302     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
303                                             "phpldapadmin-config.php")
304     paths.hklm = "hklm.ldb"
305     paths.hkcr = "hkcr.ldb"
306     paths.hkcu = "hkcu.ldb"
307     paths.hku = "hku.ldb"
308     paths.hkpd = "hkpd.ldb"
309     paths.hkpt = "hkpt.ldb"
310     paths.sysvol = lp.get("path", "sysvol")
311     paths.netlogon = lp.get("path", "netlogon")
312     paths.smbconf = lp.configfile
313     return paths
314
315
316 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
317                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
318                 schemadn=None, serverdn=None, sitename=None):
319     """Guess configuration settings to use."""
320
321     if hostname is None:
322         hostname = socket.gethostname().split(".")[0]
323
324     netbiosname = lp.get("netbios name")
325     if netbiosname is None:
326         netbiosname = hostname
327     assert netbiosname is not None
328     netbiosname = netbiosname.upper()
329     if not valid_netbios_name(netbiosname):
330         raise InvalidNetbiosName(netbiosname)
331
332     if dnsdomain is None:
333         dnsdomain = lp.get("realm")
334         if dnsdomain is None or dnsdomain == "":
335             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
336
337     dnsdomain = dnsdomain.lower()
338
339     if serverrole is None:
340         serverrole = lp.get("server role")
341         if serverrole is None:
342             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
343
344     serverrole = serverrole.lower()
345
346     realm = dnsdomain.upper()
347
348     if lp.get("realm") == "":
349         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
350
351     if lp.get("realm").upper() != realm:
352         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))
353
354     if lp.get("server role").lower() != serverrole:
355         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))
356
357     if serverrole == "domain controller":
358         if domain is None:
359             # This will, for better or worse, default to 'WORKGROUP'
360             domain = lp.get("workgroup")
361         domain = domain.upper()
362
363         if lp.get("workgroup").upper() != domain:
364             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))
365
366         if domaindn is None:
367             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
368     else:
369         domain = netbiosname
370         if domaindn is None:
371             domaindn = "DC=" + netbiosname
372         
373     if not valid_netbios_name(domain):
374         raise InvalidNetbiosName(domain)
375         
376     if hostname.upper() == realm:
377         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
378     if netbiosname == realm:
379         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
380     if domain == realm:
381         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
382
383     if rootdn is None:
384        rootdn = domaindn
385        
386     if configdn is None:
387         configdn = "CN=Configuration," + rootdn
388     if schemadn is None:
389         schemadn = "CN=Schema," + configdn
390
391     if sitename is None:
392         sitename=DEFAULTSITE
393
394     names = ProvisionNames()
395     names.rootdn = rootdn
396     names.domaindn = domaindn
397     names.configdn = configdn
398     names.schemadn = schemadn
399     names.ldapmanagerdn = "CN=Manager," + rootdn
400     names.dnsdomain = dnsdomain
401     names.domain = domain
402     names.realm = realm
403     names.netbiosname = netbiosname
404     names.hostname = hostname
405     names.sitename = sitename
406     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
407  
408     return names
409     
410
411 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
412                  targetdir, sid_generator,eadb):
413     """Create a new smb.conf file based on a couple of basic settings.
414     """
415     assert smbconf is not None
416     if hostname is None:
417         hostname = socket.gethostname().split(".")[0]
418     netbiosname = hostname.upper()
419
420     if serverrole is None:
421         serverrole = "standalone"
422
423     assert serverrole in ("domain controller", "member server", "standalone")
424     if serverrole == "domain controller":
425         smbconfsuffix = "dc"
426     elif serverrole == "member server":
427         smbconfsuffix = "member"
428     elif serverrole == "standalone":
429         smbconfsuffix = "standalone"
430
431     if sid_generator is None:
432         sid_generator = "internal"
433
434     assert domain is not None
435     domain = domain.upper()
436
437     assert realm is not None
438     realm = realm.upper()
439
440     default_lp = param.LoadParm()
441     #Load non-existant file
442     if os.path.exists(smbconf):
443         default_lp.load(smbconf)
444     if eadb:
445         if targetdir is not None:
446             privdir = os.path.join(targetdir, "private")
447         else:
448             privdir = default_lp.get("private dir")
449         posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
450     else:
451         posixeadb_line = ""
452
453     if targetdir is not None:
454         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
455         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
456
457         default_lp.set("lock dir", os.path.abspath(targetdir))
458     else:
459         privatedir_line = ""
460         lockdir_line = ""
461
462     if sid_generator == "internal":
463         sid_generator_line = ""
464     else:
465         sid_generator_line = "sid generator = " + sid_generator
466
467     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
468     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
469
470     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
471                smbconf, {
472             "NETBIOS_NAME": netbiosname,
473             "DOMAIN": domain,
474             "REALM": realm,
475             "SERVERROLE": serverrole,
476             "NETLOGONPATH": netlogon,
477             "SYSVOLPATH": sysvol,
478             "SIDGENERATOR_LINE": sid_generator_line,
479             "PRIVATEDIR_LINE": privatedir_line,
480             "LOCKDIR_LINE": lockdir_line,
481             "POSIXEADB_LINE": posixeadb_line
482             })
483
484
485 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
486                         users_gid, wheel_gid):
487     """setup reasonable name mappings for sam names to unix names.
488
489     :param samdb: SamDB object.
490     :param idmap: IDmap db object.
491     :param sid: The domain sid.
492     :param domaindn: The domain DN.
493     :param root_uid: uid of the UNIX root user.
494     :param nobody_uid: uid of the UNIX nobody user.
495     :param users_gid: gid of the UNIX users group.
496     :param wheel_gid: gid of the UNIX wheel group."""
497     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
498     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
499     
500     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
501     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
502
503
504 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
505                            provision_backend, names, schema,
506                            serverrole, 
507                            erase=False):
508     """Setup the partitions for the SAM database. 
509     
510     Alternatively, provision() may call this, and then populate the database.
511     
512     :note: This will wipe the Sam Database!
513     
514     :note: This function always removes the local SAM LDB file. The erase 
515         parameter controls whether to erase the existing data, which 
516         may not be stored locally but in LDAP.
517
518     """
519     assert session_info is not None
520
521     # We use options=["modules:"] to stop the modules loading - we
522     # just want to wipe and re-initialise the database, not start it up
523
524     try:
525         os.unlink(samdb_path)
526     except OSError:
527         pass
528
529     samdb = Ldb(url=samdb_path, session_info=session_info, 
530                 lp=lp, options=["modules:"])
531
532     ldap_backend_line = "# No LDAP backend"
533     if provision_backend.type is not "ldb":
534         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
535
536     samdb.transaction_start()
537     try:
538         message("Setting up sam.ldb partitions and settings")
539         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
540                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
541                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
542                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
543                 "LDAP_BACKEND_LINE": ldap_backend_line,
544         })
545
546         
547         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
548                 "BACKEND_TYPE": provision_backend.type,
549                 "SERVER_ROLE": serverrole
550                 })
551
552         message("Setting up sam.ldb rootDSE")
553         setup_samdb_rootdse(samdb, setup_path, names)
554     except:
555         samdb.transaction_cancel()
556         raise
557     else:
558         samdb.transaction_commit()
559
560         
561 def secretsdb_self_join(secretsdb, domain, 
562                         netbiosname, machinepass, domainsid=None,
563                         realm=None, dnsdomain=None,
564                         keytab_path=None, 
565                         key_version_number=1,
566                         secure_channel_type=SEC_CHAN_WKSTA):
567     """Add domain join-specific bits to a secrets database.
568     
569     :param secretsdb: Ldb Handle to the secrets database
570     :param machinepass: Machine password
571     """
572     attrs=["whenChanged",
573            "secret",
574            "priorSecret",
575            "priorChanged",
576            "krb5Keytab",
577            "privateKeytab"]
578     
579
580     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
581     msg["secureChannelType"] = str(secure_channel_type)
582     msg["flatname"] = [domain]
583     msg["objectClass"] = ["top", "primaryDomain"]
584     if realm is not None:
585       if dnsdomain is None:
586         dnsdomain = realm.lower()
587       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
588       msg["realm"] = realm
589       msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
590       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
591       msg["privateKeytab"] = ["secrets.keytab"]
592
593
594     msg["secret"] = [machinepass]
595     msg["samAccountName"] = ["%s$" % netbiosname]
596     msg["secureChannelType"] = [str(secure_channel_type)]
597     if domainsid is not None:
598         msg["objectSid"] = [ndr_pack(domainsid)]
599     
600     res = secretsdb.search(base="cn=Primary Domains", 
601                            attrs=attrs, 
602                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
603                            scope=ldb.SCOPE_ONELEVEL)
604     
605     for del_msg in res:
606       if del_msg.dn is not msg.dn:
607         secretsdb.delete(del_msg.dn)
608
609     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
610
611     if len(res) == 1:
612       msg["priorSecret"] = res[0]["secret"]
613       msg["priorWhenChanged"] = res[0]["whenChanged"]
614
615       if res["privateKeytab"] is not None:
616         msg["privateKeytab"] = res[0]["privateKeytab"]
617
618       if res["krb5Keytab"] is not None:
619         msg["krb5Keytab"] = res[0]["krb5Keytab"]
620
621       for el in msg:
622         el.set_flags(ldb.FLAG_MOD_REPLACE)
623         secretsdb.modify(msg)
624     else:
625       secretsdb.add(msg)
626
627
628 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
629                         realm, dnsdomain,
630                         dns_keytab_path, dnspass):
631     """Add DNS specific bits to a secrets database.
632     
633     :param secretsdb: Ldb Handle to the secrets database
634     :param setup_path: Setup path function
635     :param machinepass: Machine password
636     """
637     try:
638         os.unlink(os.path.join(private_dir, dns_keytab_path))
639     except OSError:
640         pass
641
642     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
643             "REALM": realm,
644             "DNSDOMAIN": dnsdomain,
645             "DNS_KEYTAB": dns_keytab_path,
646             "DNSPASS_B64": b64encode(dnspass),
647             })
648
649
650 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
651     """Setup the secrets database.
652
653     :param path: Path to the secrets database.
654     :param setup_path: Get the path to a setup file.
655     :param session_info: Session info.
656     :param credentials: Credentials
657     :param lp: Loadparm context
658     :return: LDB handle for the created secrets database
659     """
660     if os.path.exists(path):
661         os.unlink(path)
662     secrets_ldb = Ldb(path, session_info=session_info, 
663                       lp=lp)
664     secrets_ldb.erase()
665     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
666     secrets_ldb = Ldb(path, session_info=session_info, 
667                       lp=lp)
668     secrets_ldb.transaction_start()
669     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
670
671     if backend_credentials is not None and backend_credentials.authentication_requested():
672         if backend_credentials.get_bind_dn() is not None:
673             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
674                     "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
675                     "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
676                     })
677         else:
678             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
679                     "LDAPADMINUSER": backend_credentials.get_username(),
680                     "LDAPADMINREALM": backend_credentials.get_realm(),
681                     "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
682                     })
683
684     return secrets_ldb
685
686 def setup_privileges(path, setup_path, session_info, lp):
687     """Setup the privileges database.
688
689     :param path: Path to the privileges database.
690     :param setup_path: Get the path to a setup file.
691     :param session_info: Session info.
692     :param credentials: Credentials
693     :param lp: Loadparm context
694     :return: LDB handle for the created secrets database
695     """
696     if os.path.exists(path):
697         os.unlink(path)
698     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
699     privilege_ldb.erase()
700     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
701
702
703 def setup_registry(path, setup_path, session_info, lp):
704     """Setup the registry.
705     
706     :param path: Path to the registry database
707     :param setup_path: Function that returns the path to a setup.
708     :param session_info: Session information
709     :param credentials: Credentials
710     :param lp: Loadparm context
711     """
712     reg = registry.Registry()
713     hive = registry.open_ldb(path, session_info=session_info, 
714                          lp_ctx=lp)
715     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
716     provision_reg = setup_path("provision.reg")
717     assert os.path.exists(provision_reg)
718     reg.diff_apply(provision_reg)
719
720
721 def setup_idmapdb(path, setup_path, session_info, lp):
722     """Setup the idmap database.
723
724     :param path: path to the idmap database
725     :param setup_path: Function that returns a path to a setup file
726     :param session_info: Session information
727     :param credentials: Credentials
728     :param lp: Loadparm context
729     """
730     if os.path.exists(path):
731         os.unlink(path)
732
733     idmap_ldb = IDmapDB(path, session_info=session_info,
734                         lp=lp)
735
736     idmap_ldb.erase()
737     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
738     return idmap_ldb
739
740
741 def setup_samdb_rootdse(samdb, setup_path, names):
742     """Setup the SamDB rootdse.
743
744     :param samdb: Sam Database handle
745     :param setup_path: Obtain setup path
746     """
747     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
748         "SCHEMADN": names.schemadn, 
749         "NETBIOSNAME": names.netbiosname,
750         "DNSDOMAIN": names.dnsdomain,
751         "REALM": names.realm,
752         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
753         "DOMAINDN": names.domaindn,
754         "ROOTDN": names.rootdn,
755         "CONFIGDN": names.configdn,
756         "SERVERDN": names.serverdn,
757         })
758         
759
760 def setup_self_join(samdb, names,
761                     machinepass, dnspass, 
762                     domainsid, invocationid, setup_path,
763                     policyguid, policyguid_dc, domainControllerFunctionality,
764                     ntdsguid):
765     """Join a host to its own domain."""
766     assert isinstance(invocationid, str)
767     if ntdsguid is not None:
768         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
769     else:
770         ntdsguid_line = ""
771     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
772               "CONFIGDN": names.configdn, 
773               "SCHEMADN": names.schemadn,
774               "DOMAINDN": names.domaindn,
775               "SERVERDN": names.serverdn,
776               "INVOCATIONID": invocationid,
777               "NETBIOSNAME": names.netbiosname,
778               "DEFAULTSITE": names.sitename,
779               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
780               "MACHINEPASS_B64": b64encode(machinepass),
781               "REALM": names.realm,
782               "DOMAIN": names.domain,
783               "DOMAINSID": str(domainsid),
784               "DNSDOMAIN": names.dnsdomain,
785               "SAMBA_VERSION_STRING": version,
786               "NTDSGUID": ntdsguid_line,
787               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
788
789     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
790               "POLICYGUID": policyguid,
791               "POLICYGUID_DC": policyguid_dc,
792               "DNSDOMAIN": names.dnsdomain,
793               "DOMAINSID": str(domainsid),
794               "DOMAINDN": names.domaindn})
795     
796     # add the NTDSGUID based SPNs
797     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
798     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
799                                      expression="", scope=ldb.SCOPE_BASE)
800     assert isinstance(names.ntdsguid, str)
801
802     # Setup fSMORoleOwner entries to point at the newly created DC entry
803     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
804               "DOMAIN": names.domain,
805               "DNSDOMAIN": names.dnsdomain,
806               "DOMAINDN": names.domaindn,
807               "CONFIGDN": names.configdn,
808               "SCHEMADN": names.schemadn, 
809               "DEFAULTSITE": names.sitename,
810               "SERVERDN": names.serverdn,
811               "NETBIOSNAME": names.netbiosname,
812               "NTDSGUID": names.ntdsguid,
813               "DNSPASS_B64": b64encode(dnspass),
814               })
815
816 def getpolicypath(sysvolpath, dnsdomain, guid):
817     if guid[0] != "{":
818         guid = "{%s}" % guid
819     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
820     return policy_path
821
822 def create_gpo_struct(policy_path):
823     os.makedirs(policy_path, 0755)
824     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
825                       "[General]\r\nVersion=65543")
826     os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
827     os.makedirs(os.path.join(policy_path, "USER"), 0755)
828
829
830 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
831     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
832     create_gpo_struct(policy_path)
833
834     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
835     create_gpo_struct(policy_path)
836
837
838 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
839                 names, message, 
840                 domainsid, domainguid, policyguid, policyguid_dc,
841                 fill, adminpass, krbtgtpass, 
842                 machinepass, invocationid, dnspass, ntdsguid,
843                 serverrole, dom_for_fun_level=None,
844                 schema=None):
845     """Setup a complete SAM Database.
846     
847     :note: This will wipe the main SAM database file!
848     """
849
850     # ATTENTION: Do NOT change these default values without discussion with the
851     # team and/or release manager. They have a big impact on the whole program!
852     domainControllerFunctionality = DS_DC_FUNCTION_2008
853
854     if dom_for_fun_level is None:
855         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
856     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
857         message("You want to run SAMBA 4 on a domain and forest function level"
858                 " lower than Windows 2003 (Native). This is not recommended")
859
860     if dom_for_fun_level > domainControllerFunctionality:
861         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!")
862
863     domainFunctionality = dom_for_fun_level
864     forestFunctionality = dom_for_fun_level
865
866     # Also wipes the database
867     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
868         provision_backend=provision_backend, session_info=session_info,
869         names=names, serverrole=serverrole, schema=schema)
870
871     if schema is None:
872         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
873
874     # Load the database, but importantly, use Ldb not SamDB as we don't want to
875     # load the global schema
876     samdb = Ldb(session_info=session_info, 
877                 credentials=provision_backend.credentials, lp=lp)
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
905         message("Adding DomainDN: %s" % names.domaindn)
906
907 #impersonate domain admin
908         admin_session_info = admin_session(lp, str(domainsid))
909         samdb.set_session_info(admin_session_info)
910         if domainguid is not None:
911             domainguid_line = "objectGUID: %s\n-" % domainguid
912         else:
913             domainguid_line = ""
914
915         descr = b64encode(get_domain_descriptor(domainsid))
916         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
917                 "DOMAINDN": names.domaindn,
918                 "DOMAINGUID": domainguid_line,
919                 "DESCRIPTOR": descr
920                 })
921
922
923         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
924             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
925             "DOMAINSID": str(domainsid),
926             "SCHEMADN": names.schemadn, 
927             "NETBIOSNAME": names.netbiosname,
928             "DEFAULTSITE": names.sitename,
929             "CONFIGDN": names.configdn,
930             "SERVERDN": names.serverdn,
931             "POLICYGUID": policyguid,
932             "DOMAINDN": names.domaindn,
933             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
934             "SAMBA_VERSION_STRING": version
935             })
936
937         message("Adding configuration container")
938         descr = b64encode(get_config_descriptor(domainsid))
939         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
940             "CONFIGDN": names.configdn, 
941             "DESCRIPTOR": descr,
942             })
943
944         # The LDIF here was created when the Schema object was constructed
945         message("Setting up sam.ldb schema")
946         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
947         samdb.modify_ldif(schema.schema_dn_modify)
948         # set schemaInfo to defalt value for a new Forest
949         samdb.set_schema_info()
950         samdb.write_prefixes_from_schema()
951         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
952         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
953                        {"SCHEMADN": names.schemadn})
954
955         message("Reopening sam.ldb with new schema")
956     except:
957         samdb.transaction_cancel()
958         raise
959     else:
960         samdb.transaction_commit()
961
962     samdb = SamDB(session_info=admin_session_info,
963                 credentials=provision_backend.credentials, lp=lp,
964                 global_schema=False)
965     samdb.connect(path)
966     samdb.transaction_start()
967     try:
968         samdb.invocation_id = invocationid
969
970         message("Setting up sam.ldb configuration data")
971         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
972             "CONFIGDN": names.configdn,
973             "NETBIOSNAME": names.netbiosname,
974             "DEFAULTSITE": names.sitename,
975             "DNSDOMAIN": names.dnsdomain,
976             "DOMAIN": names.domain,
977             "SCHEMADN": names.schemadn,
978             "DOMAINDN": names.domaindn,
979             "SERVERDN": names.serverdn,
980             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
981             })
982
983         message("Setting up display specifiers")
984         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
985         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
986         check_all_substituted(display_specifiers_ldif)
987         samdb.add_ldif(display_specifiers_ldif)
988
989         message("Adding users container")
990         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
991                 "DOMAINDN": names.domaindn})
992         message("Modifying users container")
993         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
994                 "DOMAINDN": names.domaindn})
995         message("Adding computers container")
996         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
997                 "DOMAINDN": names.domaindn})
998         message("Modifying computers container")
999         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1000                 "DOMAINDN": names.domaindn})
1001         message("Setting up sam.ldb data")
1002         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1003             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1004             "DOMAINDN": names.domaindn,
1005             "NETBIOSNAME": names.netbiosname,
1006             "DEFAULTSITE": names.sitename,
1007             "CONFIGDN": names.configdn,
1008             "SERVERDN": names.serverdn,
1009             "POLICYGUID_DC": policyguid_dc
1010             })
1011
1012         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1013                 "DOMAINDN": names.domaindn})
1014
1015         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1016                 "CONFIGDN": names.configdn,
1017                 "SCHEMADN": names.schemadn})
1018         if fill == FILL_FULL:
1019             message("Setting up sam.ldb users and groups")
1020             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1021                 "DOMAINDN": names.domaindn,
1022                 "DOMAINSID": str(domainsid),
1023                 "CONFIGDN": names.configdn,
1024                 "ADMINPASS_B64": b64encode(adminpass),
1025                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1026                 })
1027
1028             message("Setting up self join")
1029             setup_self_join(samdb, names=names, invocationid=invocationid,
1030                             dnspass=dnspass,
1031                             machinepass=machinepass,
1032                             domainsid=domainsid, policyguid=policyguid,
1033                             policyguid_dc=policyguid_dc,
1034                             setup_path=setup_path,
1035                             domainControllerFunctionality=domainControllerFunctionality,
1036                             ntdsguid=ntdsguid)
1037
1038             ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1039             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1040                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1041             assert isinstance(names.ntdsguid, str)
1042     except:
1043         samdb.transaction_cancel()
1044         raise
1045     else:
1046         samdb.transaction_commit()
1047         return samdb
1048
1049
1050 FILL_FULL = "FULL"
1051 FILL_NT4SYNC = "NT4SYNC"
1052 FILL_DRS = "DRS"
1053 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1054 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)"
1055
1056 def set_dir_acl(path, acl, lp, domsid):
1057     setntacl(lp, path, acl, domsid)
1058     for root, dirs, files in os.walk(path, topdown=False):
1059         for name in files:
1060             setntacl(lp, os.path.join(root, name), acl, domsid)
1061         for name in dirs:
1062             setntacl(lp, os.path.join(root, name), acl, domsid)
1063
1064
1065 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1066     # Set ACL for GPO
1067     policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1068     set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)), 
1069         lp, str(domainsid))
1070     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1071                         attrs=["cn","nTSecurityDescriptor"],
1072                         expression="", scope=ldb.SCOPE_ONELEVEL)
1073     for policy in res:
1074         acl = ndr_unpack(security.descriptor, 
1075                          str(policy["nTSecurityDescriptor"])).as_sddl()
1076         policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1077         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, 
1078                     str(domainsid))
1079
1080 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1081     lp):
1082     try:
1083         os.chown(sysvol,-1,gid)
1084     except:
1085         canchown = False
1086     else:
1087         canchown = True
1088
1089     setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1090     for root, dirs, files in os.walk(sysvol, topdown=False):
1091         for name in files:
1092             if canchown:
1093                 os.chown(os.path.join(root, name),-1,gid)
1094             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1095         for name in dirs:
1096             if canchown:
1097                 os.chown(os.path.join(root, name),-1,gid)
1098             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1099     set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1100
1101
1102 def provision(setup_dir, message, session_info, 
1103               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1104               realm=None, 
1105               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1106               serverdn=None,
1107               domain=None, hostname=None, hostip=None, hostip6=None, 
1108               domainsid=None, adminpass=None, ldapadminpass=None, 
1109               krbtgtpass=None, domainguid=None, 
1110               policyguid=None, policyguid_dc=None, invocationid=None,
1111               machinepass=None, ntdsguid=None,
1112               dnspass=None, root=None, nobody=None, users=None, 
1113               wheel=None, backup=None, aci=None, serverrole=None,
1114               dom_for_fun_level=None,
1115               ldap_backend_extra_port=None, backend_type=None,
1116               sitename=None,
1117               ol_mmr_urls=None, ol_olc=None, 
1118               setup_ds_path=None, slapd_path=None, nosync=False,
1119               ldap_dryrun_mode=False,useeadb=False):
1120     """Provision samba4
1121     
1122     :note: caution, this wipes all existing data!
1123     """
1124
1125     def setup_path(file):
1126       return os.path.join(setup_dir, file)
1127
1128     if domainsid is None:
1129       domainsid = security.random_sid()
1130     else:
1131       domainsid = security.dom_sid(domainsid)
1132
1133     # create/adapt the group policy GUIDs
1134     if policyguid is None:
1135         policyguid = str(uuid.uuid4())
1136     policyguid = policyguid.upper()
1137     if policyguid_dc is None:
1138         policyguid_dc = str(uuid.uuid4())
1139     policyguid_dc = policyguid_dc.upper()
1140
1141     if adminpass is None:
1142         adminpass = samba.generate_random_password(12, 32)
1143     if krbtgtpass is None:
1144         krbtgtpass = samba.generate_random_password(128, 255)
1145     if machinepass is None:
1146         machinepass  = samba.generate_random_password(128, 255)
1147     if dnspass is None:
1148         dnspass = samba.generate_random_password(128, 255)
1149     if ldapadminpass is None:
1150         #Make a new, random password between Samba and it's LDAP server
1151         ldapadminpass=samba.generate_random_password(128, 255)
1152
1153     if backend_type is None:
1154         backend_type = "ldb"
1155
1156     sid_generator = "internal"
1157     if backend_type == "fedora-ds":
1158         sid_generator = "backend"
1159
1160     root_uid = findnss_uid([root or "root"])
1161     nobody_uid = findnss_uid([nobody or "nobody"])
1162     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1163     if wheel is None:
1164         wheel_gid = findnss_gid(["wheel", "adm"])
1165     else:
1166         wheel_gid = findnss_gid([wheel])
1167     try:
1168         bind_gid = findnss_gid(["bind", "named"])
1169     except KeyError:
1170         bind_gid = None
1171
1172     if targetdir is not None:
1173         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1174             os.makedirs(os.path.join(targetdir, "etc"))
1175         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1176     elif smbconf is None:
1177         smbconf = param.default_path()
1178
1179     # only install a new smb.conf if there isn't one there already
1180     if os.path.exists(smbconf):
1181         # if Samba Team members can't figure out the weird errors
1182         # loading an empty smb.conf gives, then we need to be smarter.
1183         # Pretend it just didn't exist --abartlet
1184         data = open(smbconf, 'r').read()
1185         data = data.lstrip()
1186         if data is None or data == "":
1187             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1188                          serverrole, targetdir, sid_generator, useeadb)
1189     else: 
1190         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1191                      targetdir, sid_generator, useeadb)
1192
1193     lp = param.LoadParm()
1194     lp.load(smbconf)
1195
1196     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1197                         dnsdomain=realm, serverrole=serverrole,
1198                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1199                         serverdn=serverdn, sitename=sitename)
1200
1201     paths = provision_paths_from_lp(lp, names.dnsdomain)
1202
1203     paths.bind_gid = bind_gid
1204
1205     if hostip is None:
1206         hostips = samba.interface_ips(lp, False)
1207         if len(hostips) == 0:
1208             message("No external IPv4 address has been found: I use the loopback.")
1209             hostip = '127.0.0.1'
1210         else:
1211             hostip = hostips[0]
1212             if len(hostips) > 1:
1213                 message("More than one IPv4 address found: I use " + hostip + ".")
1214
1215     if hostip6 is None:
1216         try:
1217             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1218                 if hostip6 is None:
1219                     hostip6 = ip[-1][0]
1220                 if hostip6 == '::1' and ip[-1][0] != '::1':
1221                     hostip6 = ip[-1][0]
1222         except socket.gaierror, (socket.EAI_NODATA, msg): 
1223             hostip6 = None
1224
1225     if serverrole is None:
1226         serverrole = lp.get("server role")
1227
1228     assert serverrole in ("domain controller", "member server", "standalone")
1229     if invocationid is None:
1230         invocationid = str(uuid.uuid4())
1231
1232     if not os.path.exists(paths.private_dir):
1233         os.mkdir(paths.private_dir)
1234     if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1235         os.mkdir(os.path.join(paths.private_dir,"tls"))
1236
1237     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1238  
1239     schema = Schema(setup_path, domainsid, schemadn=names.schemadn,
1240                     serverdn=names.serverdn)
1241     
1242     if backend_type == "ldb":
1243         provision_backend = LDBBackend(backend_type,
1244                                          paths=paths, setup_path=setup_path,
1245                                          lp=lp, credentials=credentials, 
1246                                          names=names,
1247                                          message=message)
1248     elif backend_type == "existing":
1249         provision_backend = ExistingBackend(backend_type,
1250                                          paths=paths, setup_path=setup_path,
1251                                          lp=lp, credentials=credentials, 
1252                                          names=names,
1253                                          message=message,
1254                                          ldapi_url=ldapi_url)
1255     elif backend_type == "fedora-ds":
1256         provision_backend = FDSBackend(backend_type,
1257                                          paths=paths, setup_path=setup_path,
1258                                          lp=lp, credentials=credentials, 
1259                                          names=names,
1260                                          message=message,
1261                                          domainsid=domainsid,
1262                                          schema=schema,
1263                                          hostname=hostname,
1264                                          ldapadminpass=ldapadminpass,
1265                                          slapd_path=slapd_path,
1266                                          ldap_backend_extra_port=ldap_backend_extra_port,
1267                                          ldap_dryrun_mode=ldap_dryrun_mode,
1268                                          root=root,
1269                                          setup_ds_path=setup_ds_path)
1270     elif backend_type == "openldap":
1271         provision_backend = OpenLDAPBackend(backend_type,
1272                                          paths=paths, setup_path=setup_path,
1273                                          lp=lp, credentials=credentials, 
1274                                          names=names,
1275                                          message=message,
1276                                          domainsid=domainsid,
1277                                          schema=schema,
1278                                          hostname=hostname,
1279                                          ldapadminpass=ldapadminpass,
1280                                          slapd_path=slapd_path,
1281                                          ldap_backend_extra_port=ldap_backend_extra_port,
1282                                          ldap_dryrun_mode=ldap_dryrun_mode,
1283                                          ol_mmr_urls=ol_mmr_urls, 
1284                                          nosync=nosync)
1285     else:
1286         raise ValueError("Unknown LDAP backend type selected")
1287
1288     provision_backend.init()
1289     provision_backend.start()
1290
1291     # only install a new shares config db if there is none
1292     if not os.path.exists(paths.shareconf):
1293         message("Setting up share.ldb")
1294         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1295                         lp=lp)
1296         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1297
1298      
1299     message("Setting up secrets.ldb")
1300     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1301         session_info=session_info,
1302         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1303
1304     message("Setting up the registry")
1305     setup_registry(paths.hklm, setup_path, session_info, 
1306                    lp=lp)
1307
1308     message("Setting up the privileges database")
1309     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1310
1311     message("Setting up idmap db")
1312     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1313                           lp=lp)
1314
1315     message("Setting up SAM db")
1316     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1317                         provision_backend, lp, names,
1318                         message, 
1319                         domainsid=domainsid, 
1320                         schema=schema, domainguid=domainguid,
1321                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1322                         fill=samdb_fill, 
1323                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1324                         invocationid=invocationid, 
1325                         machinepass=machinepass, dnspass=dnspass, 
1326                         ntdsguid=ntdsguid, serverrole=serverrole,
1327                         dom_for_fun_level=dom_for_fun_level)
1328
1329     if serverrole == "domain controller":
1330         if paths.netlogon is None:
1331             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1332             message("Please either remove %s or see the template at %s" % 
1333                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1334             assert paths.netlogon is not None
1335
1336         if paths.sysvol is None:
1337             message("Existing smb.conf does not have a [sysvol] share, but you"
1338                     " are configuring a DC.")
1339             message("Please either remove %s or see the template at %s" % 
1340                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1341             assert paths.sysvol is not None
1342
1343         if not os.path.isdir(paths.netlogon):
1344             os.makedirs(paths.netlogon, 0755)
1345
1346     if samdb_fill == FILL_FULL:
1347         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1348                             root_uid=root_uid, nobody_uid=nobody_uid,
1349                             users_gid=users_gid, wheel_gid=wheel_gid)
1350
1351         if serverrole == "domain controller":
1352             # Set up group policies (domain policy and domain controller policy)
1353             setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1354             setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, 
1355                          domainsid, names.dnsdomain, names.domaindn, lp)
1356
1357         message("Setting up sam.ldb rootDSE marking as synchronized")
1358         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1359
1360         secretsdb_self_join(secrets_ldb, domain=names.domain,
1361                             realm=names.realm,
1362                             dnsdomain=names.dnsdomain,
1363                             netbiosname=names.netbiosname,
1364                             domainsid=domainsid, 
1365                             machinepass=machinepass,
1366                             secure_channel_type=SEC_CHAN_BDC)
1367
1368         if serverrole == "domain controller":
1369             secretsdb_setup_dns(secrets_ldb, setup_path,
1370                                 paths.private_dir,
1371                                 realm=names.realm, dnsdomain=names.dnsdomain,
1372                                 dns_keytab_path=paths.dns_keytab,
1373                                 dnspass=dnspass)
1374
1375             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1376             assert isinstance(domainguid, str)
1377
1378             # Only make a zone file on the first DC, it should be replicated
1379             # with DNS replication
1380             create_zone_file(lp, message, paths, targetdir, setup_path,
1381                 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1382                 hostname=names.hostname, realm=names.realm, 
1383                 domainguid=domainguid, ntdsguid=names.ntdsguid)
1384
1385             create_named_conf(paths, setup_path, realm=names.realm,
1386                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1387
1388             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1389                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1390                               keytab_name=paths.dns_keytab)
1391             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1392             message("and %s for further documentation required for secure DNS "
1393                     "updates" % paths.namedtxt)
1394
1395             create_krb5_conf(paths.krb5conf, setup_path,
1396                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1397                              realm=names.realm)
1398             message("A Kerberos configuration suitable for Samba 4 has been "
1399                     "generated at %s" % paths.krb5conf)
1400
1401     if serverrole == "domain controller":
1402         create_dns_update_list(lp, message, paths, setup_path)
1403
1404     provision_backend.post_setup()
1405     provision_backend.shutdown()
1406     
1407     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1408                                ldapi_url)
1409
1410     #Now commit the secrets.ldb to disk
1411     secrets_ldb.transaction_commit()
1412
1413     # the commit creates the dns.keytab, now chown it
1414     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1415     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1416         try:
1417             os.chmod(dns_keytab_path, 0640)
1418             os.chown(dns_keytab_path, -1, paths.bind_gid)
1419         except OSError:
1420             message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1421                 paths.bind_gid))
1422
1423
1424     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1425
1426     message("Once the above files are installed, your Samba4 server will be ready to use")
1427     message("Server Role:           %s" % serverrole)
1428     message("Hostname:              %s" % names.hostname)
1429     message("NetBIOS Domain:        %s" % names.domain)
1430     message("DNS Domain:            %s" % names.dnsdomain)
1431     message("DOMAIN SID:            %s" % str(domainsid))
1432     if samdb_fill == FILL_FULL:
1433         message("Admin password:        %s" % adminpass)
1434     if provision_backend.type is not "ldb":
1435         if provision_backend.credentials.get_bind_dn() is not None:
1436             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1437         else:
1438             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1439
1440         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1441
1442         if provision_backend.slapd_command_escaped is not None:
1443             # now display slapd_command_file.txt to show how slapd must be started next time
1444             message("Use later the following commandline to start slapd, then Samba:")
1445             message(provision_backend.slapd_command_escaped)
1446             message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1447
1448
1449     result = ProvisionResult()
1450     result.domaindn = domaindn
1451     result.paths = paths
1452     result.lp = lp
1453     result.samdb = samdb
1454     return result
1455
1456
1457 def provision_become_dc(setup_dir=None,
1458                         smbconf=None, targetdir=None, realm=None, 
1459                         rootdn=None, domaindn=None, schemadn=None,
1460                         configdn=None, serverdn=None,
1461                         domain=None, hostname=None, domainsid=None, 
1462                         adminpass=None, krbtgtpass=None, domainguid=None, 
1463                         policyguid=None, policyguid_dc=None, invocationid=None,
1464                         machinepass=None, 
1465                         dnspass=None, root=None, nobody=None, users=None, 
1466                         wheel=None, backup=None, serverrole=None, 
1467                         ldap_backend=None, ldap_backend_type=None,
1468                         sitename=None, debuglevel=1):
1469
1470     def message(text):
1471         """print a message if quiet is not set."""
1472         print text
1473
1474     samba.set_debug_level(debuglevel)
1475
1476     return provision(setup_dir, message, system_session(), None,
1477               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1478               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1479               configdn=configdn, serverdn=serverdn, domain=domain,
1480               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1481               machinepass=machinepass, serverrole="domain controller",
1482               sitename=sitename)
1483
1484
1485 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1486     """Create a PHP LDAP admin configuration file.
1487
1488     :param path: Path to write the configuration to.
1489     :param setup_path: Function to generate setup paths.
1490     """
1491     setup_file(setup_path("phpldapadmin-config.php"), path, 
1492             {"S4_LDAPI_URI": ldapi_uri})
1493
1494
1495 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1496                      hostip, hostip6, hostname, realm, domainguid,
1497                      ntdsguid):
1498     """Write out a DNS zone file, from the info in the current database.
1499
1500     :param paths: paths object
1501     :param setup_path: Setup path function.
1502     :param dnsdomain: DNS Domain name
1503     :param domaindn: DN of the Domain
1504     :param hostip: Local IPv4 IP
1505     :param hostip6: Local IPv6 IP
1506     :param hostname: Local hostname
1507     :param realm: Realm name
1508     :param domainguid: GUID of the domain.
1509     :param ntdsguid: GUID of the hosts nTDSDSA record.
1510     """
1511     assert isinstance(domainguid, str)
1512
1513     if hostip6 is not None:
1514         hostip6_base_line = "            IN AAAA    " + hostip6
1515         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1516     else:
1517         hostip6_base_line = ""
1518         hostip6_host_line = ""
1519
1520     if hostip is not None:
1521         hostip_base_line = "            IN A    " + hostip
1522         hostip_host_line = hostname + "        IN A    " + hostip
1523     else:
1524         hostip_base_line = ""
1525         hostip_host_line = ""
1526
1527     dns_dir = os.path.dirname(paths.dns)
1528
1529     try:
1530         shutil.rmtree(dns_dir, True)
1531     except OSError:
1532         pass
1533
1534     os.mkdir(dns_dir, 0775)
1535
1536     # we need to freeze the zone while we update the contents
1537     if targetdir is None:
1538         rndc = ' '.join(lp.get("rndc command"))
1539         os.system(rndc + " freeze " + lp.get("realm"))
1540
1541     setup_file(setup_path("provision.zone"), paths.dns, {
1542             "HOSTNAME": hostname,
1543             "DNSDOMAIN": dnsdomain,
1544             "REALM": realm,
1545             "HOSTIP_BASE_LINE": hostip_base_line,
1546             "HOSTIP_HOST_LINE": hostip_host_line,
1547             "DOMAINGUID": domainguid,
1548             "DATESTRING": time.strftime("%Y%m%d%H"),
1549             "DEFAULTSITE": DEFAULTSITE,
1550             "NTDSGUID": ntdsguid,
1551             "HOSTIP6_BASE_LINE": hostip6_base_line,
1552             "HOSTIP6_HOST_LINE": hostip6_host_line,
1553         })
1554
1555     # note that we use no variable substitution on this file
1556     # the substitution is done at runtime by samba_dnsupdate
1557     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1558
1559     if paths.bind_gid is not None:
1560         try:
1561             os.chown(dns_dir, -1, paths.bind_gid)
1562             os.chown(paths.dns, -1, paths.bind_gid)
1563             # chmod needed to cope with umask
1564             os.chmod(dns_dir, 0775)
1565             os.chmod(paths.dns, 0664)
1566         except OSError:
1567             message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1568
1569     if targetdir is None:
1570         os.system(rndc + " unfreeze " + lp.get("realm"))
1571
1572
1573 def create_dns_update_list(lp, message, paths, setup_path):
1574     """Write out a dns_update_list file"""
1575     # note that we use no variable substitution on this file
1576     # the substitution is done at runtime by samba_dnsupdate
1577     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1578
1579
1580 def create_named_conf(paths, setup_path, realm, dnsdomain,
1581                       private_dir):
1582     """Write out a file containing zone statements suitable for inclusion in a
1583     named.conf file (including GSS-TSIG configuration).
1584     
1585     :param paths: all paths
1586     :param setup_path: Setup path function.
1587     :param realm: Realm name
1588     :param dnsdomain: DNS Domain name
1589     :param private_dir: Path to private directory
1590     :param keytab_name: File name of DNS keytab file
1591     """
1592
1593     setup_file(setup_path("named.conf"), paths.namedconf, {
1594             "DNSDOMAIN": dnsdomain,
1595             "REALM": realm,
1596             "ZONE_FILE": paths.dns,
1597             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1598             "NAMED_CONF": paths.namedconf,
1599             "NAMED_CONF_UPDATE": paths.namedconf_update
1600             })
1601
1602     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1603
1604 def create_named_txt(path, setup_path, realm, dnsdomain,
1605                       private_dir, keytab_name):
1606     """Write out a file containing zone statements suitable for inclusion in a
1607     named.conf file (including GSS-TSIG configuration).
1608     
1609     :param path: Path of the new named.conf file.
1610     :param setup_path: Setup path function.
1611     :param realm: Realm name
1612     :param dnsdomain: DNS Domain name
1613     :param private_dir: Path to private directory
1614     :param keytab_name: File name of DNS keytab file
1615     """
1616
1617     setup_file(setup_path("named.txt"), path, {
1618             "DNSDOMAIN": dnsdomain,
1619             "REALM": realm,
1620             "DNS_KEYTAB": keytab_name,
1621             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1622             "PRIVATE_DIR": private_dir
1623         })
1624
1625 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1626     """Write out a file containing zone statements suitable for inclusion in a
1627     named.conf file (including GSS-TSIG configuration).
1628     
1629     :param path: Path of the new named.conf file.
1630     :param setup_path: Setup path function.
1631     :param dnsdomain: DNS Domain name
1632     :param hostname: Local hostname
1633     :param realm: Realm name
1634     """
1635     setup_file(setup_path("krb5.conf"), path, {
1636             "DNSDOMAIN": dnsdomain,
1637             "HOSTNAME": hostname,
1638             "REALM": realm,
1639         })
1640
1641
1642 class ProvisioningError(Exception):
1643     """A generic provision error."""
1644
1645     def __init__(self, value):
1646         self.value = value
1647
1648     def __str__(self):
1649         return "ProvisioningError: " + self.value
1650
1651
1652 class InvalidNetbiosName(Exception):
1653     """A specified name was not a valid NetBIOS name."""
1654     def __init__(self, name):
1655         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)