abbcf5221055b8da7c1203ade0de669ce818d03a
[abartlet/samba.git/.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         samdb.write_prefixes_from_schema()
949         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
950         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
951                        {"SCHEMADN": names.schemadn})
952
953         message("Reopening sam.ldb with new schema")
954     except:
955         samdb.transaction_cancel()
956         raise
957     else:
958         samdb.transaction_commit()
959
960     samdb = SamDB(session_info=admin_session_info,
961                 credentials=provision_backend.credentials, lp=lp,
962                 global_schema=False)
963     samdb.connect(path)
964     samdb.transaction_start()
965     try:
966         samdb.invocation_id = invocationid
967
968         message("Setting up sam.ldb configuration data")
969         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
970             "CONFIGDN": names.configdn,
971             "NETBIOSNAME": names.netbiosname,
972             "DEFAULTSITE": names.sitename,
973             "DNSDOMAIN": names.dnsdomain,
974             "DOMAIN": names.domain,
975             "SCHEMADN": names.schemadn,
976             "DOMAINDN": names.domaindn,
977             "SERVERDN": names.serverdn,
978             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
979             })
980
981         message("Setting up display specifiers")
982         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
983         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
984         check_all_substituted(display_specifiers_ldif)
985         samdb.add_ldif(display_specifiers_ldif)
986
987         message("Adding users container")
988         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
989                 "DOMAINDN": names.domaindn})
990         message("Modifying users container")
991         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
992                 "DOMAINDN": names.domaindn})
993         message("Adding computers container")
994         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
995                 "DOMAINDN": names.domaindn})
996         message("Modifying computers container")
997         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
998                 "DOMAINDN": names.domaindn})
999         message("Setting up sam.ldb data")
1000         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1001             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1002             "DOMAINDN": names.domaindn,
1003             "NETBIOSNAME": names.netbiosname,
1004             "DEFAULTSITE": names.sitename,
1005             "CONFIGDN": names.configdn,
1006             "SERVERDN": names.serverdn,
1007             "POLICYGUID_DC": policyguid_dc
1008             })
1009
1010         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1011                 "DOMAINDN": names.domaindn})
1012
1013         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1014                 "CONFIGDN": names.configdn,
1015                 "SCHEMADN": names.schemadn})
1016         if fill == FILL_FULL:
1017             message("Setting up sam.ldb users and groups")
1018             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1019                 "DOMAINDN": names.domaindn,
1020                 "DOMAINSID": str(domainsid),
1021                 "CONFIGDN": names.configdn,
1022                 "ADMINPASS_B64": b64encode(adminpass),
1023                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1024                 })
1025
1026             message("Setting up self join")
1027             setup_self_join(samdb, names=names, invocationid=invocationid,
1028                             dnspass=dnspass,
1029                             machinepass=machinepass,
1030                             domainsid=domainsid, policyguid=policyguid,
1031                             policyguid_dc=policyguid_dc,
1032                             setup_path=setup_path,
1033                             domainControllerFunctionality=domainControllerFunctionality,
1034                             ntdsguid=ntdsguid)
1035
1036             ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1037             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1038                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1039             assert isinstance(names.ntdsguid, str)
1040     except:
1041         samdb.transaction_cancel()
1042         raise
1043     else:
1044         samdb.transaction_commit()
1045         return samdb
1046
1047
1048 FILL_FULL = "FULL"
1049 FILL_NT4SYNC = "NT4SYNC"
1050 FILL_DRS = "DRS"
1051 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1052 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)"
1053
1054 def set_dir_acl(path, acl, lp, domsid):
1055     setntacl(lp, path, acl, domsid)
1056     for root, dirs, files in os.walk(path, topdown=False):
1057         for name in files:
1058             setntacl(lp, os.path.join(root, name), acl, domsid)
1059         for name in dirs:
1060             setntacl(lp, os.path.join(root, name), acl, domsid)
1061
1062
1063 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1064     # Set ACL for GPO
1065     policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1066     set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)), 
1067         lp, str(domainsid))
1068     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1069                         attrs=["cn","nTSecurityDescriptor"],
1070                         expression="", scope=ldb.SCOPE_ONELEVEL)
1071     for policy in res:
1072         acl = ndr_unpack(security.descriptor, 
1073                          str(policy["nTSecurityDescriptor"])).as_sddl()
1074         policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1075         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, 
1076                     str(domainsid))
1077
1078 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1079     lp):
1080     try:
1081         os.chown(sysvol,-1,gid)
1082     except:
1083         canchown = False
1084     else:
1085         canchown = True
1086
1087     setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1088     for root, dirs, files in os.walk(sysvol, topdown=False):
1089         for name in files:
1090             if canchown:
1091                 os.chown(os.path.join(root, name),-1,gid)
1092             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1093         for name in dirs:
1094             if canchown:
1095                 os.chown(os.path.join(root, name),-1,gid)
1096             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1097     set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1098
1099
1100 def provision(setup_dir, message, session_info, 
1101               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1102               realm=None, 
1103               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1104               serverdn=None,
1105               domain=None, hostname=None, hostip=None, hostip6=None, 
1106               domainsid=None, adminpass=None, ldapadminpass=None, 
1107               krbtgtpass=None, domainguid=None, 
1108               policyguid=None, policyguid_dc=None, invocationid=None,
1109               machinepass=None, ntdsguid=None,
1110               dnspass=None, root=None, nobody=None, users=None, 
1111               wheel=None, backup=None, aci=None, serverrole=None,
1112               dom_for_fun_level=None,
1113               ldap_backend_extra_port=None, backend_type=None,
1114               sitename=None,
1115               ol_mmr_urls=None, ol_olc=None, 
1116               setup_ds_path=None, slapd_path=None, nosync=False,
1117               ldap_dryrun_mode=False,useeadb=False):
1118     """Provision samba4
1119     
1120     :note: caution, this wipes all existing data!
1121     """
1122
1123     def setup_path(file):
1124       return os.path.join(setup_dir, file)
1125
1126     if domainsid is None:
1127       domainsid = security.random_sid()
1128     else:
1129       domainsid = security.dom_sid(domainsid)
1130
1131     # create/adapt the group policy GUIDs
1132     if policyguid is None:
1133         policyguid = str(uuid.uuid4())
1134     policyguid = policyguid.upper()
1135     if policyguid_dc is None:
1136         policyguid_dc = str(uuid.uuid4())
1137     policyguid_dc = policyguid_dc.upper()
1138
1139     if adminpass is None:
1140         adminpass = samba.generate_random_password(12, 32)
1141     if krbtgtpass is None:
1142         krbtgtpass = samba.generate_random_password(128, 255)
1143     if machinepass is None:
1144         machinepass  = samba.generate_random_password(128, 255)
1145     if dnspass is None:
1146         dnspass = samba.generate_random_password(128, 255)
1147     if ldapadminpass is None:
1148         #Make a new, random password between Samba and it's LDAP server
1149         ldapadminpass=samba.generate_random_password(128, 255)
1150
1151     if backend_type is None:
1152         backend_type = "ldb"
1153
1154     sid_generator = "internal"
1155     if backend_type == "fedora-ds":
1156         sid_generator = "backend"
1157
1158     root_uid = findnss_uid([root or "root"])
1159     nobody_uid = findnss_uid([nobody or "nobody"])
1160     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1161     if wheel is None:
1162         wheel_gid = findnss_gid(["wheel", "adm"])
1163     else:
1164         wheel_gid = findnss_gid([wheel])
1165     try:
1166         bind_gid = findnss_gid(["bind", "named"])
1167     except KeyError:
1168         bind_gid = None
1169
1170     if targetdir is not None:
1171         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1172             os.makedirs(os.path.join(targetdir, "etc"))
1173         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1174     elif smbconf is None:
1175         smbconf = param.default_path()
1176
1177     # only install a new smb.conf if there isn't one there already
1178     if os.path.exists(smbconf):
1179         # if Samba Team members can't figure out the weird errors
1180         # loading an empty smb.conf gives, then we need to be smarter.
1181         # Pretend it just didn't exist --abartlet
1182         data = open(smbconf, 'r').read()
1183         data = data.lstrip()
1184         if data is None or data == "":
1185             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1186                          serverrole, targetdir, sid_generator, useeadb)
1187     else: 
1188         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1189                      targetdir, sid_generator, useeadb)
1190
1191     lp = param.LoadParm()
1192     lp.load(smbconf)
1193
1194     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1195                         dnsdomain=realm, serverrole=serverrole,
1196                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1197                         serverdn=serverdn, sitename=sitename)
1198
1199     paths = provision_paths_from_lp(lp, names.dnsdomain)
1200
1201     paths.bind_gid = bind_gid
1202
1203     if hostip is None:
1204         hostips = samba.interface_ips(lp, False)
1205         if len(hostips) == 0:
1206             message("No external IPv4 address has been found: I use the loopback.")
1207             hostip = '127.0.0.1'
1208         else:
1209             hostip = hostips[0]
1210             if len(hostips) > 1:
1211                 message("More than one IPv4 address found: I use " + hostip + ".")
1212
1213     if hostip6 is None:
1214         try:
1215             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1216                 if hostip6 is None:
1217                     hostip6 = ip[-1][0]
1218                 if hostip6 == '::1' and ip[-1][0] != '::1':
1219                     hostip6 = ip[-1][0]
1220         except socket.gaierror, (socket.EAI_NODATA, msg): 
1221             hostip6 = None
1222
1223     if serverrole is None:
1224         serverrole = lp.get("server role")
1225
1226     assert serverrole in ("domain controller", "member server", "standalone")
1227     if invocationid is None:
1228         invocationid = str(uuid.uuid4())
1229
1230     if not os.path.exists(paths.private_dir):
1231         os.mkdir(paths.private_dir)
1232     if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1233         os.mkdir(os.path.join(paths.private_dir,"tls"))
1234
1235     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1236  
1237     schema = Schema(setup_path, domainsid, schemadn=names.schemadn,
1238                     serverdn=names.serverdn)
1239     
1240     if backend_type == "ldb":
1241         provision_backend = LDBBackend(backend_type,
1242                                          paths=paths, setup_path=setup_path,
1243                                          lp=lp, credentials=credentials, 
1244                                          names=names,
1245                                          message=message)
1246     elif backend_type == "existing":
1247         provision_backend = ExistingBackend(backend_type,
1248                                          paths=paths, setup_path=setup_path,
1249                                          lp=lp, credentials=credentials, 
1250                                          names=names,
1251                                          message=message,
1252                                          ldapi_url=ldapi_url)
1253     elif backend_type == "fedora-ds":
1254         provision_backend = FDSBackend(backend_type,
1255                                          paths=paths, setup_path=setup_path,
1256                                          lp=lp, credentials=credentials, 
1257                                          names=names,
1258                                          message=message,
1259                                          domainsid=domainsid,
1260                                          schema=schema,
1261                                          hostname=hostname,
1262                                          ldapadminpass=ldapadminpass,
1263                                          slapd_path=slapd_path,
1264                                          ldap_backend_extra_port=ldap_backend_extra_port,
1265                                          ldap_dryrun_mode=ldap_dryrun_mode,
1266                                          root=root,
1267                                          setup_ds_path=setup_ds_path)
1268     elif backend_type == "openldap":
1269         provision_backend = OpenLDAPBackend(backend_type,
1270                                          paths=paths, setup_path=setup_path,
1271                                          lp=lp, credentials=credentials, 
1272                                          names=names,
1273                                          message=message,
1274                                          domainsid=domainsid,
1275                                          schema=schema,
1276                                          hostname=hostname,
1277                                          ldapadminpass=ldapadminpass,
1278                                          slapd_path=slapd_path,
1279                                          ldap_backend_extra_port=ldap_backend_extra_port,
1280                                          ldap_dryrun_mode=ldap_dryrun_mode,
1281                                          ol_mmr_urls=ol_mmr_urls, 
1282                                          nosync=nosync)
1283     else:
1284         raise ValueError("Unknown LDAP backend type selected")
1285
1286     provision_backend.init()
1287     provision_backend.start()
1288
1289     # only install a new shares config db if there is none
1290     if not os.path.exists(paths.shareconf):
1291         message("Setting up share.ldb")
1292         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1293                         lp=lp)
1294         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1295
1296      
1297     message("Setting up secrets.ldb")
1298     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1299         session_info=session_info,
1300         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1301
1302     message("Setting up the registry")
1303     setup_registry(paths.hklm, setup_path, session_info, 
1304                    lp=lp)
1305
1306     message("Setting up the privileges database")
1307     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1308
1309     message("Setting up idmap db")
1310     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1311                           lp=lp)
1312
1313     message("Setting up SAM db")
1314     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1315                         provision_backend, lp, names,
1316                         message, 
1317                         domainsid=domainsid, 
1318                         schema=schema, domainguid=domainguid,
1319                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1320                         fill=samdb_fill, 
1321                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1322                         invocationid=invocationid, 
1323                         machinepass=machinepass, dnspass=dnspass, 
1324                         ntdsguid=ntdsguid, serverrole=serverrole,
1325                         dom_for_fun_level=dom_for_fun_level)
1326
1327     if serverrole == "domain controller":
1328         if paths.netlogon is None:
1329             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1330             message("Please either remove %s or see the template at %s" % 
1331                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1332             assert paths.netlogon is not None
1333
1334         if paths.sysvol is None:
1335             message("Existing smb.conf does not have a [sysvol] share, but you"
1336                     " are configuring a DC.")
1337             message("Please either remove %s or see the template at %s" % 
1338                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1339             assert paths.sysvol is not None
1340
1341         if not os.path.isdir(paths.netlogon):
1342             os.makedirs(paths.netlogon, 0755)
1343
1344     if samdb_fill == FILL_FULL:
1345         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1346                             root_uid=root_uid, nobody_uid=nobody_uid,
1347                             users_gid=users_gid, wheel_gid=wheel_gid)
1348
1349         if serverrole == "domain controller":
1350             # Set up group policies (domain policy and domain controller policy)
1351             setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1352             setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, 
1353                          domainsid, names.dnsdomain, names.domaindn, lp)
1354
1355         message("Setting up sam.ldb rootDSE marking as synchronized")
1356         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1357
1358         secretsdb_self_join(secrets_ldb, domain=names.domain,
1359                             realm=names.realm,
1360                             dnsdomain=names.dnsdomain,
1361                             netbiosname=names.netbiosname,
1362                             domainsid=domainsid, 
1363                             machinepass=machinepass,
1364                             secure_channel_type=SEC_CHAN_BDC)
1365
1366         if serverrole == "domain controller":
1367             secretsdb_setup_dns(secrets_ldb, setup_path,
1368                                 paths.private_dir,
1369                                 realm=names.realm, dnsdomain=names.dnsdomain,
1370                                 dns_keytab_path=paths.dns_keytab,
1371                                 dnspass=dnspass)
1372
1373             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1374             assert isinstance(domainguid, str)
1375
1376             # Only make a zone file on the first DC, it should be replicated
1377             # with DNS replication
1378             create_zone_file(lp, message, paths, targetdir, setup_path,
1379                 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1380                 hostname=names.hostname, realm=names.realm, 
1381                 domainguid=domainguid, ntdsguid=names.ntdsguid)
1382
1383             create_named_conf(paths, setup_path, realm=names.realm,
1384                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1385
1386             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1387                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1388                               keytab_name=paths.dns_keytab)
1389             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1390             message("and %s for further documentation required for secure DNS "
1391                     "updates" % paths.namedtxt)
1392
1393             create_krb5_conf(paths.krb5conf, setup_path,
1394                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1395                              realm=names.realm)
1396             message("A Kerberos configuration suitable for Samba 4 has been "
1397                     "generated at %s" % paths.krb5conf)
1398
1399     if serverrole == "domain controller":
1400         create_dns_update_list(lp, message, paths, setup_path)
1401
1402     provision_backend.post_setup()
1403     provision_backend.shutdown()
1404     
1405     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1406                                ldapi_url)
1407
1408     #Now commit the secrets.ldb to disk
1409     secrets_ldb.transaction_commit()
1410
1411     # the commit creates the dns.keytab, now chown it
1412     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1413     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1414         try:
1415             os.chmod(dns_keytab_path, 0640)
1416             os.chown(dns_keytab_path, -1, paths.bind_gid)
1417         except OSError:
1418             message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1419                 paths.bind_gid))
1420
1421
1422     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1423
1424     message("Once the above files are installed, your Samba4 server will be ready to use")
1425     message("Server Role:           %s" % serverrole)
1426     message("Hostname:              %s" % names.hostname)
1427     message("NetBIOS Domain:        %s" % names.domain)
1428     message("DNS Domain:            %s" % names.dnsdomain)
1429     message("DOMAIN SID:            %s" % str(domainsid))
1430     if samdb_fill == FILL_FULL:
1431         message("Admin password:        %s" % adminpass)
1432     if provision_backend.type is not "ldb":
1433         if provision_backend.credentials.get_bind_dn() is not None:
1434             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1435         else:
1436             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1437
1438         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1439
1440         if provision_backend.slapd_command_escaped is not None:
1441             # now display slapd_command_file.txt to show how slapd must be started next time
1442             message("Use later the following commandline to start slapd, then Samba:")
1443             message(provision_backend.slapd_command_escaped)
1444             message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1445
1446
1447     result = ProvisionResult()
1448     result.domaindn = domaindn
1449     result.paths = paths
1450     result.lp = lp
1451     result.samdb = samdb
1452     return result
1453
1454
1455 def provision_become_dc(setup_dir=None,
1456                         smbconf=None, targetdir=None, realm=None, 
1457                         rootdn=None, domaindn=None, schemadn=None,
1458                         configdn=None, serverdn=None,
1459                         domain=None, hostname=None, domainsid=None, 
1460                         adminpass=None, krbtgtpass=None, domainguid=None, 
1461                         policyguid=None, policyguid_dc=None, invocationid=None,
1462                         machinepass=None, 
1463                         dnspass=None, root=None, nobody=None, users=None, 
1464                         wheel=None, backup=None, serverrole=None, 
1465                         ldap_backend=None, ldap_backend_type=None,
1466                         sitename=None, debuglevel=1):
1467
1468     def message(text):
1469         """print a message if quiet is not set."""
1470         print text
1471
1472     samba.set_debug_level(debuglevel)
1473
1474     return provision(setup_dir, message, system_session(), None,
1475               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1476               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1477               configdn=configdn, serverdn=serverdn, domain=domain,
1478               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1479               machinepass=machinepass, serverrole="domain controller",
1480               sitename=sitename)
1481
1482
1483 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1484     """Create a PHP LDAP admin configuration file.
1485
1486     :param path: Path to write the configuration to.
1487     :param setup_path: Function to generate setup paths.
1488     """
1489     setup_file(setup_path("phpldapadmin-config.php"), path, 
1490             {"S4_LDAPI_URI": ldapi_uri})
1491
1492
1493 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1494                      hostip, hostip6, hostname, realm, domainguid,
1495                      ntdsguid):
1496     """Write out a DNS zone file, from the info in the current database.
1497
1498     :param paths: paths object
1499     :param setup_path: Setup path function.
1500     :param dnsdomain: DNS Domain name
1501     :param domaindn: DN of the Domain
1502     :param hostip: Local IPv4 IP
1503     :param hostip6: Local IPv6 IP
1504     :param hostname: Local hostname
1505     :param realm: Realm name
1506     :param domainguid: GUID of the domain.
1507     :param ntdsguid: GUID of the hosts nTDSDSA record.
1508     """
1509     assert isinstance(domainguid, str)
1510
1511     if hostip6 is not None:
1512         hostip6_base_line = "            IN AAAA    " + hostip6
1513         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1514     else:
1515         hostip6_base_line = ""
1516         hostip6_host_line = ""
1517
1518     if hostip is not None:
1519         hostip_base_line = "            IN A    " + hostip
1520         hostip_host_line = hostname + "        IN A    " + hostip
1521     else:
1522         hostip_base_line = ""
1523         hostip_host_line = ""
1524
1525     dns_dir = os.path.dirname(paths.dns)
1526
1527     try:
1528         shutil.rmtree(dns_dir, True)
1529     except OSError:
1530         pass
1531
1532     os.mkdir(dns_dir, 0775)
1533
1534     # we need to freeze the zone while we update the contents
1535     if targetdir is None:
1536         rndc = ' '.join(lp.get("rndc command"))
1537         os.system(rndc + " freeze " + lp.get("realm"))
1538
1539     setup_file(setup_path("provision.zone"), paths.dns, {
1540             "HOSTNAME": hostname,
1541             "DNSDOMAIN": dnsdomain,
1542             "REALM": realm,
1543             "HOSTIP_BASE_LINE": hostip_base_line,
1544             "HOSTIP_HOST_LINE": hostip_host_line,
1545             "DOMAINGUID": domainguid,
1546             "DATESTRING": time.strftime("%Y%m%d%H"),
1547             "DEFAULTSITE": DEFAULTSITE,
1548             "NTDSGUID": ntdsguid,
1549             "HOSTIP6_BASE_LINE": hostip6_base_line,
1550             "HOSTIP6_HOST_LINE": hostip6_host_line,
1551         })
1552
1553     # note that we use no variable substitution on this file
1554     # the substitution is done at runtime by samba_dnsupdate
1555     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1556
1557     if paths.bind_gid is not None:
1558         try:
1559             os.chown(dns_dir, -1, paths.bind_gid)
1560             os.chown(paths.dns, -1, paths.bind_gid)
1561             # chmod needed to cope with umask
1562             os.chmod(dns_dir, 0775)
1563             os.chmod(paths.dns, 0664)
1564         except OSError:
1565             message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1566
1567     if targetdir is None:
1568         os.system(rndc + " unfreeze " + lp.get("realm"))
1569
1570
1571 def create_dns_update_list(lp, message, paths, setup_path):
1572     """Write out a dns_update_list file"""
1573     # note that we use no variable substitution on this file
1574     # the substitution is done at runtime by samba_dnsupdate
1575     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1576
1577
1578 def create_named_conf(paths, setup_path, realm, dnsdomain,
1579                       private_dir):
1580     """Write out a file containing zone statements suitable for inclusion in a
1581     named.conf file (including GSS-TSIG configuration).
1582     
1583     :param paths: all paths
1584     :param setup_path: Setup path function.
1585     :param realm: Realm name
1586     :param dnsdomain: DNS Domain name
1587     :param private_dir: Path to private directory
1588     :param keytab_name: File name of DNS keytab file
1589     """
1590
1591     setup_file(setup_path("named.conf"), paths.namedconf, {
1592             "DNSDOMAIN": dnsdomain,
1593             "REALM": realm,
1594             "ZONE_FILE": paths.dns,
1595             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1596             "NAMED_CONF": paths.namedconf,
1597             "NAMED_CONF_UPDATE": paths.namedconf_update
1598             })
1599
1600     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1601
1602 def create_named_txt(path, setup_path, realm, dnsdomain,
1603                       private_dir, keytab_name):
1604     """Write out a file containing zone statements suitable for inclusion in a
1605     named.conf file (including GSS-TSIG configuration).
1606     
1607     :param path: Path of the new named.conf file.
1608     :param setup_path: Setup path function.
1609     :param realm: Realm name
1610     :param dnsdomain: DNS Domain name
1611     :param private_dir: Path to private directory
1612     :param keytab_name: File name of DNS keytab file
1613     """
1614
1615     setup_file(setup_path("named.txt"), path, {
1616             "DNSDOMAIN": dnsdomain,
1617             "REALM": realm,
1618             "DNS_KEYTAB": keytab_name,
1619             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1620             "PRIVATE_DIR": private_dir
1621         })
1622
1623 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1624     """Write out a file containing zone statements suitable for inclusion in a
1625     named.conf file (including GSS-TSIG configuration).
1626     
1627     :param path: Path of the new named.conf file.
1628     :param setup_path: Setup path function.
1629     :param dnsdomain: DNS Domain name
1630     :param hostname: Local hostname
1631     :param realm: Realm name
1632     """
1633     setup_file(setup_path("krb5.conf"), path, {
1634             "DNSDOMAIN": dnsdomain,
1635             "HOSTNAME": hostname,
1636             "REALM": realm,
1637         })
1638
1639
1640 class ProvisioningError(Exception):
1641     """A generic provision error."""
1642
1643     def __init__(self, value):
1644         self.value = value
1645
1646     def __str__(self):
1647         return "ProvisioningError: " + self.value
1648
1649
1650 class InvalidNetbiosName(Exception):
1651     """A specified name was not a valid NetBIOS name."""
1652     def __init__(self, name):
1653         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)