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