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