s4:samldb - Major rework
[Samba/aatanasov.git] / source4 / scripting / python / samba / provision.py
blobedce3914655aef21bc35f537d947ee1d28b15061
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
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
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
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.
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.
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/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
29 import os
30 import sys
31 import pwd
32 import grp
33 import time
34 import uuid, glue
35 import socket
36 import param
37 import registry
38 import samba
39 import subprocess
41 import shutil
42 from credentials import Credentials, DONT_USE_KERBEROS
43 from auth import system_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, check_all_substituted, \
45 DS_BEHAVIOR_WIN2008
46 from samba.samdb import SamDB
47 from samba.idmap import IDmapDB
48 from samba.dcerpc import security
49 import urllib
50 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
51 from ms_schema import read_ms_schema
52 from ms_display_specifiers import read_ms_ldif
53 from signal import SIGTERM
55 __docformat__ = "restructuredText"
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.")
74 DEFAULTSITE = "Default-First-Site-Name"
76 class InvalidNetbiosName(Exception):
77 """A specified name was not a valid NetBIOS name."""
78 def __init__(self, name):
79 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
82 class ProvisionPaths(object):
83 def __init__(self):
84 self.shareconf = None
85 self.hklm = None
86 self.hkcu = None
87 self.hkcr = None
88 self.hku = None
89 self.hkpd = None
90 self.hkpt = None
91 self.samdb = None
92 self.idmapdb = None
93 self.secrets = None
94 self.keytab = None
95 self.dns_keytab = None
96 self.dns = None
97 self.winsdb = None
98 self.private_dir = None
99 self.ldapdir = None
100 self.slapdconf = None
101 self.modulesconf = None
102 self.memberofconf = None
103 self.fedoradsinf = None
104 self.fedoradspartitions = None
105 self.olmmron = None
106 self.olmmrserveridsconf = None
107 self.olmmrsyncreplconf = None
108 self.olcdir = None
109 self.olslapd = None
110 self.olcseedldif = None
113 class ProvisionNames(object):
114 def __init__(self):
115 self.rootdn = None
116 self.domaindn = None
117 self.configdn = None
118 self.schemadn = None
119 self.ldapmanagerdn = None
120 self.dnsdomain = None
121 self.realm = None
122 self.netbiosname = None
123 self.domain = None
124 self.hostname = None
125 self.sitename = None
126 self.smbconf = None
129 class ProvisionResult(object):
130 def __init__(self):
131 self.paths = None
132 self.domaindn = None
133 self.lp = None
134 self.samdb = None
136 class Schema(object):
137 def __init__(self, setup_path, schemadn=None,
138 serverdn=None):
139 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
141 :param samdb: Load a schema into a SamDB.
142 :param setup_path: Setup path function.
143 :param schemadn: DN of the schema
144 :param serverdn: DN of the server
146 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
149 self.ldb = Ldb()
150 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
151 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
152 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
153 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
154 check_all_substituted(self.schema_data)
156 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
157 {"SCHEMADN": schemadn,
158 "SERVERDN": serverdn,
160 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
161 {"SCHEMADN": schemadn
164 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
165 prefixmap = b64encode(prefixmap)
167 # We don't actually add this ldif, just parse it
168 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
169 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
172 # Return a hash with the forward attribute as a key and the back as the value
173 def get_linked_attributes(schemadn,schemaldb):
174 attrs = ["linkID", "lDAPDisplayName"]
175 res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
176 attributes = {}
177 for i in range (0, len(res)):
178 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
179 target = schemaldb.searchone(basedn=schemadn,
180 expression=expression,
181 attribute="lDAPDisplayName",
182 scope=SCOPE_SUBTREE)
183 if target is not None:
184 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
186 return attributes
188 def get_dnsyntax_attributes(schemadn,schemaldb):
189 attrs = ["linkID", "lDAPDisplayName"]
190 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
191 attributes = []
192 for i in range (0, len(res)):
193 attributes.append(str(res[i]["lDAPDisplayName"]))
195 return attributes
198 def check_install(lp, session_info, credentials):
199 """Check whether the current install seems ok.
201 :param lp: Loadparm context
202 :param session_info: Session information
203 :param credentials: Credentials
205 if lp.get("realm") == "":
206 raise Exception("Realm empty")
207 ldb = Ldb(lp.get("sam database"), session_info=session_info,
208 credentials=credentials, lp=lp)
209 if len(ldb.search("(cn=Administrator)")) != 1:
210 raise "No administrator account found"
213 def findnss(nssfn, names):
214 """Find a user or group from a list of possibilities.
216 :param nssfn: NSS Function to try (should raise KeyError if not found)
217 :param names: Names to check.
218 :return: Value return by first names list.
220 for name in names:
221 try:
222 return nssfn(name)
223 except KeyError:
224 pass
225 raise KeyError("Unable to find user/group %r" % names)
228 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
229 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
232 def read_and_sub_file(file, subst_vars):
233 """Read a file and sub in variables found in it
235 :param file: File to be read (typically from setup directory)
236 param subst_vars: Optional variables to subsitute in the file.
238 data = open(file, 'r').read()
239 if subst_vars is not None:
240 data = substitute_var(data, subst_vars)
241 check_all_substituted(data)
242 return data
245 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
246 """Setup a ldb in the private dir.
248 :param ldb: LDB file to import data into
249 :param ldif_path: Path of the LDIF file to load
250 :param subst_vars: Optional variables to subsitute in LDIF.
252 assert isinstance(ldif_path, str)
254 data = read_and_sub_file(ldif_path, subst_vars)
255 ldb.add_ldif(data)
258 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
259 """Modify a ldb in the private dir.
261 :param ldb: LDB object.
262 :param ldif_path: LDIF file path.
263 :param subst_vars: Optional dictionary with substitution variables.
265 data = read_and_sub_file(ldif_path, subst_vars)
267 ldb.modify_ldif(data)
270 def setup_ldb(ldb, ldif_path, subst_vars):
271 """Import a LDIF a file into a LDB handle, optionally substituting variables.
273 :note: Either all LDIF data will be added or none (using transactions).
275 :param ldb: LDB file to import into.
276 :param ldif_path: Path to the LDIF file.
277 :param subst_vars: Dictionary with substitution variables.
279 assert ldb is not None
280 ldb.transaction_start()
281 try:
282 setup_add_ldif(ldb, ldif_path, subst_vars)
283 except:
284 ldb.transaction_cancel()
285 raise
286 ldb.transaction_commit()
289 def setup_file(template, fname, subst_vars):
290 """Setup a file in the private dir.
292 :param template: Path of the template file.
293 :param fname: Path of the file to create.
294 :param subst_vars: Substitution variables.
296 f = fname
298 if os.path.exists(f):
299 os.unlink(f)
301 data = read_and_sub_file(template, subst_vars)
302 open(f, 'w').write(data)
305 def provision_paths_from_lp(lp, dnsdomain):
306 """Set the default paths for provisioning.
308 :param lp: Loadparm context.
309 :param dnsdomain: DNS Domain name
311 paths = ProvisionPaths()
312 paths.private_dir = lp.get("private dir")
313 paths.keytab = "secrets.keytab"
314 paths.dns_keytab = "dns.keytab"
316 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
317 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
318 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
319 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
320 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
321 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
322 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
323 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
324 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
325 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
326 paths.phpldapadminconfig = os.path.join(paths.private_dir,
327 "phpldapadmin-config.php")
328 paths.ldapdir = os.path.join(paths.private_dir,
329 "ldap")
330 paths.slapdconf = os.path.join(paths.ldapdir,
331 "slapd.conf")
332 paths.slapdpid = os.path.join(paths.ldapdir,
333 "slapd.pid")
334 paths.modulesconf = os.path.join(paths.ldapdir,
335 "modules.conf")
336 paths.memberofconf = os.path.join(paths.ldapdir,
337 "memberof.conf")
338 paths.fedoradsinf = os.path.join(paths.ldapdir,
339 "fedorads.inf")
340 paths.fedoradspartitions = os.path.join(paths.ldapdir,
341 "fedorads-partitions.ldif")
342 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
343 "mmr_serverids.conf")
344 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
345 "mmr_syncrepl.conf")
346 paths.olcdir = os.path.join(paths.ldapdir,
347 "slapd.d")
348 paths.olcseedldif = os.path.join(paths.ldapdir,
349 "olc_seed.ldif")
350 paths.hklm = "hklm.ldb"
351 paths.hkcr = "hkcr.ldb"
352 paths.hkcu = "hkcu.ldb"
353 paths.hku = "hku.ldb"
354 paths.hkpd = "hkpd.ldb"
355 paths.hkpt = "hkpt.ldb"
357 paths.sysvol = lp.get("path", "sysvol")
359 paths.netlogon = lp.get("path", "netlogon")
361 paths.smbconf = lp.configfile
363 return paths
366 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
367 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
368 sitename=None):
369 """Guess configuration settings to use."""
371 if hostname is None:
372 hostname = socket.gethostname().split(".")[0].lower()
374 netbiosname = hostname.upper()
375 if not valid_netbios_name(netbiosname):
376 raise InvalidNetbiosName(netbiosname)
378 hostname = hostname.lower()
380 if dnsdomain is None:
381 dnsdomain = lp.get("realm")
383 if serverrole is None:
384 serverrole = lp.get("server role")
386 assert dnsdomain is not None
387 realm = dnsdomain.upper()
389 if lp.get("realm").upper() != realm:
390 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
391 (lp.get("realm"), lp.configfile, realm))
393 dnsdomain = dnsdomain.lower()
395 if serverrole == "domain controller":
396 if domain is None:
397 domain = lp.get("workgroup")
398 if domaindn is None:
399 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
400 if lp.get("workgroup").upper() != domain.upper():
401 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
402 lp.get("workgroup"), domain)
403 else:
404 domain = netbiosname
405 if domaindn is None:
406 domaindn = "CN=" + netbiosname
408 assert domain is not None
409 domain = domain.upper()
410 if not valid_netbios_name(domain):
411 raise InvalidNetbiosName(domain)
413 if rootdn is None:
414 rootdn = domaindn
416 if configdn is None:
417 configdn = "CN=Configuration," + rootdn
418 if schemadn is None:
419 schemadn = "CN=Schema," + configdn
421 if sitename is None:
422 sitename=DEFAULTSITE
424 names = ProvisionNames()
425 names.rootdn = rootdn
426 names.domaindn = domaindn
427 names.configdn = configdn
428 names.schemadn = schemadn
429 names.ldapmanagerdn = "CN=Manager," + rootdn
430 names.dnsdomain = dnsdomain
431 names.domain = domain
432 names.realm = realm
433 names.netbiosname = netbiosname
434 names.hostname = hostname
435 names.sitename = sitename
436 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
438 return names
441 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
442 targetdir):
443 """Create a new smb.conf file based on a couple of basic settings.
445 assert smbconf is not None
446 if hostname is None:
447 hostname = socket.gethostname().split(".")[0].lower()
449 if serverrole is None:
450 serverrole = "standalone"
452 assert serverrole in ("domain controller", "member server", "standalone")
453 if serverrole == "domain controller":
454 smbconfsuffix = "dc"
455 elif serverrole == "member server":
456 smbconfsuffix = "member"
457 elif serverrole == "standalone":
458 smbconfsuffix = "standalone"
460 assert domain is not None
461 assert realm is not None
463 default_lp = param.LoadParm()
464 #Load non-existant file
465 if os.path.exists(smbconf):
466 default_lp.load(smbconf)
468 if targetdir is not None:
469 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
470 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
472 default_lp.set("lock dir", os.path.abspath(targetdir))
473 else:
474 privatedir_line = ""
475 lockdir_line = ""
477 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
478 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
480 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
481 smbconf, {
482 "HOSTNAME": hostname,
483 "DOMAIN": domain,
484 "REALM": realm,
485 "SERVERROLE": serverrole,
486 "NETLOGONPATH": netlogon,
487 "SYSVOLPATH": sysvol,
488 "PRIVATEDIR_LINE": privatedir_line,
489 "LOCKDIR_LINE": lockdir_line
493 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
494 users_gid, wheel_gid):
495 """setup reasonable name mappings for sam names to unix names.
497 :param samdb: SamDB object.
498 :param idmap: IDmap db object.
499 :param sid: The domain sid.
500 :param domaindn: The domain DN.
501 :param root_uid: uid of the UNIX root user.
502 :param nobody_uid: uid of the UNIX nobody user.
503 :param users_gid: gid of the UNIX users group.
504 :param wheel_gid: gid of the UNIX wheel group."""
506 def add_foreign(self, domaindn, sid, desc):
507 """Add a foreign security principle."""
508 add = """
509 dn: CN=%s,CN=ForeignSecurityPrincipals,%s
510 objectClass: top
511 objectClass: foreignSecurityPrincipal
512 description: %s
513 """ % (sid, domaindn, desc)
514 # deliberately ignore errors from this, as the records may
515 # already exist
516 for msg in self.parse_ldif(add):
517 self.add(msg[1])
519 add_foreign(samdb, domaindn, "S-1-5-7", "Anonymous")
520 add_foreign(samdb, domaindn, "S-1-1-0", "World")
521 add_foreign(samdb, domaindn, "S-1-5-2", "Network")
522 add_foreign(samdb, domaindn, "S-1-5-18", "System")
523 add_foreign(samdb, domaindn, "S-1-5-11", "Authenticated Users")
525 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
526 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
528 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
529 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
531 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
532 credentials, names,
533 serverrole, ldap_backend=None,
534 erase=False):
535 """Setup the partitions for the SAM database.
537 Alternatively, provision() may call this, and then populate the database.
539 :note: This will wipe the Sam Database!
541 :note: This function always removes the local SAM LDB file. The erase
542 parameter controls whether to erase the existing data, which
543 may not be stored locally but in LDAP.
545 assert session_info is not None
547 # We use options=["modules:"] to stop the modules loading - we
548 # just want to wipe and re-initialise the database, not start it up
550 try:
551 samdb = Ldb(url=samdb_path, session_info=session_info,
552 credentials=credentials, lp=lp, options=["modules:"])
553 # Wipes the database
554 samdb.erase_except_schema_controlled()
555 except LdbError:
556 os.unlink(samdb_path)
557 samdb = Ldb(url=samdb_path, session_info=session_info,
558 credentials=credentials, lp=lp, options=["modules:"])
559 # Wipes the database
560 samdb.erase_except_schema_controlled()
563 #Add modules to the list to activate them by default
564 #beware often order is important
566 # Some Known ordering constraints:
567 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
568 # - objectclass must be before password_hash, because password_hash checks
569 # that the objectclass is of type person (filled in by objectclass
570 # module when expanding the objectclass list)
571 # - partition must be last
572 # - each partition has its own module list then
573 modules_list = ["rootdse",
574 "paged_results",
575 "ranged_results",
576 "anr",
577 "server_sort",
578 "asq",
579 "extended_dn_store",
580 "extended_dn_in",
581 "rdn_name",
582 "objectclass",
583 "samldb",
584 "kludge_acl",
585 "password_hash",
586 "operational"]
587 tdb_modules_list = [
588 "subtree_rename",
589 "subtree_delete",
590 "linked_attributes",
591 "extended_dn_out_ldb"]
592 modules_list2 = ["show_deleted",
593 "partition"]
595 domaindn_ldb = "users.ldb"
596 configdn_ldb = "configuration.ldb"
597 schemadn_ldb = "schema.ldb"
598 if ldap_backend is not None:
599 domaindn_ldb = ldap_backend.ldapi_uri
600 configdn_ldb = ldap_backend.ldapi_uri
601 schemadn_ldb = ldap_backend.ldapi_uri
603 if ldap_backend.ldap_backend_type == "fedora-ds":
604 backend_modules = ["nsuniqueid", "paged_searches"]
605 # We can handle linked attributes here, as we don't have directory-side subtree operations
606 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
607 elif ldap_backend.ldap_backend_type == "openldap":
608 backend_modules = ["entryuuid", "paged_searches"]
609 # OpenLDAP handles subtree renames, so we don't want to do any of these things
610 tdb_modules_list = ["extended_dn_out_dereference"]
612 elif serverrole == "domain controller":
613 tdb_modules_list.insert(0, "repl_meta_data")
614 backend_modules = []
615 else:
616 backend_modules = ["objectguid"]
618 if tdb_modules_list is None:
619 tdb_modules_list_as_string = ""
620 else:
621 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
623 samdb.transaction_start()
624 try:
625 message("Setting up sam.ldb partitions and settings")
626 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
627 "SCHEMADN": names.schemadn,
628 "SCHEMADN_LDB": schemadn_ldb,
629 "SCHEMADN_MOD2": ",objectguid",
630 "CONFIGDN": names.configdn,
631 "CONFIGDN_LDB": configdn_ldb,
632 "DOMAINDN": names.domaindn,
633 "DOMAINDN_LDB": domaindn_ldb,
634 "SCHEMADN_MOD": "schema_fsmo,instancetype",
635 "CONFIGDN_MOD": "naming_fsmo,instancetype",
636 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
637 "MODULES_LIST": ",".join(modules_list),
638 "TDB_MODULES_LIST": tdb_modules_list_as_string,
639 "MODULES_LIST2": ",".join(modules_list2),
640 "BACKEND_MOD": ",".join(backend_modules),
643 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
645 message("Setting up sam.ldb rootDSE")
646 setup_samdb_rootdse(samdb, setup_path, names)
648 except:
649 samdb.transaction_cancel()
650 raise
652 samdb.transaction_commit()
656 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
657 netbiosname, domainsid, keytab_path, samdb_url,
658 dns_keytab_path, dnspass, machinepass):
659 """Add DC-specific bits to a secrets database.
661 :param secretsdb: Ldb Handle to the secrets database
662 :param setup_path: Setup path function
663 :param machinepass: Machine password
665 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
666 "MACHINEPASS_B64": b64encode(machinepass),
667 "DOMAIN": domain,
668 "REALM": realm,
669 "DNSDOMAIN": dnsdomain,
670 "DOMAINSID": str(domainsid),
671 "SECRETS_KEYTAB": keytab_path,
672 "NETBIOSNAME": netbiosname,
673 "SAM_LDB": samdb_url,
674 "DNS_KEYTAB": dns_keytab_path,
675 "DNSPASS_B64": b64encode(dnspass),
679 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
680 """Setup the secrets database.
682 :param path: Path to the secrets database.
683 :param setup_path: Get the path to a setup file.
684 :param session_info: Session info.
685 :param credentials: Credentials
686 :param lp: Loadparm context
687 :return: LDB handle for the created secrets database
689 if os.path.exists(path):
690 os.unlink(path)
691 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
692 lp=lp)
693 secrets_ldb.erase()
694 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
695 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
696 lp=lp)
697 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
699 if credentials is not None and credentials.authentication_requested():
700 if credentials.get_bind_dn() is not None:
701 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
702 "LDAPMANAGERDN": credentials.get_bind_dn(),
703 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
705 else:
706 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
707 "LDAPADMINUSER": credentials.get_username(),
708 "LDAPADMINREALM": credentials.get_realm(),
709 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
712 return secrets_ldb
714 def setup_registry(path, setup_path, session_info, lp):
715 """Setup the registry.
717 :param path: Path to the registry database
718 :param setup_path: Function that returns the path to a setup.
719 :param session_info: Session information
720 :param credentials: Credentials
721 :param lp: Loadparm context
723 reg = registry.Registry()
724 hive = registry.open_ldb(path, session_info=session_info,
725 lp_ctx=lp)
726 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
727 provision_reg = setup_path("provision.reg")
728 assert os.path.exists(provision_reg)
729 reg.diff_apply(provision_reg)
732 def setup_idmapdb(path, setup_path, session_info, lp):
733 """Setup the idmap database.
735 :param path: path to the idmap database
736 :param setup_path: Function that returns a path to a setup file
737 :param session_info: Session information
738 :param credentials: Credentials
739 :param lp: Loadparm context
741 if os.path.exists(path):
742 os.unlink(path)
744 idmap_ldb = IDmapDB(path, session_info=session_info,
745 lp=lp)
747 idmap_ldb.erase()
748 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
749 return idmap_ldb
752 def setup_samdb_rootdse(samdb, setup_path, names):
753 """Setup the SamDB rootdse.
755 :param samdb: Sam Database handle
756 :param setup_path: Obtain setup path
758 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
759 "SCHEMADN": names.schemadn,
760 "NETBIOSNAME": names.netbiosname,
761 "DNSDOMAIN": names.dnsdomain,
762 "REALM": names.realm,
763 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
764 "DOMAINDN": names.domaindn,
765 "ROOTDN": names.rootdn,
766 "CONFIGDN": names.configdn,
767 "SERVERDN": names.serverdn,
771 def setup_self_join(samdb, names,
772 machinepass, dnspass,
773 domainsid, invocationid, setup_path,
774 policyguid, domainControllerFunctionality):
775 """Join a host to its own domain."""
776 assert isinstance(invocationid, str)
777 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
778 "CONFIGDN": names.configdn,
779 "SCHEMADN": names.schemadn,
780 "DOMAINDN": names.domaindn,
781 "SERVERDN": names.serverdn,
782 "INVOCATIONID": invocationid,
783 "NETBIOSNAME": names.netbiosname,
784 "DEFAULTSITE": names.sitename,
785 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
786 "MACHINEPASS_B64": b64encode(machinepass),
787 "DNSPASS_B64": b64encode(dnspass),
788 "REALM": names.realm,
789 "DOMAIN": names.domain,
790 "DNSDOMAIN": names.dnsdomain,
791 "SAMBA_VERSION_STRING": version,
792 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
794 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
795 "POLICYGUID": policyguid,
796 "DNSDOMAIN": names.dnsdomain,
797 "DOMAINSID": str(domainsid),
798 "DOMAINDN": names.domaindn})
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 "DOMAINDN": names.domaindn,
803 "CONFIGDN": names.configdn,
804 "SCHEMADN": names.schemadn,
805 "DEFAULTSITE": names.sitename,
806 "SERVERDN": names.serverdn
810 def setup_samdb(path, setup_path, session_info, credentials, lp,
811 names, message,
812 domainsid, domainguid, policyguid,
813 fill, adminpass, krbtgtpass,
814 machinepass, invocationid, dnspass,
815 serverrole, schema=None, ldap_backend=None):
816 """Setup a complete SAM Database.
818 :note: This will wipe the main SAM database file!
821 domainFunctionality = DS_BEHAVIOR_WIN2008
822 forestFunctionality = DS_BEHAVIOR_WIN2008
823 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
825 # Also wipes the database
826 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
827 credentials=credentials, session_info=session_info,
828 names=names,
829 ldap_backend=ldap_backend, serverrole=serverrole)
831 if (schema == None):
832 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
834 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
835 samdb = Ldb(session_info=session_info,
836 credentials=credentials, lp=lp)
838 message("Pre-loading the Samba 4 and AD schema")
840 # Load the schema from the one we computed earlier
841 samdb.set_schema_from_ldb(schema.ldb)
843 # And now we can connect to the DB - the schema won't be loaded from the DB
844 samdb.connect(path)
846 # Load @OPTIONS
847 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
849 if fill == FILL_DRS:
850 return samdb
852 samdb.transaction_start()
853 try:
854 message("Erasing data from partitions")
855 # Load the schema (again). This time it will force a reindex,
856 # and will therefore make the erase_partitions() below
857 # computationally sane
858 samdb.set_schema_from_ldb(schema.ldb)
859 samdb.erase_partitions()
861 # Set the domain functionality levels onto the database.
862 # Various module (the password_hash module in particular) need
863 # to know what level of AD we are emulating.
865 # These will be fixed into the database via the database
866 # modifictions below, but we need them set from the start.
867 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
868 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
869 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
871 samdb.set_domain_sid(str(domainsid))
872 if serverrole == "domain controller":
873 samdb.set_invocation_id(invocationid)
875 message("Adding DomainDN: %s" % names.domaindn)
876 if serverrole == "domain controller":
877 domain_oc = "domainDNS"
878 else:
879 domain_oc = "samba4LocalDomain"
881 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
882 "DOMAINDN": names.domaindn,
883 "DOMAIN_OC": domain_oc
886 message("Modifying DomainDN: " + names.domaindn + "")
887 if domainguid is not None:
888 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
889 else:
890 domainguid_mod = ""
892 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
893 "LDAPTIME": timestring(int(time.time())),
894 "DOMAINSID": str(domainsid),
895 "SCHEMADN": names.schemadn,
896 "NETBIOSNAME": names.netbiosname,
897 "DEFAULTSITE": names.sitename,
898 "CONFIGDN": names.configdn,
899 "SERVERDN": names.serverdn,
900 "POLICYGUID": policyguid,
901 "DOMAINDN": names.domaindn,
902 "DOMAINGUID_MOD": domainguid_mod,
903 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
906 message("Adding configuration container")
907 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
908 "CONFIGDN": names.configdn,
910 message("Modifying configuration container")
911 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
912 "CONFIGDN": names.configdn,
913 "SCHEMADN": names.schemadn,
916 # The LDIF here was created when the Schema object was constructed
917 message("Setting up sam.ldb schema")
918 samdb.add_ldif(schema.schema_dn_add)
919 samdb.modify_ldif(schema.schema_dn_modify)
920 samdb.write_prefixes_from_schema()
921 samdb.add_ldif(schema.schema_data)
922 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
923 {"SCHEMADN": names.schemadn})
925 message("Setting up sam.ldb configuration data")
926 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
927 "CONFIGDN": names.configdn,
928 "NETBIOSNAME": names.netbiosname,
929 "DEFAULTSITE": names.sitename,
930 "DNSDOMAIN": names.dnsdomain,
931 "DOMAIN": names.domain,
932 "SCHEMADN": names.schemadn,
933 "DOMAINDN": names.domaindn,
934 "SERVERDN": names.serverdn,
935 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
938 message("Setting up display specifiers")
939 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
940 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
941 check_all_substituted(display_specifiers_ldif)
942 samdb.add_ldif(display_specifiers_ldif)
944 message("Adding users container")
945 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
946 "DOMAINDN": names.domaindn})
947 message("Modifying users container")
948 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
949 "DOMAINDN": names.domaindn})
950 message("Adding computers container")
951 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
952 "DOMAINDN": names.domaindn})
953 message("Modifying computers container")
954 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
955 "DOMAINDN": names.domaindn})
956 message("Setting up sam.ldb data")
957 setup_add_ldif(samdb, setup_path("provision.ldif"), {
958 "DOMAINDN": names.domaindn,
959 "NETBIOSNAME": names.netbiosname,
960 "DEFAULTSITE": names.sitename,
961 "CONFIGDN": names.configdn,
962 "SERVERDN": names.serverdn
965 if fill == FILL_FULL:
966 message("Setting up sam.ldb users and groups")
967 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
968 "DOMAINDN": names.domaindn,
969 "DOMAINSID": str(domainsid),
970 "CONFIGDN": names.configdn,
971 "ADMINPASS_B64": b64encode(adminpass),
972 "KRBTGTPASS_B64": b64encode(krbtgtpass),
975 if serverrole == "domain controller":
976 message("Setting up self join")
977 setup_self_join(samdb, names=names, invocationid=invocationid,
978 dnspass=dnspass,
979 machinepass=machinepass,
980 domainsid=domainsid, policyguid=policyguid,
981 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
983 except:
984 samdb.transaction_cancel()
985 raise
987 samdb.transaction_commit()
988 return samdb
991 FILL_FULL = "FULL"
992 FILL_NT4SYNC = "NT4SYNC"
993 FILL_DRS = "DRS"
996 def provision(setup_dir, message, session_info,
997 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
998 rootdn=None, domaindn=None, schemadn=None, configdn=None,
999 serverdn=None,
1000 domain=None, hostname=None, hostip=None, hostip6=None,
1001 domainsid=None, adminpass=None, ldapadminpass=None,
1002 krbtgtpass=None, domainguid=None,
1003 policyguid=None, invocationid=None, machinepass=None,
1004 dnspass=None, root=None, nobody=None, users=None,
1005 wheel=None, backup=None, aci=None, serverrole=None,
1006 ldap_backend_extra_port=None, ldap_backend_type=None, sitename=None,
1007 ol_mmr_urls=None, ol_olc=None,
1008 setup_ds_path=None, slapd_path=None, nosync=False,
1009 ldap_dryrun_mode=False):
1010 """Provision samba4
1012 :note: caution, this wipes all existing data!
1015 def setup_path(file):
1016 return os.path.join(setup_dir, file)
1018 if domainsid is None:
1019 domainsid = security.random_sid()
1021 if policyguid is None:
1022 policyguid = str(uuid.uuid4())
1023 if adminpass is None:
1024 adminpass = glue.generate_random_str(12)
1025 if krbtgtpass is None:
1026 krbtgtpass = glue.generate_random_str(12)
1027 if machinepass is None:
1028 machinepass = glue.generate_random_str(12)
1029 if dnspass is None:
1030 dnspass = glue.generate_random_str(12)
1031 if ldapadminpass is None:
1032 #Make a new, random password between Samba and it's LDAP server
1033 ldapadminpass=glue.generate_random_str(12)
1036 root_uid = findnss_uid([root or "root"])
1037 nobody_uid = findnss_uid([nobody or "nobody"])
1038 users_gid = findnss_gid([users or "users"])
1039 if wheel is None:
1040 wheel_gid = findnss_gid(["wheel", "adm"])
1041 else:
1042 wheel_gid = findnss_gid([wheel])
1044 if targetdir is not None:
1045 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1046 os.makedirs(os.path.join(targetdir, "etc"))
1047 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1048 elif smbconf is None:
1049 smbconf = param.default_path()
1051 # only install a new smb.conf if there isn't one there already
1052 if not os.path.exists(smbconf):
1053 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1054 targetdir)
1056 lp = param.LoadParm()
1057 lp.load(smbconf)
1059 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1060 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1061 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1062 serverdn=serverdn)
1064 paths = provision_paths_from_lp(lp, names.dnsdomain)
1066 if hostip is None:
1067 try:
1068 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1069 except socket.gaierror, (socket.EAI_NODATA, msg):
1070 hostip = None
1072 if hostip6 is None:
1073 try:
1074 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1075 except socket.gaierror, (socket.EAI_NODATA, msg):
1076 hostip6 = None
1078 if serverrole is None:
1079 serverrole = lp.get("server role")
1081 assert serverrole in ("domain controller", "member server", "standalone")
1082 if invocationid is None and serverrole == "domain controller":
1083 invocationid = str(uuid.uuid4())
1085 if not os.path.exists(paths.private_dir):
1086 os.mkdir(paths.private_dir)
1088 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1090 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1092 provision_backend = None
1093 if ldap_backend_type:
1094 # We only support an LDAP backend over ldapi://
1096 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, lp=lp, credentials=credentials,
1097 names=names,
1098 message=message, hostname=hostname,
1099 root=root, schema=schema, ldap_backend_type=ldap_backend_type,
1100 ldapadminpass=ldapadminpass,
1101 ldap_backend_extra_port=ldap_backend_extra_port,
1102 ol_mmr_urls=ol_mmr_urls,
1103 slapd_path=slapd_path,
1104 setup_ds_path=setup_ds_path,
1105 ldap_dryrun_mode=ldap_dryrun_mode)
1107 # Now use the backend credentials to access the databases
1108 credentials = provision_backend.credentials
1110 # only install a new shares config db if there is none
1111 if not os.path.exists(paths.shareconf):
1112 message("Setting up share.ldb")
1113 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1114 credentials=credentials, lp=lp)
1115 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1118 message("Setting up secrets.ldb")
1119 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1120 session_info=session_info,
1121 credentials=credentials, lp=lp)
1123 message("Setting up the registry")
1124 setup_registry(paths.hklm, setup_path, session_info,
1125 lp=lp)
1127 message("Setting up idmap db")
1128 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1129 lp=lp)
1131 message("Setting up SAM db")
1132 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1133 credentials=credentials, lp=lp, names=names,
1134 message=message,
1135 domainsid=domainsid,
1136 schema=schema, domainguid=domainguid, policyguid=policyguid,
1137 fill=samdb_fill,
1138 adminpass=adminpass, krbtgtpass=krbtgtpass,
1139 invocationid=invocationid,
1140 machinepass=machinepass, dnspass=dnspass,
1141 serverrole=serverrole, ldap_backend=provision_backend)
1143 if serverrole == "domain controller":
1144 if paths.netlogon is None:
1145 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1146 message("Please either remove %s or see the template at %s" %
1147 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1148 assert(paths.netlogon is not None)
1150 if paths.sysvol is None:
1151 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1152 message("Please either remove %s or see the template at %s" %
1153 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1154 assert(paths.sysvol is not None)
1156 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1157 "{" + policyguid + "}")
1158 os.makedirs(policy_path, 0755)
1159 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1160 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1161 os.makedirs(os.path.join(policy_path, "User"), 0755)
1162 if not os.path.isdir(paths.netlogon):
1163 os.makedirs(paths.netlogon, 0755)
1165 if samdb_fill == FILL_FULL:
1166 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1167 root_uid=root_uid, nobody_uid=nobody_uid,
1168 users_gid=users_gid, wheel_gid=wheel_gid)
1170 message("Setting up sam.ldb rootDSE marking as synchronized")
1171 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1173 # Only make a zone file on the first DC, it should be replicated with DNS replication
1174 if serverrole == "domain controller":
1175 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1176 credentials=credentials, lp=lp)
1177 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1178 netbiosname=names.netbiosname, domainsid=domainsid,
1179 keytab_path=paths.keytab, samdb_url=paths.samdb,
1180 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1181 machinepass=machinepass, dnsdomain=names.dnsdomain)
1183 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1184 assert isinstance(domainguid, str)
1185 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1186 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1187 scope=SCOPE_SUBTREE)
1188 assert isinstance(hostguid, str)
1190 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1191 domaindn=names.domaindn, hostip=hostip,
1192 hostip6=hostip6, hostname=names.hostname,
1193 dnspass=dnspass, realm=names.realm,
1194 domainguid=domainguid, hostguid=hostguid)
1196 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1197 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1199 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1200 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1201 keytab_name=paths.dns_keytab)
1202 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1203 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1205 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1206 hostname=names.hostname, realm=names.realm)
1207 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1210 # if backend is openldap, terminate slapd after final provision and check its proper termination
1211 if provision_backend is not None and provision_backend.slapd is not None:
1212 if provision_backend.slapd.poll() is None:
1213 #Kill the slapd
1214 if hasattr(provision_backend.slapd, "terminate"):
1215 provision_backend.slapd.terminate()
1216 else:
1217 import signal
1218 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1220 #and now wait for it to die
1221 provision_backend.slapd.communicate()
1223 # now display slapd_command_file.txt to show how slapd must be started next time
1224 message("Use later the following commandline to start slapd, then Samba:")
1225 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1226 message(slapd_command)
1227 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1229 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1230 "SLAPD_COMMAND" : slapd_command})
1233 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1234 ldapi_url)
1236 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1238 message("Once the above files are installed, your Samba4 server will be ready to use")
1239 message("Server Role: %s" % serverrole)
1240 message("Hostname: %s" % names.hostname)
1241 message("NetBIOS Domain: %s" % names.domain)
1242 message("DNS Domain: %s" % names.dnsdomain)
1243 message("DOMAIN SID: %s" % str(domainsid))
1244 if samdb_fill == FILL_FULL:
1245 message("Admin password: %s" % adminpass)
1246 if provision_backend:
1247 if provision_backend.credentials.get_bind_dn() is not None:
1248 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1249 else:
1250 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1252 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1254 result = ProvisionResult()
1255 result.domaindn = domaindn
1256 result.paths = paths
1257 result.lp = lp
1258 result.samdb = samdb
1259 return result
1263 def provision_become_dc(setup_dir=None,
1264 smbconf=None, targetdir=None, realm=None,
1265 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1266 serverdn=None,
1267 domain=None, hostname=None, domainsid=None,
1268 adminpass=None, krbtgtpass=None, domainguid=None,
1269 policyguid=None, invocationid=None, machinepass=None,
1270 dnspass=None, root=None, nobody=None, users=None,
1271 wheel=None, backup=None, serverrole=None,
1272 ldap_backend=None, ldap_backend_type=None, sitename=None, debuglevel=1):
1274 def message(text):
1275 """print a message if quiet is not set."""
1276 print text
1278 glue.set_debug_level(debuglevel)
1280 return provision(setup_dir, message, system_session(), None,
1281 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1282 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1283 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1286 def setup_db_config(setup_path, dbdir):
1287 """Setup a Berkeley database.
1289 :param setup_path: Setup path function.
1290 :param dbdir: Database directory."""
1291 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1292 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1293 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1294 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1296 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1297 {"LDAPDBDIR": dbdir})
1299 class ProvisionBackend(object):
1300 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1301 names=None, message=None,
1302 hostname=None, root=None,
1303 schema=None, ldapadminpass=None,
1304 ldap_backend_type=None, ldap_backend_extra_port=None,
1305 ol_mmr_urls=None,
1306 setup_ds_path=None, slapd_path=None,
1307 nosync=False, ldap_dryrun_mode=False):
1308 """Provision an LDAP backend for samba4
1310 This works for OpenLDAP and Fedora DS
1313 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1315 if not os.path.isdir(paths.ldapdir):
1316 os.makedirs(paths.ldapdir, 0700)
1318 if ldap_backend_type == "existing":
1319 #Check to see that this 'existing' LDAP backend in fact exists
1320 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1321 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1322 expression="(objectClass=OpenLDAProotDSE)")
1324 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1325 # This caused them to be set into the long-term database later in the script.
1326 self.credentials = credentials
1327 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1328 return
1330 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1331 # if another instance of slapd is already running
1332 try:
1333 ldapi_db = Ldb(self.ldapi_uri)
1334 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1335 expression="(objectClass=OpenLDAProotDSE)");
1336 try:
1337 f = open(paths.slapdpid, "r")
1338 p = f.read()
1339 f.close()
1340 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1341 except:
1342 pass
1344 raise("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1346 except LdbError, e:
1347 pass
1349 # Try to print helpful messages when the user has not specified the path to slapd
1350 if slapd_path is None:
1351 raise("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1352 if not os.path.exists(slapd_path):
1353 message (slapd_path)
1354 raise("Warning: Given Path to slapd does not exist!")
1356 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1357 try:
1358 os.unlink(schemadb_path)
1359 except OSError:
1360 pass
1363 # Put the LDIF of the schema into a database so we can search on
1364 # it to generate schema-dependent configurations in Fedora DS and
1365 # OpenLDAP
1366 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1367 schema.ldb.connect(schemadb_path)
1368 schema.ldb.transaction_start()
1370 # These bits of LDIF are supplied when the Schema object is created
1371 schema.ldb.add_ldif(schema.schema_dn_add)
1372 schema.ldb.modify_ldif(schema.schema_dn_modify)
1373 schema.ldb.add_ldif(schema.schema_data)
1374 schema.ldb.transaction_commit()
1376 self.credentials = Credentials()
1377 self.credentials.guess(lp)
1378 #Kerberos to an ldapi:// backend makes no sense
1379 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1380 self.ldap_backend_type = ldap_backend_type
1382 if ldap_backend_type == "fedora-ds":
1383 provision_fds_backend(self, paths=paths, setup_path=setup_path, names=names, message=message,
1384 hostname=hostname, ldapadminpass=ldapadminpass, root=root,
1385 schema=schema, ldap_backend_extra_port=ldap_backend_extra_port,
1386 setup_ds_path=setup_ds_path, slapd_path=slapd_path,
1387 nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1389 elif ldap_backend_type == "openldap":
1390 provision_openldap_backend(self, paths=paths, setup_path=setup_path, names=names, message=message,
1391 hostname=hostname, ldapadminpass=ldapadminpass, root=root,
1392 schema=schema, ldap_backend_extra_port=ldap_backend_extra_port,
1393 ol_mmr_urls=ol_mmr_urls,
1394 slapd_path=slapd_path,
1395 nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1396 else:
1397 raise("Unknown LDAP backend type selected")
1399 self.credentials.set_password(ldapadminpass)
1401 # Now start the slapd, so we can provision onto it. We keep the
1402 # subprocess context around, to kill this off at the successful
1403 # end of the script
1404 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1406 while self.slapd.poll() is None:
1407 # Wait until the socket appears
1408 try:
1409 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1410 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1411 expression="(objectClass=OpenLDAProotDSE)")
1412 # If we have got here, then we must have a valid connection to the LDAP server!
1413 return
1414 except LdbError, e:
1415 time.sleep(1)
1416 pass
1418 raise "slapd died before we could make a connection to it"
1421 def provision_openldap_backend(result, paths=None, setup_path=None, names=None, message=None,
1422 hostname=None, ldapadminpass=None, root=None,
1423 schema=None,
1424 ldap_backend_extra_port=None,
1425 ol_mmr_urls=None,
1426 slapd_path=None, nosync=False,
1427 ldap_dryrun_mode=False):
1429 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1430 nosync_config = ""
1431 if nosync:
1432 nosync_config = "dbnosync"
1434 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1435 refint_attributes = ""
1436 memberof_config = "# Generated from Samba4 schema\n"
1437 for att in lnkattr.keys():
1438 if lnkattr[att] is not None:
1439 refint_attributes = refint_attributes + " " + att
1441 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1442 { "MEMBER_ATTR" : att ,
1443 "MEMBEROF_ATTR" : lnkattr[att] })
1445 refint_config = read_and_sub_file(setup_path("refint.conf"),
1446 { "LINK_ATTRS" : refint_attributes})
1448 attrs = ["linkID", "lDAPDisplayName"]
1449 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1450 index_config = ""
1451 for i in range (0, len(res)):
1452 index_attr = res[i]["lDAPDisplayName"][0]
1453 if index_attr == "objectGUID":
1454 index_attr = "entryUUID"
1456 index_config += "index " + index_attr + " eq\n"
1458 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1459 mmr_on_config = ""
1460 mmr_replicator_acl = ""
1461 mmr_serverids_config = ""
1462 mmr_syncrepl_schema_config = ""
1463 mmr_syncrepl_config_config = ""
1464 mmr_syncrepl_user_config = ""
1467 if ol_mmr_urls is not None:
1468 # For now, make these equal
1469 mmr_pass = ldapadminpass
1471 url_list=filter(None,ol_mmr_urls.split(' '))
1472 if (len(url_list) == 1):
1473 url_list=filter(None,ol_mmr_urls.split(','))
1476 mmr_on_config = "MirrorMode On"
1477 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1478 serverid=0
1479 for url in url_list:
1480 serverid=serverid+1
1481 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1482 { "SERVERID" : str(serverid),
1483 "LDAPSERVER" : url })
1484 rid=serverid*10
1485 rid=rid+1
1486 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1487 { "RID" : str(rid),
1488 "MMRDN": names.schemadn,
1489 "LDAPSERVER" : url,
1490 "MMR_PASSWORD": mmr_pass})
1492 rid=rid+1
1493 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1494 { "RID" : str(rid),
1495 "MMRDN": names.configdn,
1496 "LDAPSERVER" : url,
1497 "MMR_PASSWORD": mmr_pass})
1499 rid=rid+1
1500 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1501 { "RID" : str(rid),
1502 "MMRDN": names.domaindn,
1503 "LDAPSERVER" : url,
1504 "MMR_PASSWORD": mmr_pass })
1505 # OpenLDAP cn=config initialisation
1506 olc_syncrepl_config = ""
1507 olc_mmr_config = ""
1508 # if mmr = yes, generate cn=config-replication directives
1509 # and olc_seed.lif for the other mmr-servers
1510 if ol_mmr_urls is not None:
1511 serverid=0
1512 olc_serverids_config = ""
1513 olc_syncrepl_seed_config = ""
1514 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1515 rid=1000
1516 for url in url_list:
1517 serverid=serverid+1
1518 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1519 { "SERVERID" : str(serverid),
1520 "LDAPSERVER" : url })
1522 rid=rid+1
1523 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1524 { "RID" : str(rid),
1525 "LDAPSERVER" : url,
1526 "MMR_PASSWORD": mmr_pass})
1528 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1529 { "RID" : str(rid),
1530 "LDAPSERVER" : url})
1532 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1533 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1534 "OLC_PW": ldapadminpass,
1535 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1536 # end olc
1538 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1539 {"DNSDOMAIN": names.dnsdomain,
1540 "LDAPDIR": paths.ldapdir,
1541 "DOMAINDN": names.domaindn,
1542 "CONFIGDN": names.configdn,
1543 "SCHEMADN": names.schemadn,
1544 "MEMBEROF_CONFIG": memberof_config,
1545 "MIRRORMODE": mmr_on_config,
1546 "REPLICATOR_ACL": mmr_replicator_acl,
1547 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1548 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1549 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1550 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1551 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1552 "OLC_MMR_CONFIG": olc_mmr_config,
1553 "REFINT_CONFIG": refint_config,
1554 "INDEX_CONFIG": index_config,
1555 "NOSYNC": nosync_config})
1557 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1558 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1559 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1561 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1562 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1564 setup_file(setup_path("cn=samba.ldif"),
1565 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1566 { "UUID": str(uuid.uuid4()),
1567 "LDAPTIME": timestring(int(time.time()))} )
1568 setup_file(setup_path("cn=samba-admin.ldif"),
1569 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1570 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1571 "UUID": str(uuid.uuid4()),
1572 "LDAPTIME": timestring(int(time.time()))} )
1574 if ol_mmr_urls is not None:
1575 setup_file(setup_path("cn=replicator.ldif"),
1576 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1577 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1578 "UUID": str(uuid.uuid4()),
1579 "LDAPTIME": timestring(int(time.time()))} )
1582 mapping = "schema-map-openldap-2.3"
1583 backend_schema = "backend-schema.schema"
1585 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1586 assert backend_schema_data is not None
1587 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1589 # now we generate the needed strings to start slapd automatically,
1590 # first ldapi_uri...
1591 if ldap_backend_extra_port is not None:
1592 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1593 # specified there as part of it's clue as to it's own name,
1594 # and not to replicate to itself
1595 if ol_mmr_urls is None:
1596 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1597 else:
1598 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1599 else:
1600 server_port_string = ""
1602 # Prepare the 'result' information - the commands to return in particular
1603 result.slapd_provision_command = [slapd_path]
1605 result.slapd_provision_command.append("-F" + paths.olcdir)
1607 result.slapd_provision_command.append("-h")
1609 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1610 result.slapd_command = list(result.slapd_provision_command)
1612 result.slapd_provision_command.append(result.ldapi_uri)
1613 result.slapd_provision_command.append("-d0")
1615 uris = result.ldapi_uri
1616 if server_port_string is not "":
1617 uris = uris + " " + server_port_string
1619 result.slapd_command.append(uris)
1621 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1622 result.credentials.set_username("samba-admin")
1624 # If we were just looking for crashes up to this point, it's a
1625 # good time to exit before we realise we don't have OpenLDAP on
1626 # this system
1627 if ldap_dryrun_mode:
1628 sys.exit(0)
1630 # Finally, convert the configuration into cn=config style!
1631 if not os.path.isdir(paths.olcdir):
1632 os.makedirs(paths.olcdir, 0770)
1634 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1636 # We can't do this, as OpenLDAP is strange. It gives an error
1637 # output to the above, but does the conversion sucessfully...
1639 # if retcode != 0:
1640 # raise("conversion from slapd.conf to cn=config failed")
1642 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1643 raise("conversion from slapd.conf to cn=config failed")
1645 # Don't confuse the admin by leaving the slapd.conf around
1646 os.remove(paths.slapdconf)
1649 def provision_fds_backend(result, paths=None, setup_path=None, names=None, message=None,
1650 hostname=None, ldapadminpass=None, root=None,
1651 schema=None,
1652 ldap_backend_extra_port=None,
1653 setup_ds_path=None,
1654 slapd_path=None,
1655 nosync=False,
1656 ldap_dryrun_mode=False):
1658 if ldap_backend_extra_port is not None:
1659 serverport = "ServerPort=%d" % ldap_backend_extra_port
1660 else:
1661 serverport = ""
1663 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1664 {"ROOT": root,
1665 "HOSTNAME": hostname,
1666 "DNSDOMAIN": names.dnsdomain,
1667 "LDAPDIR": paths.ldapdir,
1668 "DOMAINDN": names.domaindn,
1669 "LDAPMANAGERDN": names.ldapmanagerdn,
1670 "LDAPMANAGERPASS": ldapadminpass,
1671 "SERVERPORT": serverport})
1673 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1674 {"CONFIGDN": names.configdn,
1675 "SCHEMADN": names.schemadn,
1678 mapping = "schema-map-fedora-ds-1.0"
1679 backend_schema = "99_ad.ldif"
1681 # Build a schema file in Fedora DS format
1682 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1683 assert backend_schema_data is not None
1684 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1686 result.credentials.set_bind_dn(names.ldapmanagerdn)
1688 # Destory the target directory, or else setup-ds.pl will complain
1689 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1690 shutil.rmtree(fedora_ds_dir, True)
1692 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1693 #In the 'provision' command line, stay in the foreground so we can easily kill it
1694 result.slapd_provision_command.append("-d0")
1696 #the command for the final run is the normal script
1697 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1699 # If we were just looking for crashes up to this point, it's a
1700 # good time to exit before we realise we don't have Fedora DS on
1701 if ldap_dryrun_mode:
1702 sys.exit(0)
1704 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1705 if setup_ds_path is None:
1706 raise("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1707 if not os.path.exists(setup_ds_path):
1708 message (setup_ds_path)
1709 raise("Warning: Given Path to slapd does not exist!")
1711 # Run the Fedora DS setup utility
1712 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1713 if retcode != 0:
1714 raise("setup-ds failed")
1716 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1717 """Create a PHP LDAP admin configuration file.
1719 :param path: Path to write the configuration to.
1720 :param setup_path: Function to generate setup paths.
1722 setup_file(setup_path("phpldapadmin-config.php"), path,
1723 {"S4_LDAPI_URI": ldapi_uri})
1726 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1727 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1728 """Write out a DNS zone file, from the info in the current database.
1730 :param path: Path of the new zone file.
1731 :param setup_path: Setup path function.
1732 :param dnsdomain: DNS Domain name
1733 :param domaindn: DN of the Domain
1734 :param hostip: Local IPv4 IP
1735 :param hostip6: Local IPv6 IP
1736 :param hostname: Local hostname
1737 :param dnspass: Password for DNS
1738 :param realm: Realm name
1739 :param domainguid: GUID of the domain.
1740 :param hostguid: GUID of the host.
1742 assert isinstance(domainguid, str)
1744 if hostip6 is not None:
1745 hostip6_base_line = " IN AAAA " + hostip6
1746 hostip6_host_line = hostname + " IN AAAA " + hostip6
1747 else:
1748 hostip6_base_line = ""
1749 hostip6_host_line = ""
1751 if hostip is not None:
1752 hostip_base_line = " IN A " + hostip
1753 hostip_host_line = hostname + " IN A " + hostip
1754 else:
1755 hostip_base_line = ""
1756 hostip_host_line = ""
1758 setup_file(setup_path("provision.zone"), path, {
1759 "DNSPASS_B64": b64encode(dnspass),
1760 "HOSTNAME": hostname,
1761 "DNSDOMAIN": dnsdomain,
1762 "REALM": realm,
1763 "HOSTIP_BASE_LINE": hostip_base_line,
1764 "HOSTIP_HOST_LINE": hostip_host_line,
1765 "DOMAINGUID": domainguid,
1766 "DATESTRING": time.strftime("%Y%m%d%H"),
1767 "DEFAULTSITE": DEFAULTSITE,
1768 "HOSTGUID": hostguid,
1769 "HOSTIP6_BASE_LINE": hostip6_base_line,
1770 "HOSTIP6_HOST_LINE": hostip6_host_line,
1774 def create_named_conf(path, setup_path, realm, dnsdomain,
1775 private_dir):
1776 """Write out a file containing zone statements suitable for inclusion in a
1777 named.conf file (including GSS-TSIG configuration).
1779 :param path: Path of the new named.conf file.
1780 :param setup_path: Setup path function.
1781 :param realm: Realm name
1782 :param dnsdomain: DNS Domain name
1783 :param private_dir: Path to private directory
1784 :param keytab_name: File name of DNS keytab file
1787 setup_file(setup_path("named.conf"), path, {
1788 "DNSDOMAIN": dnsdomain,
1789 "REALM": realm,
1790 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1791 "PRIVATE_DIR": private_dir
1794 def create_named_txt(path, setup_path, realm, dnsdomain,
1795 private_dir, keytab_name):
1796 """Write out a file containing zone statements suitable for inclusion in a
1797 named.conf file (including GSS-TSIG configuration).
1799 :param path: Path of the new named.conf file.
1800 :param setup_path: Setup path function.
1801 :param realm: Realm name
1802 :param dnsdomain: DNS Domain name
1803 :param private_dir: Path to private directory
1804 :param keytab_name: File name of DNS keytab file
1807 setup_file(setup_path("named.txt"), path, {
1808 "DNSDOMAIN": dnsdomain,
1809 "REALM": realm,
1810 "DNS_KEYTAB": keytab_name,
1811 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1812 "PRIVATE_DIR": private_dir
1815 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1816 """Write out a file containing zone statements suitable for inclusion in a
1817 named.conf file (including GSS-TSIG configuration).
1819 :param path: Path of the new named.conf file.
1820 :param setup_path: Setup path function.
1821 :param dnsdomain: DNS Domain name
1822 :param hostname: Local hostname
1823 :param realm: Realm name
1826 setup_file(setup_path("krb5.conf"), path, {
1827 "DNSDOMAIN": dnsdomain,
1828 "HOSTNAME": hostname,
1829 "REALM": realm,