s4-rodc: Fix provision warnings by creating ntds objectGUID in provision
[kai/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, ntds_guid, dnspass, ntdsguid,
844                 serverrole, dom_for_fun_level=None,
845                 schema=None):
846     """Setup a complete SAM Database.
847     
848     :note: This will wipe the main SAM database file!
849     """
850
851     # ATTENTION: Do NOT change these default values without discussion with the
852     # team and/or release manager. They have a big impact on the whole program!
853     domainControllerFunctionality = DS_DC_FUNCTION_2008
854
855     if dom_for_fun_level is None:
856         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
857     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
858         message("You want to run SAMBA 4 on a domain and forest function level"
859                 " lower than Windows 2003 (Native). This is not recommended")
860
861     if dom_for_fun_level > domainControllerFunctionality:
862         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
863
864     domainFunctionality = dom_for_fun_level
865     forestFunctionality = dom_for_fun_level
866
867     # Also wipes the database
868     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
869         provision_backend=provision_backend, session_info=session_info,
870         names=names, serverrole=serverrole, schema=schema)
871
872     if schema is None:
873         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
874
875     # Load the database, but don's load the global schema and don't connect quite yet
876     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
877                   credentials=provision_backend.credentials, lp=lp, global_schema=False)
878
879     message("Pre-loading the Samba 4 and AD schema")
880
881     # Load the schema from the one we computed earlier
882     samdb.set_schema_from_ldb(schema.ldb)
883
884     # And now we can connect to the DB - the schema won't be loaded from the DB
885     samdb.connect(path)
886
887     if fill == FILL_DRS:
888         return samdb
889         
890     samdb.transaction_start()
891     try:
892         # Set the domain functionality levels onto the database.
893         # Various module (the password_hash module in particular) need
894         # to know what level of AD we are emulating.
895
896         # These will be fixed into the database via the database
897         # modifictions below, but we need them set from the start.
898         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
899         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
900         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
901
902         samdb.set_domain_sid(str(domainsid))
903         samdb.set_invocation_id(invocationid)
904         samdb.set_ntds_GUID(ntds_guid)
905         samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
906
907         message("Adding DomainDN: %s" % names.domaindn)
908
909 #impersonate domain admin
910         admin_session_info = admin_session(lp, str(domainsid))
911         samdb.set_session_info(admin_session_info)
912         if domainguid is not None:
913             domainguid_line = "objectGUID: %s\n-" % domainguid
914         else:
915             domainguid_line = ""
916
917         descr = b64encode(get_domain_descriptor(domainsid))
918         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
919                 "DOMAINDN": names.domaindn,
920                 "DOMAINGUID": domainguid_line,
921                 "DESCRIPTOR": descr
922                 })
923
924
925         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
926             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
927             "DOMAINSID": str(domainsid),
928             "SCHEMADN": names.schemadn, 
929             "NETBIOSNAME": names.netbiosname,
930             "DEFAULTSITE": names.sitename,
931             "CONFIGDN": names.configdn,
932             "SERVERDN": names.serverdn,
933             "POLICYGUID": policyguid,
934             "DOMAINDN": names.domaindn,
935             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
936             "SAMBA_VERSION_STRING": version
937             })
938
939         message("Adding configuration container")
940         descr = b64encode(get_config_descriptor(domainsid))
941         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
942             "CONFIGDN": names.configdn, 
943             "DESCRIPTOR": descr,
944             })
945
946         # The LDIF here was created when the Schema object was constructed
947         message("Setting up sam.ldb schema")
948         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
949         samdb.modify_ldif(schema.schema_dn_modify)
950         samdb.write_prefixes_from_schema()
951         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
952         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
953                        {"SCHEMADN": names.schemadn})
954
955         message("Reopening sam.ldb with new schema")
956     except:
957         samdb.transaction_cancel()
958         raise
959     else:
960         samdb.transaction_commit()
961
962     samdb = SamDB(session_info=admin_session_info,
963                 credentials=provision_backend.credentials, lp=lp,
964                 global_schema=False)
965     samdb.connect(path)
966     samdb.transaction_start()
967     try:
968         samdb.invocation_id = invocationid
969
970         message("Setting up sam.ldb configuration data")
971         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
972             "CONFIGDN": names.configdn,
973             "NETBIOSNAME": names.netbiosname,
974             "DEFAULTSITE": names.sitename,
975             "DNSDOMAIN": names.dnsdomain,
976             "DOMAIN": names.domain,
977             "SCHEMADN": names.schemadn,
978             "DOMAINDN": names.domaindn,
979             "SERVERDN": names.serverdn,
980             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
981             })
982
983         message("Setting up display specifiers")
984         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
985         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
986         check_all_substituted(display_specifiers_ldif)
987         samdb.add_ldif(display_specifiers_ldif)
988
989         message("Adding users container")
990         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
991                 "DOMAINDN": names.domaindn})
992         message("Modifying users container")
993         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
994                 "DOMAINDN": names.domaindn})
995         message("Adding computers container")
996         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
997                 "DOMAINDN": names.domaindn})
998         message("Modifying computers container")
999         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1000                 "DOMAINDN": names.domaindn})
1001         message("Setting up sam.ldb data")
1002         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1003             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1004             "DOMAINDN": names.domaindn,
1005             "NETBIOSNAME": names.netbiosname,
1006             "DEFAULTSITE": names.sitename,
1007             "CONFIGDN": names.configdn,
1008             "SERVERDN": names.serverdn,
1009             "POLICYGUID_DC": policyguid_dc
1010             })
1011
1012         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1013                 "DOMAINDN": names.domaindn})
1014
1015         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1016                 "CONFIGDN": names.configdn,
1017                 "SCHEMADN": names.schemadn})
1018         if fill == FILL_FULL:
1019             message("Setting up sam.ldb users and groups")
1020             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1021                 "DOMAINDN": names.domaindn,
1022                 "DOMAINSID": str(domainsid),
1023                 "CONFIGDN": names.configdn,
1024                 "ADMINPASS_B64": b64encode(adminpass),
1025                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1026                 })
1027
1028             message("Setting up self join")
1029             setup_self_join(samdb, names=names, invocationid=invocationid,
1030                             dnspass=dnspass,
1031                             machinepass=machinepass,
1032                             domainsid=domainsid, policyguid=policyguid,
1033                             policyguid_dc=policyguid_dc,
1034                             setup_path=setup_path,
1035                             domainControllerFunctionality=domainControllerFunctionality,
1036                             ntdsguid=ntdsguid)
1037
1038             ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1039             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1040                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1041             assert isinstance(names.ntdsguid, str)
1042     except:
1043         samdb.transaction_cancel()
1044         raise
1045     else:
1046         samdb.transaction_commit()
1047         return samdb
1048
1049
1050 FILL_FULL = "FULL"
1051 FILL_NT4SYNC = "NT4SYNC"
1052 FILL_DRS = "DRS"
1053 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1054 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1055
1056 def set_dir_acl(path, acl, lp, domsid):
1057     setntacl(lp, path, acl, domsid)
1058     for root, dirs, files in os.walk(path, topdown=False):
1059         for name in files:
1060             setntacl(lp, os.path.join(root, name), acl, domsid)
1061         for name in dirs:
1062             setntacl(lp, os.path.join(root, name), acl, domsid)
1063
1064
1065 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1066     # Set ACL for GPO
1067     policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1068     set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)), 
1069         lp, str(domainsid))
1070     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1071                         attrs=["cn","nTSecurityDescriptor"],
1072                         expression="", scope=ldb.SCOPE_ONELEVEL)
1073     for policy in res:
1074         acl = ndr_unpack(security.descriptor, 
1075                          str(policy["nTSecurityDescriptor"])).as_sddl()
1076         policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1077         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, 
1078                     str(domainsid))
1079
1080 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1081     lp):
1082     try:
1083         os.chown(sysvol,-1,gid)
1084     except:
1085         canchown = False
1086     else:
1087         canchown = True
1088
1089     setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1090     for root, dirs, files in os.walk(sysvol, topdown=False):
1091         for name in files:
1092             if canchown:
1093                 os.chown(os.path.join(root, name),-1,gid)
1094             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1095         for name in dirs:
1096             if canchown:
1097                 os.chown(os.path.join(root, name),-1,gid)
1098             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1099     set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1100
1101
1102 def provision(setup_dir, message, session_info, 
1103               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1104               realm=None, 
1105               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1106               serverdn=None,
1107               domain=None, hostname=None, hostip=None, hostip6=None, 
1108               domainsid=None, adminpass=None, ldapadminpass=None, 
1109               krbtgtpass=None, domainguid=None, 
1110               policyguid=None, policyguid_dc=None, invocationid=None,
1111               machinepass=None, ntdsguid=None,
1112               dnspass=None, root=None, nobody=None, users=None, 
1113               wheel=None, backup=None, aci=None, serverrole=None,
1114               dom_for_fun_level=None,
1115               ldap_backend_extra_port=None, backend_type=None,
1116               sitename=None,
1117               ol_mmr_urls=None, ol_olc=None, 
1118               setup_ds_path=None, slapd_path=None, nosync=False,
1119               ldap_dryrun_mode=False,useeadb=False):
1120     """Provision samba4
1121     
1122     :note: caution, this wipes all existing data!
1123     """
1124
1125     def setup_path(file):
1126       return os.path.join(setup_dir, file)
1127
1128     if domainsid is None:
1129       domainsid = security.random_sid()
1130     else:
1131       domainsid = security.dom_sid(domainsid)
1132
1133     # create/adapt the group policy GUIDs
1134     if policyguid is None:
1135         policyguid = str(uuid.uuid4())
1136     policyguid = policyguid.upper()
1137     if policyguid_dc is None:
1138         policyguid_dc = str(uuid.uuid4())
1139     policyguid_dc = policyguid_dc.upper()
1140
1141     if adminpass is None:
1142         adminpass = samba.generate_random_password(12, 32)
1143     if krbtgtpass is None:
1144         krbtgtpass = samba.generate_random_password(128, 255)
1145     if machinepass is None:
1146         machinepass  = samba.generate_random_password(128, 255)
1147     if dnspass is None:
1148         dnspass = samba.generate_random_password(128, 255)
1149     if ldapadminpass is None:
1150         #Make a new, random password between Samba and it's LDAP server
1151         ldapadminpass=samba.generate_random_password(128, 255)
1152
1153     if backend_type is None:
1154         backend_type = "ldb"
1155
1156     sid_generator = "internal"
1157     if backend_type == "fedora-ds":
1158         sid_generator = "backend"
1159
1160     root_uid = findnss_uid([root or "root"])
1161     nobody_uid = findnss_uid([nobody or "nobody"])
1162     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1163     if wheel is None:
1164         wheel_gid = findnss_gid(["wheel", "adm"])
1165     else:
1166         wheel_gid = findnss_gid([wheel])
1167     try:
1168         bind_gid = findnss_gid(["bind", "named"])
1169     except KeyError:
1170         bind_gid = None
1171
1172     if targetdir is not None:
1173         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1174     elif smbconf is None:
1175         smbconf = param.default_path()
1176     if not os.path.exists(os.path.dirname(smbconf)):
1177         os.makedirs(os.path.dirname(smbconf))
1178
1179     # only install a new smb.conf if there isn't one there already
1180     if os.path.exists(smbconf):
1181         # if Samba Team members can't figure out the weird errors
1182         # loading an empty smb.conf gives, then we need to be smarter.
1183         # Pretend it just didn't exist --abartlet
1184         data = open(smbconf, 'r').read()
1185         data = data.lstrip()
1186         if data is None or data == "":
1187             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1188                          serverrole, targetdir, sid_generator, useeadb)
1189     else: 
1190         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1191                      targetdir, sid_generator, useeadb)
1192
1193     lp = param.LoadParm()
1194     lp.load(smbconf)
1195
1196     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1197                         dnsdomain=realm, serverrole=serverrole,
1198                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1199                         serverdn=serverdn, sitename=sitename)
1200
1201     paths = provision_paths_from_lp(lp, names.dnsdomain)
1202
1203     paths.bind_gid = bind_gid
1204
1205     if hostip is None:
1206         hostips = samba.interface_ips(lp, False)
1207         if len(hostips) == 0:
1208             message("No external IPv4 address has been found: I use the loopback.")
1209             hostip = '127.0.0.1'
1210         else:
1211             hostip = hostips[0]
1212             if len(hostips) > 1:
1213                 message("More than one IPv4 address found: I use " + hostip + ".")
1214
1215     if hostip6 is None:
1216         try:
1217             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1218                 if hostip6 is None:
1219                     hostip6 = ip[-1][0]
1220                 if hostip6 == '::1' and ip[-1][0] != '::1':
1221                     hostip6 = ip[-1][0]
1222         except socket.gaierror, (socket.EAI_NODATA, msg): 
1223             hostip6 = None
1224
1225     if serverrole is None:
1226         serverrole = lp.get("server role")
1227
1228     assert serverrole in ("domain controller", "member server", "standalone")
1229     if invocationid is None:
1230         invocationid = str(uuid.uuid4())
1231
1232     ntds_guid = 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)
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                         ntds_guid=ntds_guid,
1328                         machinepass=machinepass, dnspass=dnspass, 
1329                         ntdsguid=ntdsguid, serverrole=serverrole,
1330                         dom_for_fun_level=dom_for_fun_level)
1331
1332     if serverrole == "domain controller":
1333         if paths.netlogon is None:
1334             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1335             message("Please either remove %s or see the template at %s" % 
1336                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1337             assert paths.netlogon is not None
1338
1339         if paths.sysvol is None:
1340             message("Existing smb.conf does not have a [sysvol] share, but you"
1341                     " are configuring a DC.")
1342             message("Please either remove %s or see the template at %s" % 
1343                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1344             assert paths.sysvol is not None
1345
1346         if not os.path.isdir(paths.netlogon):
1347             os.makedirs(paths.netlogon, 0755)
1348
1349     if samdb_fill == FILL_FULL:
1350         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1351                             root_uid=root_uid, nobody_uid=nobody_uid,
1352                             users_gid=users_gid, wheel_gid=wheel_gid)
1353
1354         if serverrole == "domain controller":
1355             # Set up group policies (domain policy and domain controller policy)
1356             setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1357             setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, 
1358                          domainsid, names.dnsdomain, names.domaindn, lp)
1359
1360         message("Setting up sam.ldb rootDSE marking as synchronized")
1361         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1362
1363         secretsdb_self_join(secrets_ldb, domain=names.domain,
1364                             realm=names.realm,
1365                             dnsdomain=names.dnsdomain,
1366                             netbiosname=names.netbiosname,
1367                             domainsid=domainsid, 
1368                             machinepass=machinepass,
1369                             secure_channel_type=SEC_CHAN_BDC)
1370
1371         if serverrole == "domain controller":
1372             secretsdb_setup_dns(secrets_ldb, setup_path,
1373                                 paths.private_dir,
1374                                 realm=names.realm, dnsdomain=names.dnsdomain,
1375                                 dns_keytab_path=paths.dns_keytab,
1376                                 dnspass=dnspass)
1377
1378             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1379             assert isinstance(domainguid, str)
1380
1381             # Only make a zone file on the first DC, it should be replicated
1382             # with DNS replication
1383             create_zone_file(lp, message, paths, targetdir, setup_path,
1384                 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1385                 hostname=names.hostname, realm=names.realm, 
1386                 domainguid=domainguid, ntdsguid=names.ntdsguid)
1387
1388             create_named_conf(paths, setup_path, realm=names.realm,
1389                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1390
1391             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1392                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1393                               keytab_name=paths.dns_keytab)
1394             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1395             message("and %s for further documentation required for secure DNS "
1396                     "updates" % paths.namedtxt)
1397
1398             create_krb5_conf(paths.krb5conf, setup_path,
1399                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1400                              realm=names.realm)
1401             message("A Kerberos configuration suitable for Samba 4 has been "
1402                     "generated at %s" % paths.krb5conf)
1403
1404     if serverrole == "domain controller":
1405         create_dns_update_list(lp, message, paths, setup_path)
1406
1407     provision_backend.post_setup()
1408     provision_backend.shutdown()
1409     
1410     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1411                                ldapi_url)
1412
1413     #Now commit the secrets.ldb to disk
1414     secrets_ldb.transaction_commit()
1415
1416     # the commit creates the dns.keytab, now chown it
1417     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1418     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1419         try:
1420             os.chmod(dns_keytab_path, 0640)
1421             os.chown(dns_keytab_path, -1, paths.bind_gid)
1422         except OSError:
1423             message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1424                 paths.bind_gid))
1425
1426
1427     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1428
1429     message("Once the above files are installed, your Samba4 server will be ready to use")
1430     message("Server Role:           %s" % serverrole)
1431     message("Hostname:              %s" % names.hostname)
1432     message("NetBIOS Domain:        %s" % names.domain)
1433     message("DNS Domain:            %s" % names.dnsdomain)
1434     message("DOMAIN SID:            %s" % str(domainsid))
1435     if samdb_fill == FILL_FULL:
1436         message("Admin password:        %s" % adminpass)
1437     if provision_backend.type is not "ldb":
1438         if provision_backend.credentials.get_bind_dn() is not None:
1439             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1440         else:
1441             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1442
1443         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1444
1445         if provision_backend.slapd_command_escaped is not None:
1446             # now display slapd_command_file.txt to show how slapd must be started next time
1447             message("Use later the following commandline to start slapd, then Samba:")
1448             message(provision_backend.slapd_command_escaped)
1449             message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1450
1451
1452     result = ProvisionResult()
1453     result.domaindn = domaindn
1454     result.paths = paths
1455     result.lp = lp
1456     result.samdb = samdb
1457     return result
1458
1459
1460 def provision_become_dc(setup_dir=None,
1461                         smbconf=None, targetdir=None, realm=None, 
1462                         rootdn=None, domaindn=None, schemadn=None,
1463                         configdn=None, serverdn=None,
1464                         domain=None, hostname=None, domainsid=None, 
1465                         adminpass=None, krbtgtpass=None, domainguid=None, 
1466                         policyguid=None, policyguid_dc=None, invocationid=None,
1467                         machinepass=None, 
1468                         dnspass=None, root=None, nobody=None, users=None, 
1469                         wheel=None, backup=None, serverrole=None, 
1470                         ldap_backend=None, ldap_backend_type=None,
1471                         sitename=None, debuglevel=1):
1472
1473     def message(text):
1474         """print a message if quiet is not set."""
1475         print text
1476
1477     samba.set_debug_level(debuglevel)
1478
1479     return provision(setup_dir, message, system_session(), None,
1480               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1481               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1482               configdn=configdn, serverdn=serverdn, domain=domain,
1483               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1484               machinepass=machinepass, serverrole="domain controller",
1485               sitename=sitename)
1486
1487
1488 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1489     """Create a PHP LDAP admin configuration file.
1490
1491     :param path: Path to write the configuration to.
1492     :param setup_path: Function to generate setup paths.
1493     """
1494     setup_file(setup_path("phpldapadmin-config.php"), path, 
1495             {"S4_LDAPI_URI": ldapi_uri})
1496
1497
1498 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1499                      hostip, hostip6, hostname, realm, domainguid,
1500                      ntdsguid):
1501     """Write out a DNS zone file, from the info in the current database.
1502
1503     :param paths: paths object
1504     :param setup_path: Setup path function.
1505     :param dnsdomain: DNS Domain name
1506     :param domaindn: DN of the Domain
1507     :param hostip: Local IPv4 IP
1508     :param hostip6: Local IPv6 IP
1509     :param hostname: Local hostname
1510     :param realm: Realm name
1511     :param domainguid: GUID of the domain.
1512     :param ntdsguid: GUID of the hosts nTDSDSA record.
1513     """
1514     assert isinstance(domainguid, str)
1515
1516     if hostip6 is not None:
1517         hostip6_base_line = "            IN AAAA    " + hostip6
1518         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1519     else:
1520         hostip6_base_line = ""
1521         hostip6_host_line = ""
1522
1523     if hostip is not None:
1524         hostip_base_line = "            IN A    " + hostip
1525         hostip_host_line = hostname + "        IN A    " + hostip
1526     else:
1527         hostip_base_line = ""
1528         hostip_host_line = ""
1529
1530     dns_dir = os.path.dirname(paths.dns)
1531
1532     try:
1533         shutil.rmtree(dns_dir, True)
1534     except OSError:
1535         pass
1536
1537     os.mkdir(dns_dir, 0775)
1538
1539     # we need to freeze the zone while we update the contents
1540     if targetdir is None:
1541         rndc = ' '.join(lp.get("rndc command"))
1542         os.system(rndc + " freeze " + lp.get("realm"))
1543
1544     setup_file(setup_path("provision.zone"), paths.dns, {
1545             "HOSTNAME": hostname,
1546             "DNSDOMAIN": dnsdomain,
1547             "REALM": realm,
1548             "HOSTIP_BASE_LINE": hostip_base_line,
1549             "HOSTIP_HOST_LINE": hostip_host_line,
1550             "DOMAINGUID": domainguid,
1551             "DATESTRING": time.strftime("%Y%m%d%H"),
1552             "DEFAULTSITE": DEFAULTSITE,
1553             "NTDSGUID": ntdsguid,
1554             "HOSTIP6_BASE_LINE": hostip6_base_line,
1555             "HOSTIP6_HOST_LINE": hostip6_host_line,
1556         })
1557
1558     # note that we use no variable substitution on this file
1559     # the substitution is done at runtime by samba_dnsupdate
1560     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1561
1562     # and the SPN update list
1563     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1564
1565     if paths.bind_gid is not None:
1566         try:
1567             os.chown(dns_dir, -1, paths.bind_gid)
1568             os.chown(paths.dns, -1, paths.bind_gid)
1569             # chmod needed to cope with umask
1570             os.chmod(dns_dir, 0775)
1571             os.chmod(paths.dns, 0664)
1572         except OSError:
1573             message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1574
1575     if targetdir is None:
1576         os.system(rndc + " unfreeze " + lp.get("realm"))
1577
1578
1579 def create_dns_update_list(lp, message, paths, setup_path):
1580     """Write out a dns_update_list file"""
1581     # note that we use no variable substitution on this file
1582     # the substitution is done at runtime by samba_dnsupdate
1583     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1584     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1585
1586 def create_named_conf(paths, setup_path, realm, dnsdomain,
1587                       private_dir):
1588     """Write out a file containing zone statements suitable for inclusion in a
1589     named.conf file (including GSS-TSIG configuration).
1590     
1591     :param paths: all paths
1592     :param setup_path: Setup path function.
1593     :param realm: Realm name
1594     :param dnsdomain: DNS Domain name
1595     :param private_dir: Path to private directory
1596     :param keytab_name: File name of DNS keytab file
1597     """
1598
1599     setup_file(setup_path("named.conf"), paths.namedconf, {
1600             "DNSDOMAIN": dnsdomain,
1601             "REALM": realm,
1602             "ZONE_FILE": paths.dns,
1603             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1604             "NAMED_CONF": paths.namedconf,
1605             "NAMED_CONF_UPDATE": paths.namedconf_update
1606             })
1607
1608     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1609
1610 def create_named_txt(path, setup_path, realm, dnsdomain,
1611                       private_dir, keytab_name):
1612     """Write out a file containing zone statements suitable for inclusion in a
1613     named.conf file (including GSS-TSIG configuration).
1614     
1615     :param path: Path of the new named.conf file.
1616     :param setup_path: Setup path function.
1617     :param realm: Realm name
1618     :param dnsdomain: DNS Domain name
1619     :param private_dir: Path to private directory
1620     :param keytab_name: File name of DNS keytab file
1621     """
1622
1623     setup_file(setup_path("named.txt"), path, {
1624             "DNSDOMAIN": dnsdomain,
1625             "REALM": realm,
1626             "DNS_KEYTAB": keytab_name,
1627             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1628             "PRIVATE_DIR": private_dir
1629         })
1630
1631 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1632     """Write out a file containing zone statements suitable for inclusion in a
1633     named.conf file (including GSS-TSIG configuration).
1634     
1635     :param path: Path of the new named.conf file.
1636     :param setup_path: Setup path function.
1637     :param dnsdomain: DNS Domain name
1638     :param hostname: Local hostname
1639     :param realm: Realm name
1640     """
1641     setup_file(setup_path("krb5.conf"), path, {
1642             "DNSDOMAIN": dnsdomain,
1643             "HOSTNAME": hostname,
1644             "REALM": realm,
1645         })
1646
1647
1648 class ProvisioningError(Exception):
1649     """A generic provision error."""
1650
1651     def __init__(self, value):
1652         self.value = value
1653
1654     def __str__(self):
1655         return "ProvisioningError: " + self.value
1656
1657
1658 class InvalidNetbiosName(Exception):
1659     """A specified name was not a valid NetBIOS name."""
1660     def __init__(self, name):
1661         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)