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