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
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2000, DS_DC_FUNCTION_2008_R2
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
62 class ProvisioningError(ValueError):
67 """Find the setup directory used by provision."""
68 dirname = os.path.dirname(__file__)
69 if "/site-packages/" in dirname:
70 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
71 for suffix in ["share/setup", "share/samba/setup", "setup"]:
72 ret = os.path.join(prefix, suffix)
73 if os.path.isdir(ret):
76 ret = os.path.join(dirname, "../../../setup")
77 if os.path.isdir(ret):
79 raise Exception("Unable to find setup directory.")
82 DEFAULTSITE = "Default-First-Site-Name"
84 class InvalidNetbiosName(Exception):
85 """A specified name was not a valid NetBIOS name."""
86 def __init__(self, name):
87 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
90 class ProvisionPaths(object):
103 self.dns_keytab = None
106 self.private_dir = None
108 self.slapdconf = None
109 self.modulesconf = None
110 self.memberofconf = None
111 self.fedoradsinf = None
112 self.fedoradspartitions = None
113 self.fedoradssasl = None
115 self.olmmrserveridsconf = None
116 self.olmmrsyncreplconf = None
119 self.olcseedldif = None
122 class ProvisionNames(object):
129 self.ldapmanagerdn = None
130 self.dnsdomain = None
132 self.netbiosname = None
139 class ProvisionResult(object):
146 class Schema(object):
147 def __init__(self, setup_path, schemadn=None,
148 serverdn=None, sambadn=None, ldap_backend_type=None):
149 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
151 :param samdb: Load a schema into a SamDB.
152 :param setup_path: Setup path function.
153 :param schemadn: DN of the schema
154 :param serverdn: DN of the server
156 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
160 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
161 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
162 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
163 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
164 check_all_substituted(self.schema_data)
166 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
167 {"SCHEMADN": schemadn,
168 "SERVERDN": serverdn,
170 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
171 {"SCHEMADN": schemadn
174 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
175 prefixmap = b64encode(prefixmap)
177 # We don't actually add this ldif, just parse it
178 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
179 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
182 # Return a hash with the forward attribute as a key and the back as the value
183 def get_linked_attributes(schemadn,schemaldb):
184 attrs = ["linkID", "lDAPDisplayName"]
185 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)
187 for i in range (0, len(res)):
188 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
189 target = schemaldb.searchone(basedn=schemadn,
190 expression=expression,
191 attribute="lDAPDisplayName",
193 if target is not None:
194 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
198 def get_dnsyntax_attributes(schemadn,schemaldb):
199 attrs = ["linkID", "lDAPDisplayName"]
200 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
202 for i in range (0, len(res)):
203 attributes.append(str(res[i]["lDAPDisplayName"]))
208 def check_install(lp, session_info, credentials):
209 """Check whether the current install seems ok.
211 :param lp: Loadparm context
212 :param session_info: Session information
213 :param credentials: Credentials
215 if lp.get("realm") == "":
216 raise Exception("Realm empty")
217 ldb = Ldb(lp.get("sam database"), session_info=session_info,
218 credentials=credentials, lp=lp)
219 if len(ldb.search("(cn=Administrator)")) != 1:
220 raise ProvisioningError("No administrator account found")
223 def findnss(nssfn, names):
224 """Find a user or group from a list of possibilities.
226 :param nssfn: NSS Function to try (should raise KeyError if not found)
227 :param names: Names to check.
228 :return: Value return by first names list.
235 raise KeyError("Unable to find user/group %r" % names)
238 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
239 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
242 def read_and_sub_file(file, subst_vars):
243 """Read a file and sub in variables found in it
245 :param file: File to be read (typically from setup directory)
246 param subst_vars: Optional variables to subsitute in the file.
248 data = open(file, 'r').read()
249 if subst_vars is not None:
250 data = substitute_var(data, subst_vars)
251 check_all_substituted(data)
255 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
256 """Setup a ldb in the private dir.
258 :param ldb: LDB file to import data into
259 :param ldif_path: Path of the LDIF file to load
260 :param subst_vars: Optional variables to subsitute in LDIF.
262 assert isinstance(ldif_path, str)
264 data = read_and_sub_file(ldif_path, subst_vars)
268 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
269 """Modify a ldb in the private dir.
271 :param ldb: LDB object.
272 :param ldif_path: LDIF file path.
273 :param subst_vars: Optional dictionary with substitution variables.
275 data = read_and_sub_file(ldif_path, subst_vars)
277 ldb.modify_ldif(data)
280 def setup_ldb(ldb, ldif_path, subst_vars):
281 """Import a LDIF a file into a LDB handle, optionally substituting variables.
283 :note: Either all LDIF data will be added or none (using transactions).
285 :param ldb: LDB file to import into.
286 :param ldif_path: Path to the LDIF file.
287 :param subst_vars: Dictionary with substitution variables.
289 assert ldb is not None
290 ldb.transaction_start()
292 setup_add_ldif(ldb, ldif_path, subst_vars)
294 ldb.transaction_cancel()
296 ldb.transaction_commit()
299 def setup_file(template, fname, subst_vars):
300 """Setup a file in the private dir.
302 :param template: Path of the template file.
303 :param fname: Path of the file to create.
304 :param subst_vars: Substitution variables.
308 if os.path.exists(f):
311 data = read_and_sub_file(template, subst_vars)
312 open(f, 'w').write(data)
315 def provision_paths_from_lp(lp, dnsdomain):
316 """Set the default paths for provisioning.
318 :param lp: Loadparm context.
319 :param dnsdomain: DNS Domain name
321 paths = ProvisionPaths()
322 paths.private_dir = lp.get("private dir")
323 paths.dns_keytab = "dns.keytab"
325 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
326 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
327 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
328 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
329 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
330 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
331 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
332 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
333 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
334 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
335 paths.phpldapadminconfig = os.path.join(paths.private_dir,
336 "phpldapadmin-config.php")
337 paths.ldapdir = os.path.join(paths.private_dir,
339 paths.slapdconf = os.path.join(paths.ldapdir,
341 paths.slapdpid = os.path.join(paths.ldapdir,
343 paths.modulesconf = os.path.join(paths.ldapdir,
345 paths.memberofconf = os.path.join(paths.ldapdir,
347 paths.fedoradsinf = os.path.join(paths.ldapdir,
349 paths.fedoradspartitions = os.path.join(paths.ldapdir,
350 "fedorads-partitions.ldif")
351 paths.fedoradssasl = os.path.join(paths.ldapdir,
352 "fedorads-sasl.ldif")
353 paths.fedoradssamba = os.path.join(paths.ldapdir,
354 "fedorads-samba.ldif")
355 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
356 "mmr_serverids.conf")
357 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
359 paths.olcdir = os.path.join(paths.ldapdir,
361 paths.olcseedldif = os.path.join(paths.ldapdir,
363 paths.hklm = "hklm.ldb"
364 paths.hkcr = "hkcr.ldb"
365 paths.hkcu = "hkcu.ldb"
366 paths.hku = "hku.ldb"
367 paths.hkpd = "hkpd.ldb"
368 paths.hkpt = "hkpt.ldb"
370 paths.sysvol = lp.get("path", "sysvol")
372 paths.netlogon = lp.get("path", "netlogon")
374 paths.smbconf = lp.configfile
379 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
380 serverrole=None, rootdn=None, domaindn=None, configdn=None,
381 schemadn=None, serverdn=None, sitename=None, sambadn=None):
382 """Guess configuration settings to use."""
385 hostname = socket.gethostname().split(".")[0].lower()
387 netbiosname = hostname.upper()
388 if not valid_netbios_name(netbiosname):
389 raise InvalidNetbiosName(netbiosname)
391 hostname = hostname.lower()
393 if dnsdomain is None:
394 dnsdomain = lp.get("realm")
396 if serverrole is None:
397 serverrole = lp.get("server role")
399 assert dnsdomain is not None
400 realm = dnsdomain.upper()
402 if lp.get("realm").upper() != realm:
403 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
404 (lp.get("realm"), lp.configfile, realm))
406 dnsdomain = dnsdomain.lower()
408 if serverrole == "domain controller":
410 domain = lp.get("workgroup")
412 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
413 if lp.get("workgroup").upper() != domain.upper():
414 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
415 lp.get("workgroup"), domain)
419 domaindn = "CN=" + netbiosname
421 assert domain is not None
422 domain = domain.upper()
423 if not valid_netbios_name(domain):
424 raise InvalidNetbiosName(domain)
426 if netbiosname.upper() == realm.upper():
427 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
429 if hostname.upper() == realm.upper():
430 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
432 if domain.upper() == realm.upper():
433 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
439 configdn = "CN=Configuration," + rootdn
441 schemadn = "CN=Schema," + configdn
448 names = ProvisionNames()
449 names.rootdn = rootdn
450 names.domaindn = domaindn
451 names.configdn = configdn
452 names.schemadn = schemadn
453 names.sambadn = sambadn
454 names.ldapmanagerdn = "CN=Manager," + rootdn
455 names.dnsdomain = dnsdomain
456 names.domain = domain
458 names.netbiosname = netbiosname
459 names.hostname = hostname
460 names.sitename = sitename
461 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
466 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
468 """Create a new smb.conf file based on a couple of basic settings.
470 assert smbconf is not None
472 hostname = socket.gethostname().split(".")[0].lower()
474 if serverrole is None:
475 serverrole = "standalone"
477 assert serverrole in ("domain controller", "member server", "standalone")
478 if serverrole == "domain controller":
480 elif serverrole == "member server":
481 smbconfsuffix = "member"
482 elif serverrole == "standalone":
483 smbconfsuffix = "standalone"
485 assert domain is not None
486 assert realm is not None
488 default_lp = param.LoadParm()
489 #Load non-existant file
490 if os.path.exists(smbconf):
491 default_lp.load(smbconf)
493 if targetdir is not None:
494 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
495 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
497 default_lp.set("lock dir", os.path.abspath(targetdir))
502 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
503 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
505 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
507 "HOSTNAME": hostname,
510 "SERVERROLE": serverrole,
511 "NETLOGONPATH": netlogon,
512 "SYSVOLPATH": sysvol,
513 "PRIVATEDIR_LINE": privatedir_line,
514 "LOCKDIR_LINE": lockdir_line
518 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
519 users_gid, wheel_gid):
520 """setup reasonable name mappings for sam names to unix names.
522 :param samdb: SamDB object.
523 :param idmap: IDmap db object.
524 :param sid: The domain sid.
525 :param domaindn: The domain DN.
526 :param root_uid: uid of the UNIX root user.
527 :param nobody_uid: uid of the UNIX nobody user.
528 :param users_gid: gid of the UNIX users group.
529 :param wheel_gid: gid of the UNIX wheel group."""
531 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
532 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
534 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
535 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
537 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
539 serverrole, ldap_backend=None,
541 """Setup the partitions for the SAM database.
543 Alternatively, provision() may call this, and then populate the database.
545 :note: This will wipe the Sam Database!
547 :note: This function always removes the local SAM LDB file. The erase
548 parameter controls whether to erase the existing data, which
549 may not be stored locally but in LDAP.
551 assert session_info is not None
553 # We use options=["modules:"] to stop the modules loading - we
554 # just want to wipe and re-initialise the database, not start it up
557 samdb = Ldb(url=samdb_path, session_info=session_info,
558 credentials=credentials, lp=lp, options=["modules:"])
560 samdb.erase_except_schema_controlled()
562 os.unlink(samdb_path)
563 samdb = Ldb(url=samdb_path, session_info=session_info,
564 credentials=credentials, lp=lp, options=["modules:"])
566 samdb.erase_except_schema_controlled()
569 #Add modules to the list to activate them by default
570 #beware often order is important
572 # Some Known ordering constraints:
573 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
574 # - objectclass must be before password_hash, because password_hash checks
575 # that the objectclass is of type person (filled in by objectclass
576 # module when expanding the objectclass list)
577 # - partition must be last
578 # - each partition has its own module list then
579 modules_list = ["resolve_oids",
599 "extended_dn_out_ldb"]
600 modules_list2 = ["show_deleted",
603 domaindn_ldb = "users.ldb"
604 configdn_ldb = "configuration.ldb"
605 schemadn_ldb = "schema.ldb"
606 if ldap_backend is not None:
607 domaindn_ldb = ldap_backend.ldapi_uri
608 configdn_ldb = ldap_backend.ldapi_uri
609 schemadn_ldb = ldap_backend.ldapi_uri
611 if ldap_backend.ldap_backend_type == "fedora-ds":
612 backend_modules = ["nsuniqueid", "paged_searches"]
613 # We can handle linked attributes here, as we don't have directory-side subtree operations
614 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
615 elif ldap_backend.ldap_backend_type == "openldap":
616 backend_modules = ["entryuuid", "paged_searches"]
617 # OpenLDAP handles subtree renames, so we don't want to do any of these things
618 tdb_modules_list = ["extended_dn_out_dereference"]
620 elif serverrole == "domain controller":
621 tdb_modules_list.insert(0, "repl_meta_data")
624 backend_modules = ["objectguid"]
626 if tdb_modules_list is None:
627 tdb_modules_list_as_string = ""
629 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
631 samdb.transaction_start()
633 message("Setting up sam.ldb partitions and settings")
634 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
635 "SCHEMADN": names.schemadn,
636 "SCHEMADN_LDB": schemadn_ldb,
637 "SCHEMADN_MOD2": ",objectguid",
638 "CONFIGDN": names.configdn,
639 "CONFIGDN_LDB": configdn_ldb,
640 "DOMAINDN": names.domaindn,
641 "DOMAINDN_LDB": domaindn_ldb,
642 "SCHEMADN_MOD": "schema_fsmo,instancetype",
643 "CONFIGDN_MOD": "naming_fsmo,instancetype",
644 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
645 "MODULES_LIST": ",".join(modules_list),
646 "TDB_MODULES_LIST": tdb_modules_list_as_string,
647 "MODULES_LIST2": ",".join(modules_list2),
648 "BACKEND_MOD": ",".join(backend_modules),
651 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
653 message("Setting up sam.ldb rootDSE")
654 setup_samdb_rootdse(samdb, setup_path, names)
657 samdb.transaction_cancel()
660 samdb.transaction_commit()
662 def secretsdb_self_join(secretsdb, domain,
663 netbiosname, domainsid, machinepass,
664 realm=None, dnsdomain=None,
666 key_version_number=1,
667 secure_channel_type=SEC_CHAN_WKSTA):
668 """Add domain join-specific bits to a secrets database.
670 :param secretsdb: Ldb Handle to the secrets database
671 :param machinepass: Machine password
673 attrs=["whenChanged",
681 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
682 msg["secureChannelType"] = str(secure_channel_type)
683 msg["flatname"] = [domain]
684 msg["objectClass"] = ["top", "primaryDomain"]
685 if realm is not None:
686 if dnsdomain is None:
687 dnsdomain = realm.lower()
688 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
690 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
691 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
692 msg["privateKeytab"] = ["secrets.keytab"];
695 msg["secret"] = [machinepass]
696 msg["samAccountName"] = ["%s$" % netbiosname]
697 msg["secureChannelType"] = [str(secure_channel_type)]
698 msg["objectSid"] = [ndr_pack(domainsid)]
700 res = secretsdb.search(base="cn=Primary Domains",
702 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
703 scope=SCOPE_ONELEVEL)
706 if del_msg.dn is not msg.dn:
707 secretsdb.delete(del_msg.dn)
709 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
712 msg["priorSecret"] = res[0]["secret"]
713 msg["priorWhenChanged"] = res[0]["whenChanged"]
715 if res["privateKeytab"] is not None:
716 msg["privateKeytab"] = res[0]["privateKeytab"]
718 if res["krb5Keytab"] is not None:
719 msg["krb5Keytab"] = res[0]["krb5Keytab"]
722 el.set_flags(ldb.FLAG_MOD_REPLACE)
723 secretsdb.modify(msg)
728 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
729 dns_keytab_path, dnspass):
730 """Add DNS specific bits to a secrets database.
732 :param secretsdb: Ldb Handle to the secrets database
733 :param setup_path: Setup path function
734 :param machinepass: Machine password
736 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
738 "DNSDOMAIN": dnsdomain,
739 "DNS_KEYTAB": dns_keytab_path,
740 "DNSPASS_B64": b64encode(dnspass),
744 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
745 """Setup the secrets database.
747 :param path: Path to the secrets database.
748 :param setup_path: Get the path to a setup file.
749 :param session_info: Session info.
750 :param credentials: Credentials
751 :param lp: Loadparm context
752 :return: LDB handle for the created secrets database
754 if os.path.exists(path):
756 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
759 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
760 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
762 secrets_ldb.transaction_start()
763 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
765 if credentials is not None and credentials.authentication_requested():
766 if credentials.get_bind_dn() is not None:
767 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
768 "LDAPMANAGERDN": credentials.get_bind_dn(),
769 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
772 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
773 "LDAPADMINUSER": credentials.get_username(),
774 "LDAPADMINREALM": credentials.get_realm(),
775 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
780 def setup_registry(path, setup_path, session_info, lp):
781 """Setup the registry.
783 :param path: Path to the registry database
784 :param setup_path: Function that returns the path to a setup.
785 :param session_info: Session information
786 :param credentials: Credentials
787 :param lp: Loadparm context
789 reg = registry.Registry()
790 hive = registry.open_ldb(path, session_info=session_info,
792 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
793 provision_reg = setup_path("provision.reg")
794 assert os.path.exists(provision_reg)
795 reg.diff_apply(provision_reg)
798 def setup_idmapdb(path, setup_path, session_info, lp):
799 """Setup the idmap database.
801 :param path: path to the idmap database
802 :param setup_path: Function that returns a path to a setup file
803 :param session_info: Session information
804 :param credentials: Credentials
805 :param lp: Loadparm context
807 if os.path.exists(path):
810 idmap_ldb = IDmapDB(path, session_info=session_info,
814 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
818 def setup_samdb_rootdse(samdb, setup_path, names):
819 """Setup the SamDB rootdse.
821 :param samdb: Sam Database handle
822 :param setup_path: Obtain setup path
824 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
825 "SCHEMADN": names.schemadn,
826 "NETBIOSNAME": names.netbiosname,
827 "DNSDOMAIN": names.dnsdomain,
828 "REALM": names.realm,
829 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
830 "DOMAINDN": names.domaindn,
831 "ROOTDN": names.rootdn,
832 "CONFIGDN": names.configdn,
833 "SERVERDN": names.serverdn,
837 def setup_self_join(samdb, names,
838 machinepass, dnspass,
839 domainsid, invocationid, setup_path,
840 policyguid, policyguid_dc, domainControllerFunctionality):
841 """Join a host to its own domain."""
842 assert isinstance(invocationid, str)
843 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
844 "CONFIGDN": names.configdn,
845 "SCHEMADN": names.schemadn,
846 "DOMAINDN": names.domaindn,
847 "SERVERDN": names.serverdn,
848 "INVOCATIONID": invocationid,
849 "NETBIOSNAME": names.netbiosname,
850 "DEFAULTSITE": names.sitename,
851 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
852 "MACHINEPASS_B64": b64encode(machinepass),
853 "DNSPASS_B64": b64encode(dnspass),
854 "REALM": names.realm,
855 "DOMAIN": names.domain,
856 "DNSDOMAIN": names.dnsdomain,
857 "SAMBA_VERSION_STRING": version,
858 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
860 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
861 "POLICYGUID": policyguid,
862 "POLICYGUID_DC": policyguid_dc,
863 "DNSDOMAIN": names.dnsdomain,
864 "DOMAINSID": str(domainsid),
865 "DOMAINDN": names.domaindn})
867 # add the NTDSGUID based SPNs
868 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
869 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
870 expression="", scope=SCOPE_BASE)
871 assert isinstance(names.ntdsguid, str)
873 # Setup fSMORoleOwner entries to point at the newly created DC entry
874 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
875 "DOMAIN": names.domain,
876 "DNSDOMAIN": names.dnsdomain,
877 "DOMAINDN": names.domaindn,
878 "CONFIGDN": names.configdn,
879 "SCHEMADN": names.schemadn,
880 "DEFAULTSITE": names.sitename,
881 "SERVERDN": names.serverdn,
882 "NETBIOSNAME": names.netbiosname,
883 "NTDSGUID": names.ntdsguid
887 def setup_samdb(path, setup_path, session_info, credentials, lp,
889 domainsid, domainguid, policyguid, policyguid_dc,
890 fill, adminpass, krbtgtpass,
891 machinepass, invocationid, dnspass,
892 serverrole, schema=None, ldap_backend=None):
893 """Setup a complete SAM Database.
895 :note: This will wipe the main SAM database file!
898 domainFunctionality = DS_DOMAIN_FUNCTION_2000
899 forestFunctionality = DS_DOMAIN_FUNCTION_2000
900 domainControllerFunctionality = DS_DC_FUNCTION_2008_R2
902 # Also wipes the database
903 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
904 credentials=credentials, session_info=session_info,
906 ldap_backend=ldap_backend, serverrole=serverrole)
909 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
910 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
912 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
913 samdb = Ldb(session_info=session_info,
914 credentials=credentials, lp=lp)
916 message("Pre-loading the Samba 4 and AD schema")
918 # Load the schema from the one we computed earlier
919 samdb.set_schema_from_ldb(schema.ldb)
921 # And now we can connect to the DB - the schema won't be loaded from the DB
925 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
930 samdb.transaction_start()
932 message("Erasing data from partitions")
933 # Load the schema (again). This time it will force a reindex,
934 # and will therefore make the erase_partitions() below
935 # computationally sane
936 samdb.set_schema_from_ldb(schema.ldb)
937 samdb.erase_partitions()
939 # Set the domain functionality levels onto the database.
940 # Various module (the password_hash module in particular) need
941 # to know what level of AD we are emulating.
943 # These will be fixed into the database via the database
944 # modifictions below, but we need them set from the start.
945 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
946 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
947 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
949 samdb.set_domain_sid(str(domainsid))
950 if serverrole == "domain controller":
951 samdb.set_invocation_id(invocationid)
953 message("Adding DomainDN: %s" % names.domaindn)
954 if serverrole == "domain controller":
955 domain_oc = "domainDNS"
957 domain_oc = "samba4LocalDomain"
959 #impersonate domain admin
960 admin_session_info = admin_session(lp, str(domainsid))
961 samdb.set_session_info(admin_session_info)
963 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
964 "DOMAINDN": names.domaindn,
965 "DOMAIN_OC": domain_oc
968 message("Modifying DomainDN: " + names.domaindn + "")
969 if domainguid is not None:
970 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
974 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
975 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
976 "DOMAINSID": str(domainsid),
977 "SCHEMADN": names.schemadn,
978 "NETBIOSNAME": names.netbiosname,
979 "DEFAULTSITE": names.sitename,
980 "CONFIGDN": names.configdn,
981 "SERVERDN": names.serverdn,
982 "POLICYGUID": policyguid,
983 "DOMAINDN": names.domaindn,
984 "DOMAINGUID_MOD": domainguid_mod,
985 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
986 "SAMBA_VERSION_STRING": version
989 message("Adding configuration container")
990 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
991 "CONFIGDN": names.configdn,
993 message("Modifying configuration container")
994 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
995 "CONFIGDN": names.configdn,
996 "SCHEMADN": names.schemadn,
999 # The LDIF here was created when the Schema object was constructed
1000 message("Setting up sam.ldb schema")
1001 samdb.add_ldif(schema.schema_dn_add)
1002 samdb.modify_ldif(schema.schema_dn_modify)
1003 samdb.write_prefixes_from_schema()
1004 samdb.add_ldif(schema.schema_data)
1005 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1006 {"SCHEMADN": names.schemadn})
1008 message("Setting up sam.ldb configuration data")
1009 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1010 "CONFIGDN": names.configdn,
1011 "NETBIOSNAME": names.netbiosname,
1012 "DEFAULTSITE": names.sitename,
1013 "DNSDOMAIN": names.dnsdomain,
1014 "DOMAIN": names.domain,
1015 "SCHEMADN": names.schemadn,
1016 "DOMAINDN": names.domaindn,
1017 "SERVERDN": names.serverdn,
1018 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1021 message("Setting up display specifiers")
1022 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1023 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1024 check_all_substituted(display_specifiers_ldif)
1025 samdb.add_ldif(display_specifiers_ldif)
1027 message("Adding users container")
1028 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1029 "DOMAINDN": names.domaindn})
1030 message("Modifying users container")
1031 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1032 "DOMAINDN": names.domaindn})
1033 message("Adding computers container")
1034 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1035 "DOMAINDN": names.domaindn})
1036 message("Modifying computers container")
1037 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1038 "DOMAINDN": names.domaindn})
1039 message("Setting up sam.ldb data")
1040 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1041 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1042 "DOMAINDN": names.domaindn,
1043 "NETBIOSNAME": names.netbiosname,
1044 "DEFAULTSITE": names.sitename,
1045 "CONFIGDN": names.configdn,
1046 "SERVERDN": names.serverdn,
1047 "POLICYGUID_DC": policyguid_dc
1050 if fill == FILL_FULL:
1051 message("Setting up sam.ldb users and groups")
1052 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1053 "DOMAINDN": names.domaindn,
1054 "DOMAINSID": str(domainsid),
1055 "CONFIGDN": names.configdn,
1056 "ADMINPASS_B64": b64encode(adminpass),
1057 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1060 if serverrole == "domain controller":
1061 message("Setting up self join")
1062 setup_self_join(samdb, names=names, invocationid=invocationid,
1064 machinepass=machinepass,
1065 domainsid=domainsid, policyguid=policyguid,
1066 policyguid_dc=policyguid_dc,
1067 setup_path=setup_path,
1068 domainControllerFunctionality=domainControllerFunctionality)
1070 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1071 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1072 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1073 assert isinstance(names.ntdsguid, str)
1076 samdb.transaction_cancel()
1079 samdb.transaction_commit()
1084 FILL_NT4SYNC = "NT4SYNC"
1088 def provision(setup_dir, message, session_info,
1089 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1091 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1093 domain=None, hostname=None, hostip=None, hostip6=None,
1094 domainsid=None, adminpass=None, ldapadminpass=None,
1095 krbtgtpass=None, domainguid=None,
1096 policyguid=None, policyguid_dc=None, invocationid=None,
1098 dnspass=None, root=None, nobody=None, users=None,
1099 wheel=None, backup=None, aci=None, serverrole=None,
1100 ldap_backend_extra_port=None, ldap_backend_type=None,
1102 ol_mmr_urls=None, ol_olc=None,
1103 setup_ds_path=None, slapd_path=None, nosync=False,
1104 ldap_dryrun_mode=False):
1107 :note: caution, this wipes all existing data!
1110 def setup_path(file):
1111 return os.path.join(setup_dir, file)
1113 if domainsid is None:
1114 domainsid = security.random_sid()
1116 domainsid = security.dom_sid(domainsid)
1119 # create/adapt the group policy GUIDs
1120 if policyguid is None:
1121 policyguid = str(uuid.uuid4())
1122 policyguid = policyguid.upper()
1123 if policyguid_dc is None:
1124 policyguid_dc = str(uuid.uuid4())
1125 policyguid_dc = policyguid_dc.upper()
1127 if adminpass is None:
1128 adminpass = glue.generate_random_str(12)
1129 if krbtgtpass is None:
1130 krbtgtpass = glue.generate_random_str(12)
1131 if machinepass is None:
1132 machinepass = glue.generate_random_str(12)
1134 dnspass = glue.generate_random_str(12)
1135 if ldapadminpass is None:
1136 #Make a new, random password between Samba and it's LDAP server
1137 ldapadminpass=glue.generate_random_str(12)
1140 root_uid = findnss_uid([root or "root"])
1141 nobody_uid = findnss_uid([nobody or "nobody"])
1142 users_gid = findnss_gid([users or "users"])
1144 wheel_gid = findnss_gid(["wheel", "adm"])
1146 wheel_gid = findnss_gid([wheel])
1148 if targetdir is not None:
1149 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1150 os.makedirs(os.path.join(targetdir, "etc"))
1151 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1152 elif smbconf is None:
1153 smbconf = param.default_path()
1155 # only install a new smb.conf if there isn't one there already
1156 if not os.path.exists(smbconf):
1157 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1160 lp = param.LoadParm()
1163 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1164 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1165 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1168 paths = provision_paths_from_lp(lp, names.dnsdomain)
1172 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1173 except socket.gaierror, (socket.EAI_NODATA, msg):
1178 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1179 except socket.gaierror, (socket.EAI_NODATA, msg):
1182 if serverrole is None:
1183 serverrole = lp.get("server role")
1185 assert serverrole in ("domain controller", "member server", "standalone")
1186 if invocationid is None and serverrole == "domain controller":
1187 invocationid = str(uuid.uuid4())
1189 if not os.path.exists(paths.private_dir):
1190 os.mkdir(paths.private_dir)
1192 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1194 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1195 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1197 secrets_credentials = credentials
1198 provision_backend = None
1199 if ldap_backend_type:
1200 # We only support an LDAP backend over ldapi://
1202 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1203 lp=lp, credentials=credentials,
1205 message=message, hostname=hostname,
1206 root=root, schema=schema,
1207 ldap_backend_type=ldap_backend_type,
1208 ldapadminpass=ldapadminpass,
1209 ldap_backend_extra_port=ldap_backend_extra_port,
1210 ol_mmr_urls=ol_mmr_urls,
1211 slapd_path=slapd_path,
1212 setup_ds_path=setup_ds_path,
1213 ldap_dryrun_mode=ldap_dryrun_mode)
1215 # Now use the backend credentials to access the databases
1216 credentials = provision_backend.credentials
1217 secrets_credentials = provision_backend.adminCredentials
1218 ldapi_url = provision_backend.ldapi_uri
1220 # only install a new shares config db if there is none
1221 if not os.path.exists(paths.shareconf):
1222 message("Setting up share.ldb")
1223 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1224 credentials=credentials, lp=lp)
1225 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1228 message("Setting up secrets.ldb")
1229 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1230 session_info=session_info,
1231 credentials=secrets_credentials, lp=lp)
1233 message("Setting up the registry")
1234 setup_registry(paths.hklm, setup_path, session_info,
1237 message("Setting up idmap db")
1238 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1241 message("Setting up SAM db")
1242 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1243 credentials=credentials, lp=lp, names=names,
1245 domainsid=domainsid,
1246 schema=schema, domainguid=domainguid,
1247 policyguid=policyguid, policyguid_dc=policyguid_dc,
1249 adminpass=adminpass, krbtgtpass=krbtgtpass,
1250 invocationid=invocationid,
1251 machinepass=machinepass, dnspass=dnspass,
1252 serverrole=serverrole, ldap_backend=provision_backend)
1254 if serverrole == "domain controller":
1255 if paths.netlogon is None:
1256 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1257 message("Please either remove %s or see the template at %s" %
1258 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1259 assert(paths.netlogon is not None)
1261 if paths.sysvol is None:
1262 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1263 message("Please either remove %s or see the template at %s" %
1264 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1265 assert(paths.sysvol is not None)
1267 # Set up group policies (domain policy and domain controller policy)
1269 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1270 "{" + policyguid + "}")
1271 os.makedirs(policy_path, 0755)
1272 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1273 "[General]\r\nVersion=65543")
1274 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1275 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1277 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1278 "{" + policyguid_dc + "}")
1279 os.makedirs(policy_path_dc, 0755)
1280 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1281 "[General]\r\nVersion=2")
1282 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1283 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1285 if not os.path.isdir(paths.netlogon):
1286 os.makedirs(paths.netlogon, 0755)
1288 if samdb_fill == FILL_FULL:
1289 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1290 root_uid=root_uid, nobody_uid=nobody_uid,
1291 users_gid=users_gid, wheel_gid=wheel_gid)
1293 message("Setting up sam.ldb rootDSE marking as synchronized")
1294 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1296 # Only make a zone file on the first DC, it should be replicated with DNS replication
1297 if serverrole == "domain controller":
1298 secretsdb_self_join(secrets_ldb, domain=domain,
1300 dnsdomain=names.dnsdomain,
1301 netbiosname=names.netbiosname,
1302 domainsid=domainsid,
1303 machinepass=machinepass,
1304 secure_channel_type=SEC_CHAN_BDC)
1306 secretsdb_setup_dns(secrets_ldb, setup_path,
1307 realm=names.realm, dnsdomain=names.dnsdomain,
1308 dns_keytab_path=paths.dns_keytab,
1311 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1312 assert isinstance(domainguid, str)
1314 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1315 domaindn=names.domaindn, hostip=hostip,
1316 hostip6=hostip6, hostname=names.hostname,
1317 dnspass=dnspass, realm=names.realm,
1318 domainguid=domainguid, ntdsguid=names.ntdsguid)
1320 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1321 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1323 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1324 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1325 keytab_name=paths.dns_keytab)
1326 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1327 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1329 create_krb5_conf(paths.krb5conf, setup_path,
1330 dnsdomain=names.dnsdomain, hostname=names.hostname,
1332 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1334 #Now commit the secrets.ldb to disk
1335 secrets_ldb.transaction_commit()
1337 if provision_backend is not None:
1338 if ldap_backend_type == "fedora-ds":
1339 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1341 # delete default SASL mappings
1342 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1344 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1345 for i in range (0, len(res)):
1346 dn = str(res[i]["dn"])
1349 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1352 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1354 m.dn = ldb.Dn(1, names.domaindn)
1357 m.dn = ldb.Dn(1, names.configdn)
1360 m.dn = ldb.Dn(1, names.schemadn)
1363 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1364 if provision_backend.slapd.poll() is None:
1366 if hasattr(provision_backend.slapd, "terminate"):
1367 provision_backend.slapd.terminate()
1369 # Older python versions don't have .terminate()
1371 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1373 #and now wait for it to die
1374 provision_backend.slapd.communicate()
1376 # now display slapd_command_file.txt to show how slapd must be started next time
1377 message("Use later the following commandline to start slapd, then Samba:")
1378 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1379 message(slapd_command)
1380 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1382 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1383 "SLAPD_COMMAND" : slapd_command})
1386 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1389 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1391 message("Once the above files are installed, your Samba4 server will be ready to use")
1392 message("Server Role: %s" % serverrole)
1393 message("Hostname: %s" % names.hostname)
1394 message("NetBIOS Domain: %s" % names.domain)
1395 message("DNS Domain: %s" % names.dnsdomain)
1396 message("DOMAIN SID: %s" % str(domainsid))
1397 if samdb_fill == FILL_FULL:
1398 message("Admin password: %s" % adminpass)
1399 if provision_backend:
1400 if provision_backend.credentials.get_bind_dn() is not None:
1401 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1403 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1405 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1407 result = ProvisionResult()
1408 result.domaindn = domaindn
1409 result.paths = paths
1411 result.samdb = samdb
1416 def provision_become_dc(setup_dir=None,
1417 smbconf=None, targetdir=None, realm=None,
1418 rootdn=None, domaindn=None, schemadn=None,
1419 configdn=None, serverdn=None,
1420 domain=None, hostname=None, domainsid=None,
1421 adminpass=None, krbtgtpass=None, domainguid=None,
1422 policyguid=None, policyguid_dc=None, invocationid=None,
1424 dnspass=None, root=None, nobody=None, users=None,
1425 wheel=None, backup=None, serverrole=None,
1426 ldap_backend=None, ldap_backend_type=None,
1427 sitename=None, debuglevel=1):
1430 """print a message if quiet is not set."""
1433 glue.set_debug_level(debuglevel)
1435 return provision(setup_dir, message, system_session(), None,
1436 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1437 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1438 configdn=configdn, serverdn=serverdn, domain=domain,
1439 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1440 machinepass=machinepass, serverrole="domain controller",
1444 def setup_db_config(setup_path, dbdir):
1445 """Setup a Berkeley database.
1447 :param setup_path: Setup path function.
1448 :param dbdir: Database directory."""
1449 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1450 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1451 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1452 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1454 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1455 {"LDAPDBDIR": dbdir})
1457 class ProvisionBackend(object):
1458 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1459 names=None, message=None,
1460 hostname=None, root=None,
1461 schema=None, ldapadminpass=None,
1462 ldap_backend_type=None, ldap_backend_extra_port=None,
1464 setup_ds_path=None, slapd_path=None,
1465 nosync=False, ldap_dryrun_mode=False):
1466 """Provision an LDAP backend for samba4
1468 This works for OpenLDAP and Fedora DS
1471 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1473 if not os.path.isdir(paths.ldapdir):
1474 os.makedirs(paths.ldapdir, 0700)
1476 if ldap_backend_type == "existing":
1477 #Check to see that this 'existing' LDAP backend in fact exists
1478 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1479 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1480 expression="(objectClass=OpenLDAProotDSE)")
1482 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1483 # This caused them to be set into the long-term database later in the script.
1484 self.credentials = credentials
1485 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1488 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1489 # if another instance of slapd is already running
1491 ldapi_db = Ldb(self.ldapi_uri)
1492 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1493 expression="(objectClass=OpenLDAProotDSE)");
1495 f = open(paths.slapdpid, "r")
1498 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1502 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1507 # Try to print helpful messages when the user has not specified the path to slapd
1508 if slapd_path is None:
1509 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1510 if not os.path.exists(slapd_path):
1511 message (slapd_path)
1512 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1514 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1516 os.unlink(schemadb_path)
1521 # Put the LDIF of the schema into a database so we can search on
1522 # it to generate schema-dependent configurations in Fedora DS and
1524 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1525 schema.ldb.connect(schemadb_path)
1526 schema.ldb.transaction_start()
1528 # These bits of LDIF are supplied when the Schema object is created
1529 schema.ldb.add_ldif(schema.schema_dn_add)
1530 schema.ldb.modify_ldif(schema.schema_dn_modify)
1531 schema.ldb.add_ldif(schema.schema_data)
1532 schema.ldb.transaction_commit()
1534 self.credentials = Credentials()
1535 self.credentials.guess(lp)
1536 #Kerberos to an ldapi:// backend makes no sense
1537 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1539 self.adminCredentials = Credentials()
1540 self.adminCredentials.guess(lp)
1541 #Kerberos to an ldapi:// backend makes no sense
1542 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1544 self.ldap_backend_type = ldap_backend_type
1546 if ldap_backend_type == "fedora-ds":
1547 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1548 names=names, message=message,
1550 ldapadminpass=ldapadminpass, root=root,
1552 ldap_backend_extra_port=ldap_backend_extra_port,
1553 setup_ds_path=setup_ds_path,
1554 slapd_path=slapd_path,
1556 ldap_dryrun_mode=ldap_dryrun_mode)
1558 elif ldap_backend_type == "openldap":
1559 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1560 names=names, message=message,
1562 ldapadminpass=ldapadminpass, root=root,
1564 ldap_backend_extra_port=ldap_backend_extra_port,
1565 ol_mmr_urls=ol_mmr_urls,
1566 slapd_path=slapd_path,
1568 ldap_dryrun_mode=ldap_dryrun_mode)
1570 raise ProvisioningError("Unknown LDAP backend type selected")
1572 self.credentials.set_password(ldapadminpass)
1573 self.adminCredentials.set_username("samba-admin")
1574 self.adminCredentials.set_password(ldapadminpass)
1576 # Now start the slapd, so we can provision onto it. We keep the
1577 # subprocess context around, to kill this off at the successful
1579 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1581 while self.slapd.poll() is None:
1582 # Wait until the socket appears
1584 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1585 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1586 expression="(objectClass=OpenLDAProotDSE)")
1587 # If we have got here, then we must have a valid connection to the LDAP server!
1593 raise ProvisioningError("slapd died before we could make a connection to it")
1596 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1598 hostname=None, ldapadminpass=None, root=None,
1600 ldap_backend_extra_port=None,
1602 slapd_path=None, nosync=False,
1603 ldap_dryrun_mode=False):
1605 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1608 nosync_config = "dbnosync"
1610 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1611 refint_attributes = ""
1612 memberof_config = "# Generated from Samba4 schema\n"
1613 for att in lnkattr.keys():
1614 if lnkattr[att] is not None:
1615 refint_attributes = refint_attributes + " " + att
1617 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1618 { "MEMBER_ATTR" : att ,
1619 "MEMBEROF_ATTR" : lnkattr[att] })
1621 refint_config = read_and_sub_file(setup_path("refint.conf"),
1622 { "LINK_ATTRS" : refint_attributes})
1624 attrs = ["linkID", "lDAPDisplayName"]
1625 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1627 for i in range (0, len(res)):
1628 index_attr = res[i]["lDAPDisplayName"][0]
1629 if index_attr == "objectGUID":
1630 index_attr = "entryUUID"
1632 index_config += "index " + index_attr + " eq\n"
1634 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1636 mmr_replicator_acl = ""
1637 mmr_serverids_config = ""
1638 mmr_syncrepl_schema_config = ""
1639 mmr_syncrepl_config_config = ""
1640 mmr_syncrepl_user_config = ""
1643 if ol_mmr_urls is not None:
1644 # For now, make these equal
1645 mmr_pass = ldapadminpass
1647 url_list=filter(None,ol_mmr_urls.split(' '))
1648 if (len(url_list) == 1):
1649 url_list=filter(None,ol_mmr_urls.split(','))
1652 mmr_on_config = "MirrorMode On"
1653 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1655 for url in url_list:
1657 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1658 { "SERVERID" : str(serverid),
1659 "LDAPSERVER" : url })
1662 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1664 "MMRDN": names.schemadn,
1666 "MMR_PASSWORD": mmr_pass})
1669 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1671 "MMRDN": names.configdn,
1673 "MMR_PASSWORD": mmr_pass})
1676 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1678 "MMRDN": names.domaindn,
1680 "MMR_PASSWORD": mmr_pass })
1681 # OpenLDAP cn=config initialisation
1682 olc_syncrepl_config = ""
1684 # if mmr = yes, generate cn=config-replication directives
1685 # and olc_seed.lif for the other mmr-servers
1686 if ol_mmr_urls is not None:
1688 olc_serverids_config = ""
1689 olc_syncrepl_seed_config = ""
1690 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1692 for url in url_list:
1694 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1695 { "SERVERID" : str(serverid),
1696 "LDAPSERVER" : url })
1699 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1702 "MMR_PASSWORD": mmr_pass})
1704 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1706 "LDAPSERVER" : url})
1708 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1709 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1710 "OLC_PW": ldapadminpass,
1711 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1714 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1715 {"DNSDOMAIN": names.dnsdomain,
1716 "LDAPDIR": paths.ldapdir,
1717 "DOMAINDN": names.domaindn,
1718 "CONFIGDN": names.configdn,
1719 "SCHEMADN": names.schemadn,
1720 "MEMBEROF_CONFIG": memberof_config,
1721 "MIRRORMODE": mmr_on_config,
1722 "REPLICATOR_ACL": mmr_replicator_acl,
1723 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1724 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1725 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1726 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1727 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1728 "OLC_MMR_CONFIG": olc_mmr_config,
1729 "REFINT_CONFIG": refint_config,
1730 "INDEX_CONFIG": index_config,
1731 "NOSYNC": nosync_config})
1733 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1734 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1735 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1737 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1738 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1740 setup_file(setup_path("cn=samba.ldif"),
1741 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1742 { "UUID": str(uuid.uuid4()),
1743 "LDAPTIME": timestring(int(time.time()))} )
1744 setup_file(setup_path("cn=samba-admin.ldif"),
1745 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1746 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1747 "UUID": str(uuid.uuid4()),
1748 "LDAPTIME": timestring(int(time.time()))} )
1750 if ol_mmr_urls is not None:
1751 setup_file(setup_path("cn=replicator.ldif"),
1752 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1753 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1754 "UUID": str(uuid.uuid4()),
1755 "LDAPTIME": timestring(int(time.time()))} )
1758 mapping = "schema-map-openldap-2.3"
1759 backend_schema = "backend-schema.schema"
1761 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1762 assert backend_schema_data is not None
1763 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1765 # now we generate the needed strings to start slapd automatically,
1766 # first ldapi_uri...
1767 if ldap_backend_extra_port is not None:
1768 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1769 # specified there as part of it's clue as to it's own name,
1770 # and not to replicate to itself
1771 if ol_mmr_urls is None:
1772 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1774 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1776 server_port_string = ""
1778 # Prepare the 'result' information - the commands to return in particular
1779 result.slapd_provision_command = [slapd_path]
1781 result.slapd_provision_command.append("-F" + paths.olcdir)
1783 result.slapd_provision_command.append("-h")
1785 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1786 result.slapd_command = list(result.slapd_provision_command)
1788 result.slapd_provision_command.append(result.ldapi_uri)
1789 result.slapd_provision_command.append("-d0")
1791 uris = result.ldapi_uri
1792 if server_port_string is not "":
1793 uris = uris + " " + server_port_string
1795 result.slapd_command.append(uris)
1797 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1798 result.credentials.set_username("samba-admin")
1800 # If we were just looking for crashes up to this point, it's a
1801 # good time to exit before we realise we don't have OpenLDAP on
1803 if ldap_dryrun_mode:
1806 # Finally, convert the configuration into cn=config style!
1807 if not os.path.isdir(paths.olcdir):
1808 os.makedirs(paths.olcdir, 0770)
1810 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1812 # We can't do this, as OpenLDAP is strange. It gives an error
1813 # output to the above, but does the conversion sucessfully...
1816 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1818 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1819 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1821 # Don't confuse the admin by leaving the slapd.conf around
1822 os.remove(paths.slapdconf)
1825 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1827 hostname=None, ldapadminpass=None, root=None,
1829 ldap_backend_extra_port=None,
1833 ldap_dryrun_mode=False):
1835 if ldap_backend_extra_port is not None:
1836 serverport = "ServerPort=%d" % ldap_backend_extra_port
1840 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1842 "HOSTNAME": hostname,
1843 "DNSDOMAIN": names.dnsdomain,
1844 "LDAPDIR": paths.ldapdir,
1845 "DOMAINDN": names.domaindn,
1846 "LDAPMANAGERDN": names.ldapmanagerdn,
1847 "LDAPMANAGERPASS": ldapadminpass,
1848 "SERVERPORT": serverport})
1850 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1851 {"CONFIGDN": names.configdn,
1852 "SCHEMADN": names.schemadn,
1853 "SAMBADN": names.sambadn,
1856 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1857 {"SAMBADN": names.sambadn,
1860 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1861 {"SAMBADN": names.sambadn,
1862 "LDAPADMINPASS": ldapadminpass
1865 mapping = "schema-map-fedora-ds-1.0"
1866 backend_schema = "99_ad.ldif"
1868 # Build a schema file in Fedora DS format
1869 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1870 assert backend_schema_data is not None
1871 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1873 result.credentials.set_bind_dn(names.ldapmanagerdn)
1875 # Destory the target directory, or else setup-ds.pl will complain
1876 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1877 shutil.rmtree(fedora_ds_dir, True)
1879 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1880 #In the 'provision' command line, stay in the foreground so we can easily kill it
1881 result.slapd_provision_command.append("-d0")
1883 #the command for the final run is the normal script
1884 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1886 # If we were just looking for crashes up to this point, it's a
1887 # good time to exit before we realise we don't have Fedora DS on
1888 if ldap_dryrun_mode:
1891 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1892 if setup_ds_path is None:
1893 raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1894 if not os.path.exists(setup_ds_path):
1895 message (setup_ds_path)
1896 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1898 # Run the Fedora DS setup utility
1899 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1901 raise ProvisioningError("setup-ds failed")
1904 retcode = subprocess.call([
1905 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1906 close_fds=True, shell=False)
1908 raise("ldib2db failed")
1910 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1911 """Create a PHP LDAP admin configuration file.
1913 :param path: Path to write the configuration to.
1914 :param setup_path: Function to generate setup paths.
1916 setup_file(setup_path("phpldapadmin-config.php"), path,
1917 {"S4_LDAPI_URI": ldapi_uri})
1920 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1921 hostip, hostip6, hostname, dnspass, realm, domainguid,
1923 """Write out a DNS zone file, from the info in the current database.
1925 :param path: Path of the new zone file.
1926 :param setup_path: Setup path function.
1927 :param dnsdomain: DNS Domain name
1928 :param domaindn: DN of the Domain
1929 :param hostip: Local IPv4 IP
1930 :param hostip6: Local IPv6 IP
1931 :param hostname: Local hostname
1932 :param dnspass: Password for DNS
1933 :param realm: Realm name
1934 :param domainguid: GUID of the domain.
1935 :param ntdsguid: GUID of the hosts nTDSDSA record.
1937 assert isinstance(domainguid, str)
1939 if hostip6 is not None:
1940 hostip6_base_line = " IN AAAA " + hostip6
1941 hostip6_host_line = hostname + " IN AAAA " + hostip6
1943 hostip6_base_line = ""
1944 hostip6_host_line = ""
1946 if hostip is not None:
1947 hostip_base_line = " IN A " + hostip
1948 hostip_host_line = hostname + " IN A " + hostip
1950 hostip_base_line = ""
1951 hostip_host_line = ""
1953 setup_file(setup_path("provision.zone"), path, {
1954 "DNSPASS_B64": b64encode(dnspass),
1955 "HOSTNAME": hostname,
1956 "DNSDOMAIN": dnsdomain,
1958 "HOSTIP_BASE_LINE": hostip_base_line,
1959 "HOSTIP_HOST_LINE": hostip_host_line,
1960 "DOMAINGUID": domainguid,
1961 "DATESTRING": time.strftime("%Y%m%d%H"),
1962 "DEFAULTSITE": DEFAULTSITE,
1963 "NTDSGUID": ntdsguid,
1964 "HOSTIP6_BASE_LINE": hostip6_base_line,
1965 "HOSTIP6_HOST_LINE": hostip6_host_line,
1969 def create_named_conf(path, setup_path, realm, dnsdomain,
1971 """Write out a file containing zone statements suitable for inclusion in a
1972 named.conf file (including GSS-TSIG configuration).
1974 :param path: Path of the new named.conf file.
1975 :param setup_path: Setup path function.
1976 :param realm: Realm name
1977 :param dnsdomain: DNS Domain name
1978 :param private_dir: Path to private directory
1979 :param keytab_name: File name of DNS keytab file
1982 setup_file(setup_path("named.conf"), path, {
1983 "DNSDOMAIN": dnsdomain,
1985 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1986 "PRIVATE_DIR": private_dir
1989 def create_named_txt(path, setup_path, realm, dnsdomain,
1990 private_dir, keytab_name):
1991 """Write out a file containing zone statements suitable for inclusion in a
1992 named.conf file (including GSS-TSIG configuration).
1994 :param path: Path of the new named.conf file.
1995 :param setup_path: Setup path function.
1996 :param realm: Realm name
1997 :param dnsdomain: DNS Domain name
1998 :param private_dir: Path to private directory
1999 :param keytab_name: File name of DNS keytab file
2002 setup_file(setup_path("named.txt"), path, {
2003 "DNSDOMAIN": dnsdomain,
2005 "DNS_KEYTAB": keytab_name,
2006 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2007 "PRIVATE_DIR": private_dir
2010 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2011 """Write out a file containing zone statements suitable for inclusion in a
2012 named.conf file (including GSS-TSIG configuration).
2014 :param path: Path of the new named.conf file.
2015 :param setup_path: Setup path function.
2016 :param dnsdomain: DNS Domain name
2017 :param hostname: Local hostname
2018 :param realm: Realm name
2021 setup_file(setup_path("krb5.conf"), path, {
2022 "DNSDOMAIN": dnsdomain,
2023 "HOSTNAME": hostname,