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