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
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, \
46 from samba.samdb import SamDB
47 from samba.idmap import IDmapDB
48 from samba.dcerpc import security
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"
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):
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(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):
95 self.dns_keytab = None
98 self.private_dir = None
100 self.slapdconf = None
101 self.modulesconf = None
102 self.memberofconf = None
103 self.fedoradsinf = None
104 self.fedoradspartitions = None
106 self.olmmrserveridsconf = None
107 self.olmmrsyncreplconf = None
110 self.olcseedldif = None
113 class ProvisionNames(object):
119 self.ldapmanagerdn = None
120 self.dnsdomain = None
122 self.netbiosname = None
129 class ProvisionResult(object):
136 class Schema(object):
137 def __init__(self, setup_path, schemadn=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
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)
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",
183 if target is not None:
184 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
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)
192 for i in range (0, len(res)):
193 attributes.append(str(res[i]["lDAPDisplayName"]))
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.
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)
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)
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()
282 setup_add_ldif(ldb, ldif_path, subst_vars)
284 ldb.transaction_cancel()
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.
298 if os.path.exists(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,
330 paths.slapdconf = os.path.join(paths.ldapdir,
332 paths.slapdpid = os.path.join(paths.ldapdir,
334 paths.modulesconf = os.path.join(paths.ldapdir,
336 paths.memberofconf = os.path.join(paths.ldapdir,
338 paths.fedoradsinf = os.path.join(paths.ldapdir,
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,
346 paths.olcdir = os.path.join(paths.ldapdir,
348 paths.olcseedldif = os.path.join(paths.ldapdir,
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
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,
369 """Guess configuration settings to use."""
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":
397 domain = lp.get("workgroup")
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)
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)
417 configdn = "CN=Configuration," + rootdn
419 schemadn = "CN=Schema," + configdn
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
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)
441 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
443 """Create a new smb.conf file based on a couple of basic settings.
445 assert smbconf is not 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":
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))
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),
482 "HOSTNAME": hostname,
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 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
507 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
509 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
510 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
512 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
514 serverrole, ldap_backend=None,
516 """Setup the partitions for the SAM database.
518 Alternatively, provision() may call this, and then populate the database.
520 :note: This will wipe the Sam Database!
522 :note: This function always removes the local SAM LDB file. The erase
523 parameter controls whether to erase the existing data, which
524 may not be stored locally but in LDAP.
526 assert session_info is not None
528 # We use options=["modules:"] to stop the modules loading - we
529 # just want to wipe and re-initialise the database, not start it up
532 samdb = Ldb(url=samdb_path, session_info=session_info,
533 credentials=credentials, lp=lp, options=["modules:"])
535 samdb.erase_except_schema_controlled()
537 os.unlink(samdb_path)
538 samdb = Ldb(url=samdb_path, session_info=session_info,
539 credentials=credentials, lp=lp, options=["modules:"])
541 samdb.erase_except_schema_controlled()
544 #Add modules to the list to activate them by default
545 #beware often order is important
547 # Some Known ordering constraints:
548 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
549 # - objectclass must be before password_hash, because password_hash checks
550 # that the objectclass is of type person (filled in by objectclass
551 # module when expanding the objectclass list)
552 # - partition must be last
553 # - each partition has its own module list then
554 modules_list = ["rootdse",
571 "extended_dn_out_ldb"]
572 modules_list2 = ["show_deleted",
576 domaindn_ldb = "users.ldb"
577 configdn_ldb = "configuration.ldb"
578 schemadn_ldb = "schema.ldb"
579 if ldap_backend is not None:
580 domaindn_ldb = ldap_backend.ldapi_uri
581 configdn_ldb = ldap_backend.ldapi_uri
582 schemadn_ldb = ldap_backend.ldapi_uri
584 if ldap_backend.ldap_backend_type == "fedora-ds":
585 backend_modules = ["nsuniqueid", "paged_searches"]
586 # We can handle linked attributes here, as we don't have directory-side subtree operations
587 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
588 elif ldap_backend.ldap_backend_type == "openldap":
589 backend_modules = ["entryuuid", "paged_searches"]
590 # OpenLDAP handles subtree renames, so we don't want to do any of these things
591 tdb_modules_list = ["extended_dn_out_dereference"]
593 elif serverrole == "domain controller":
594 tdb_modules_list.insert(0, "repl_meta_data")
597 backend_modules = ["objectguid"]
599 if tdb_modules_list is None:
600 tdb_modules_list_as_string = ""
602 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
604 samdb.transaction_start()
606 message("Setting up sam.ldb partitions and settings")
607 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
608 "SCHEMADN": names.schemadn,
609 "SCHEMADN_LDB": schemadn_ldb,
610 "SCHEMADN_MOD2": ",objectguid",
611 "CONFIGDN": names.configdn,
612 "CONFIGDN_LDB": configdn_ldb,
613 "DOMAINDN": names.domaindn,
614 "DOMAINDN_LDB": domaindn_ldb,
615 "SCHEMADN_MOD": "schema_fsmo,instancetype",
616 "CONFIGDN_MOD": "naming_fsmo,instancetype",
617 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
618 "MODULES_LIST": ",".join(modules_list),
619 "TDB_MODULES_LIST": tdb_modules_list_as_string,
620 "MODULES_LIST2": ",".join(modules_list2),
621 "BACKEND_MOD": ",".join(backend_modules),
624 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
626 message("Setting up sam.ldb rootDSE")
627 setup_samdb_rootdse(samdb, setup_path, names)
630 samdb.transaction_cancel()
633 samdb.transaction_commit()
637 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
638 netbiosname, domainsid, keytab_path, samdb_url,
639 dns_keytab_path, dnspass, machinepass):
640 """Add DC-specific bits to a secrets database.
642 :param secretsdb: Ldb Handle to the secrets database
643 :param setup_path: Setup path function
644 :param machinepass: Machine password
646 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
647 "MACHINEPASS_B64": b64encode(machinepass),
650 "DNSDOMAIN": dnsdomain,
651 "DOMAINSID": str(domainsid),
652 "SECRETS_KEYTAB": keytab_path,
653 "NETBIOSNAME": netbiosname,
654 "SAM_LDB": samdb_url,
655 "DNS_KEYTAB": dns_keytab_path,
656 "DNSPASS_B64": b64encode(dnspass),
660 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
661 """Setup the secrets database.
663 :param path: Path to the secrets database.
664 :param setup_path: Get the path to a setup file.
665 :param session_info: Session info.
666 :param credentials: Credentials
667 :param lp: Loadparm context
668 :return: LDB handle for the created secrets database
670 if os.path.exists(path):
672 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
675 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
676 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
678 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
680 if credentials is not None and credentials.authentication_requested():
681 if credentials.get_bind_dn() is not None:
682 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
683 "LDAPMANAGERDN": credentials.get_bind_dn(),
684 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
687 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
688 "LDAPADMINUSER": credentials.get_username(),
689 "LDAPADMINREALM": credentials.get_realm(),
690 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
695 def setup_registry(path, setup_path, session_info, lp):
696 """Setup the registry.
698 :param path: Path to the registry database
699 :param setup_path: Function that returns the path to a setup.
700 :param session_info: Session information
701 :param credentials: Credentials
702 :param lp: Loadparm context
704 reg = registry.Registry()
705 hive = registry.open_ldb(path, session_info=session_info,
707 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
708 provision_reg = setup_path("provision.reg")
709 assert os.path.exists(provision_reg)
710 reg.diff_apply(provision_reg)
713 def setup_idmapdb(path, setup_path, session_info, lp):
714 """Setup the idmap database.
716 :param path: path to the idmap database
717 :param setup_path: Function that returns a path to a setup file
718 :param session_info: Session information
719 :param credentials: Credentials
720 :param lp: Loadparm context
722 if os.path.exists(path):
725 idmap_ldb = IDmapDB(path, session_info=session_info,
729 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
733 def setup_samdb_rootdse(samdb, setup_path, names):
734 """Setup the SamDB rootdse.
736 :param samdb: Sam Database handle
737 :param setup_path: Obtain setup path
739 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
740 "SCHEMADN": names.schemadn,
741 "NETBIOSNAME": names.netbiosname,
742 "DNSDOMAIN": names.dnsdomain,
743 "REALM": names.realm,
744 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
745 "DOMAINDN": names.domaindn,
746 "ROOTDN": names.rootdn,
747 "CONFIGDN": names.configdn,
748 "SERVERDN": names.serverdn,
752 def setup_self_join(samdb, names,
753 machinepass, dnspass,
754 domainsid, invocationid, setup_path,
755 policyguid, domainControllerFunctionality):
756 """Join a host to its own domain."""
757 assert isinstance(invocationid, str)
758 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
759 "CONFIGDN": names.configdn,
760 "SCHEMADN": names.schemadn,
761 "DOMAINDN": names.domaindn,
762 "SERVERDN": names.serverdn,
763 "INVOCATIONID": invocationid,
764 "NETBIOSNAME": names.netbiosname,
765 "DEFAULTSITE": names.sitename,
766 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
767 "MACHINEPASS_B64": b64encode(machinepass),
768 "DNSPASS_B64": b64encode(dnspass),
769 "REALM": names.realm,
770 "DOMAIN": names.domain,
771 "DNSDOMAIN": names.dnsdomain,
772 "SAMBA_VERSION_STRING": version,
773 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
775 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
776 "POLICYGUID": policyguid,
777 "DNSDOMAIN": names.dnsdomain,
778 "DOMAINSID": str(domainsid),
779 "DOMAINDN": names.domaindn})
781 # Setup fSMORoleOwner entries to point at the newly created DC entry
782 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
783 "DOMAINDN": names.domaindn,
784 "CONFIGDN": names.configdn,
785 "SCHEMADN": names.schemadn,
786 "DEFAULTSITE": names.sitename,
787 "SERVERDN": names.serverdn
791 def setup_samdb(path, setup_path, session_info, credentials, lp,
793 domainsid, domainguid, policyguid,
794 fill, adminpass, krbtgtpass,
795 machinepass, invocationid, dnspass,
796 serverrole, schema=None, ldap_backend=None):
797 """Setup a complete SAM Database.
799 :note: This will wipe the main SAM database file!
802 domainFunctionality = DS_BEHAVIOR_WIN2008
803 forestFunctionality = DS_BEHAVIOR_WIN2008
804 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
806 # Also wipes the database
807 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
808 credentials=credentials, session_info=session_info,
810 ldap_backend=ldap_backend, serverrole=serverrole)
813 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
815 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
816 samdb = Ldb(session_info=session_info,
817 credentials=credentials, lp=lp)
819 message("Pre-loading the Samba 4 and AD schema")
821 # Load the schema from the one we computed earlier
822 samdb.set_schema_from_ldb(schema.ldb)
824 # And now we can connect to the DB - the schema won't be loaded from the DB
828 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
833 samdb.transaction_start()
835 message("Erasing data from partitions")
836 # Load the schema (again). This time it will force a reindex,
837 # and will therefore make the erase_partitions() below
838 # computationally sane
839 samdb.set_schema_from_ldb(schema.ldb)
840 samdb.erase_partitions()
842 # Set the domain functionality levels onto the database.
843 # Various module (the password_hash module in particular) need
844 # to know what level of AD we are emulating.
846 # These will be fixed into the database via the database
847 # modifictions below, but we need them set from the start.
848 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
849 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
850 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
852 samdb.set_domain_sid(str(domainsid))
853 if serverrole == "domain controller":
854 samdb.set_invocation_id(invocationid)
856 message("Adding DomainDN: %s" % names.domaindn)
857 if serverrole == "domain controller":
858 domain_oc = "domainDNS"
860 domain_oc = "samba4LocalDomain"
862 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
863 "DOMAINDN": names.domaindn,
864 "DOMAIN_OC": domain_oc
867 message("Modifying DomainDN: " + names.domaindn + "")
868 if domainguid is not None:
869 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
873 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
874 "LDAPTIME": timestring(int(time.time())),
875 "DOMAINSID": str(domainsid),
876 "SCHEMADN": names.schemadn,
877 "NETBIOSNAME": names.netbiosname,
878 "DEFAULTSITE": names.sitename,
879 "CONFIGDN": names.configdn,
880 "SERVERDN": names.serverdn,
881 "POLICYGUID": policyguid,
882 "DOMAINDN": names.domaindn,
883 "DOMAINGUID_MOD": domainguid_mod,
884 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
887 message("Adding configuration container")
888 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
889 "CONFIGDN": names.configdn,
891 message("Modifying configuration container")
892 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
893 "CONFIGDN": names.configdn,
894 "SCHEMADN": names.schemadn,
897 # The LDIF here was created when the Schema object was constructed
898 message("Setting up sam.ldb schema")
899 samdb.add_ldif(schema.schema_dn_add)
900 samdb.modify_ldif(schema.schema_dn_modify)
901 samdb.write_prefixes_from_schema()
902 samdb.add_ldif(schema.schema_data)
903 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
904 {"SCHEMADN": names.schemadn})
906 message("Setting up sam.ldb configuration data")
907 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
908 "CONFIGDN": names.configdn,
909 "NETBIOSNAME": names.netbiosname,
910 "DEFAULTSITE": names.sitename,
911 "DNSDOMAIN": names.dnsdomain,
912 "DOMAIN": names.domain,
913 "SCHEMADN": names.schemadn,
914 "DOMAINDN": names.domaindn,
915 "SERVERDN": names.serverdn,
916 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
919 message("Setting up display specifiers")
920 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
921 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
922 check_all_substituted(display_specifiers_ldif)
923 samdb.add_ldif(display_specifiers_ldif)
925 message("Adding users container")
926 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
927 "DOMAINDN": names.domaindn})
928 message("Modifying users container")
929 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
930 "DOMAINDN": names.domaindn})
931 message("Adding computers container")
932 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
933 "DOMAINDN": names.domaindn})
934 message("Modifying computers container")
935 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
936 "DOMAINDN": names.domaindn})
937 message("Setting up sam.ldb data")
938 setup_add_ldif(samdb, setup_path("provision.ldif"), {
939 "DOMAINDN": names.domaindn,
940 "NETBIOSNAME": names.netbiosname,
941 "DEFAULTSITE": names.sitename,
942 "CONFIGDN": names.configdn,
943 "SERVERDN": names.serverdn
946 if fill == FILL_FULL:
947 message("Setting up sam.ldb users and groups")
948 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
949 "DOMAINDN": names.domaindn,
950 "DOMAINSID": str(domainsid),
951 "CONFIGDN": names.configdn,
952 "ADMINPASS_B64": b64encode(adminpass),
953 "KRBTGTPASS_B64": b64encode(krbtgtpass),
956 if serverrole == "domain controller":
957 message("Setting up self join")
958 setup_self_join(samdb, names=names, invocationid=invocationid,
960 machinepass=machinepass,
961 domainsid=domainsid, policyguid=policyguid,
962 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
965 samdb.transaction_cancel()
968 samdb.transaction_commit()
973 FILL_NT4SYNC = "NT4SYNC"
977 def provision(setup_dir, message, session_info,
978 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
979 rootdn=None, domaindn=None, schemadn=None, configdn=None,
981 domain=None, hostname=None, hostip=None, hostip6=None,
982 domainsid=None, adminpass=None, ldapadminpass=None,
983 krbtgtpass=None, domainguid=None,
984 policyguid=None, invocationid=None, machinepass=None,
985 dnspass=None, root=None, nobody=None, users=None,
986 wheel=None, backup=None, aci=None, serverrole=None,
987 ldap_backend_extra_port=None, ldap_backend_type=None, sitename=None,
988 ol_mmr_urls=None, ol_olc=None,
989 setup_ds_path=None, slapd_path=None, nosync=False,
990 ldap_dryrun_mode=False):
993 :note: caution, this wipes all existing data!
996 def setup_path(file):
997 return os.path.join(setup_dir, file)
999 if domainsid is None:
1000 domainsid = security.random_sid()
1002 if policyguid is None:
1003 policyguid = str(uuid.uuid4())
1004 if adminpass is None:
1005 adminpass = glue.generate_random_str(12)
1006 if krbtgtpass is None:
1007 krbtgtpass = glue.generate_random_str(12)
1008 if machinepass is None:
1009 machinepass = glue.generate_random_str(12)
1011 dnspass = glue.generate_random_str(12)
1012 if ldapadminpass is None:
1013 #Make a new, random password between Samba and it's LDAP server
1014 ldapadminpass=glue.generate_random_str(12)
1017 root_uid = findnss_uid([root or "root"])
1018 nobody_uid = findnss_uid([nobody or "nobody"])
1019 users_gid = findnss_gid([users or "users"])
1021 wheel_gid = findnss_gid(["wheel", "adm"])
1023 wheel_gid = findnss_gid([wheel])
1025 if targetdir is not None:
1026 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1027 os.makedirs(os.path.join(targetdir, "etc"))
1028 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1029 elif smbconf is None:
1030 smbconf = param.default_path()
1032 # only install a new smb.conf if there isn't one there already
1033 if not os.path.exists(smbconf):
1034 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1037 lp = param.LoadParm()
1040 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1041 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1042 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1045 paths = provision_paths_from_lp(lp, names.dnsdomain)
1049 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1050 except socket.gaierror, (socket.EAI_NODATA, msg):
1055 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1056 except socket.gaierror, (socket.EAI_NODATA, msg):
1059 if serverrole is None:
1060 serverrole = lp.get("server role")
1062 assert serverrole in ("domain controller", "member server", "standalone")
1063 if invocationid is None and serverrole == "domain controller":
1064 invocationid = str(uuid.uuid4())
1066 if not os.path.exists(paths.private_dir):
1067 os.mkdir(paths.private_dir)
1069 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1071 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1073 provision_backend = None
1074 if ldap_backend_type:
1075 # We only support an LDAP backend over ldapi://
1077 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, lp=lp, credentials=credentials,
1079 message=message, hostname=hostname,
1080 root=root, schema=schema, ldap_backend_type=ldap_backend_type,
1081 ldapadminpass=ldapadminpass,
1082 ldap_backend_extra_port=ldap_backend_extra_port,
1083 ol_mmr_urls=ol_mmr_urls,
1084 slapd_path=slapd_path,
1085 setup_ds_path=setup_ds_path,
1086 ldap_dryrun_mode=ldap_dryrun_mode)
1088 # Now use the backend credentials to access the databases
1089 credentials = provision_backend.credentials
1091 # only install a new shares config db if there is none
1092 if not os.path.exists(paths.shareconf):
1093 message("Setting up share.ldb")
1094 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1095 credentials=credentials, lp=lp)
1096 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1099 message("Setting up secrets.ldb")
1100 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1101 session_info=session_info,
1102 credentials=credentials, lp=lp)
1104 message("Setting up the registry")
1105 setup_registry(paths.hklm, setup_path, session_info,
1108 message("Setting up idmap db")
1109 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1112 message("Setting up SAM db")
1113 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1114 credentials=credentials, lp=lp, names=names,
1116 domainsid=domainsid,
1117 schema=schema, domainguid=domainguid, policyguid=policyguid,
1119 adminpass=adminpass, krbtgtpass=krbtgtpass,
1120 invocationid=invocationid,
1121 machinepass=machinepass, dnspass=dnspass,
1122 serverrole=serverrole, ldap_backend=provision_backend)
1124 if serverrole == "domain controller":
1125 if paths.netlogon is None:
1126 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1127 message("Please either remove %s or see the template at %s" %
1128 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1129 assert(paths.netlogon is not None)
1131 if paths.sysvol is None:
1132 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1133 message("Please either remove %s or see the template at %s" %
1134 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1135 assert(paths.sysvol is not None)
1137 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1138 "{" + policyguid + "}")
1139 os.makedirs(policy_path, 0755)
1140 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1141 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1142 os.makedirs(os.path.join(policy_path, "User"), 0755)
1143 if not os.path.isdir(paths.netlogon):
1144 os.makedirs(paths.netlogon, 0755)
1146 if samdb_fill == FILL_FULL:
1147 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1148 root_uid=root_uid, nobody_uid=nobody_uid,
1149 users_gid=users_gid, wheel_gid=wheel_gid)
1151 message("Setting up sam.ldb rootDSE marking as synchronized")
1152 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1154 # Only make a zone file on the first DC, it should be replicated with DNS replication
1155 if serverrole == "domain controller":
1156 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1157 credentials=credentials, lp=lp)
1158 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1159 netbiosname=names.netbiosname, domainsid=domainsid,
1160 keytab_path=paths.keytab, samdb_url=paths.samdb,
1161 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1162 machinepass=machinepass, dnsdomain=names.dnsdomain)
1164 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1165 assert isinstance(domainguid, str)
1166 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1167 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1168 scope=SCOPE_SUBTREE)
1169 assert isinstance(hostguid, str)
1171 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1172 domaindn=names.domaindn, hostip=hostip,
1173 hostip6=hostip6, hostname=names.hostname,
1174 dnspass=dnspass, realm=names.realm,
1175 domainguid=domainguid, hostguid=hostguid)
1177 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1178 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1180 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1181 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1182 keytab_name=paths.dns_keytab)
1183 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1184 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1186 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1187 hostname=names.hostname, realm=names.realm)
1188 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1191 # if backend is openldap, terminate slapd after final provision and check its proper termination
1192 if provision_backend is not None and provision_backend.slapd is not None:
1193 if provision_backend.slapd.poll() is None:
1195 if hasattr(provision_backend.slapd, "terminate"):
1196 provision_backend.slapd.terminate()
1199 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1201 #and now wait for it to die
1202 provision_backend.slapd.communicate()
1204 # now display slapd_command_file.txt to show how slapd must be started next time
1205 message("Use later the following commandline to start slapd, then Samba:")
1206 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1207 message(slapd_command)
1208 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1210 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1211 "SLAPD_COMMAND" : slapd_command})
1214 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1217 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1219 message("Once the above files are installed, your Samba4 server will be ready to use")
1220 message("Server Role: %s" % serverrole)
1221 message("Hostname: %s" % names.hostname)
1222 message("NetBIOS Domain: %s" % names.domain)
1223 message("DNS Domain: %s" % names.dnsdomain)
1224 message("DOMAIN SID: %s" % str(domainsid))
1225 if samdb_fill == FILL_FULL:
1226 message("Admin password: %s" % adminpass)
1227 if provision_backend:
1228 if provision_backend.credentials.get_bind_dn() is not None:
1229 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1231 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1233 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1235 result = ProvisionResult()
1236 result.domaindn = domaindn
1237 result.paths = paths
1239 result.samdb = samdb
1244 def provision_become_dc(setup_dir=None,
1245 smbconf=None, targetdir=None, realm=None,
1246 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1248 domain=None, hostname=None, domainsid=None,
1249 adminpass=None, krbtgtpass=None, domainguid=None,
1250 policyguid=None, invocationid=None, machinepass=None,
1251 dnspass=None, root=None, nobody=None, users=None,
1252 wheel=None, backup=None, serverrole=None,
1253 ldap_backend=None, ldap_backend_type=None, sitename=None, debuglevel=1):
1256 """print a message if quiet is not set."""
1259 glue.set_debug_level(debuglevel)
1261 return provision(setup_dir, message, system_session(), None,
1262 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1263 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1264 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1267 def setup_db_config(setup_path, dbdir):
1268 """Setup a Berkeley database.
1270 :param setup_path: Setup path function.
1271 :param dbdir: Database directory."""
1272 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1273 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1274 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1275 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1277 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1278 {"LDAPDBDIR": dbdir})
1280 class ProvisionBackend(object):
1281 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1282 names=None, message=None,
1283 hostname=None, root=None,
1284 schema=None, ldapadminpass=None,
1285 ldap_backend_type=None, ldap_backend_extra_port=None,
1287 setup_ds_path=None, slapd_path=None,
1288 nosync=False, ldap_dryrun_mode=False):
1289 """Provision an LDAP backend for samba4
1291 This works for OpenLDAP and Fedora DS
1294 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1296 if not os.path.isdir(paths.ldapdir):
1297 os.makedirs(paths.ldapdir, 0700)
1299 if ldap_backend_type == "existing":
1300 #Check to see that this 'existing' LDAP backend in fact exists
1301 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1302 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1303 expression="(objectClass=OpenLDAProotDSE)")
1305 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1306 # This caused them to be set into the long-term database later in the script.
1307 self.credentials = credentials
1308 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1311 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1312 # if another instance of slapd is already running
1314 ldapi_db = Ldb(self.ldapi_uri)
1315 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1316 expression="(objectClass=OpenLDAProotDSE)");
1318 f = open(paths.slapdpid, "r")
1321 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1325 raise("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1330 # Try to print helpful messages when the user has not specified the path to slapd
1331 if slapd_path is None:
1332 raise("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1333 if not os.path.exists(slapd_path):
1334 message (slapd_path)
1335 raise("Warning: Given Path to slapd does not exist!")
1337 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1339 os.unlink(schemadb_path)
1344 # Put the LDIF of the schema into a database so we can search on
1345 # it to generate schema-dependent configurations in Fedora DS and
1347 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1348 schema.ldb.connect(schemadb_path)
1349 schema.ldb.transaction_start()
1351 # These bits of LDIF are supplied when the Schema object is created
1352 schema.ldb.add_ldif(schema.schema_dn_add)
1353 schema.ldb.modify_ldif(schema.schema_dn_modify)
1354 schema.ldb.add_ldif(schema.schema_data)
1355 schema.ldb.transaction_commit()
1357 self.credentials = Credentials()
1358 self.credentials.guess(lp)
1359 #Kerberos to an ldapi:// backend makes no sense
1360 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1361 self.ldap_backend_type = ldap_backend_type
1363 if ldap_backend_type == "fedora-ds":
1364 provision_fds_backend(self, paths=paths, setup_path=setup_path, names=names, message=message,
1365 hostname=hostname, ldapadminpass=ldapadminpass, root=root,
1366 schema=schema, ldap_backend_extra_port=ldap_backend_extra_port,
1367 setup_ds_path=setup_ds_path, slapd_path=slapd_path,
1368 nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1370 elif ldap_backend_type == "openldap":
1371 provision_openldap_backend(self, paths=paths, setup_path=setup_path, names=names, message=message,
1372 hostname=hostname, ldapadminpass=ldapadminpass, root=root,
1373 schema=schema, ldap_backend_extra_port=ldap_backend_extra_port,
1374 ol_mmr_urls=ol_mmr_urls,
1375 slapd_path=slapd_path,
1376 nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1378 raise("Unknown LDAP backend type selected")
1380 self.credentials.set_password(ldapadminpass)
1382 # Now start the slapd, so we can provision onto it. We keep the
1383 # subprocess context around, to kill this off at the successful
1385 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1387 while self.slapd.poll() is None:
1388 # Wait until the socket appears
1390 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1391 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1392 expression="(objectClass=OpenLDAProotDSE)")
1393 # If we have got here, then we must have a valid connection to the LDAP server!
1399 raise "slapd died before we could make a connection to it"
1402 def provision_openldap_backend(result, paths=None, setup_path=None, names=None, message=None,
1403 hostname=None, ldapadminpass=None, root=None,
1405 ldap_backend_extra_port=None,
1407 slapd_path=None, nosync=False,
1408 ldap_dryrun_mode=False):
1410 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1413 nosync_config = "dbnosync"
1415 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1416 refint_attributes = ""
1417 memberof_config = "# Generated from Samba4 schema\n"
1418 for att in lnkattr.keys():
1419 if lnkattr[att] is not None:
1420 refint_attributes = refint_attributes + " " + att
1422 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1423 { "MEMBER_ATTR" : att ,
1424 "MEMBEROF_ATTR" : lnkattr[att] })
1426 refint_config = read_and_sub_file(setup_path("refint.conf"),
1427 { "LINK_ATTRS" : refint_attributes})
1429 attrs = ["linkID", "lDAPDisplayName"]
1430 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1432 for i in range (0, len(res)):
1433 index_attr = res[i]["lDAPDisplayName"][0]
1434 if index_attr == "objectGUID":
1435 index_attr = "entryUUID"
1437 index_config += "index " + index_attr + " eq\n"
1439 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1441 mmr_replicator_acl = ""
1442 mmr_serverids_config = ""
1443 mmr_syncrepl_schema_config = ""
1444 mmr_syncrepl_config_config = ""
1445 mmr_syncrepl_user_config = ""
1448 if ol_mmr_urls is not None:
1449 # For now, make these equal
1450 mmr_pass = ldapadminpass
1452 url_list=filter(None,ol_mmr_urls.split(' '))
1453 if (len(url_list) == 1):
1454 url_list=filter(None,ol_mmr_urls.split(','))
1457 mmr_on_config = "MirrorMode On"
1458 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1460 for url in url_list:
1462 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1463 { "SERVERID" : str(serverid),
1464 "LDAPSERVER" : url })
1467 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1469 "MMRDN": names.schemadn,
1471 "MMR_PASSWORD": mmr_pass})
1474 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1476 "MMRDN": names.configdn,
1478 "MMR_PASSWORD": mmr_pass})
1481 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1483 "MMRDN": names.domaindn,
1485 "MMR_PASSWORD": mmr_pass })
1486 # OpenLDAP cn=config initialisation
1487 olc_syncrepl_config = ""
1489 # if mmr = yes, generate cn=config-replication directives
1490 # and olc_seed.lif for the other mmr-servers
1491 if ol_mmr_urls is not None:
1493 olc_serverids_config = ""
1494 olc_syncrepl_seed_config = ""
1495 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1497 for url in url_list:
1499 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1500 { "SERVERID" : str(serverid),
1501 "LDAPSERVER" : url })
1504 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1507 "MMR_PASSWORD": mmr_pass})
1509 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1511 "LDAPSERVER" : url})
1513 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1514 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1515 "OLC_PW": ldapadminpass,
1516 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1519 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1520 {"DNSDOMAIN": names.dnsdomain,
1521 "LDAPDIR": paths.ldapdir,
1522 "DOMAINDN": names.domaindn,
1523 "CONFIGDN": names.configdn,
1524 "SCHEMADN": names.schemadn,
1525 "MEMBEROF_CONFIG": memberof_config,
1526 "MIRRORMODE": mmr_on_config,
1527 "REPLICATOR_ACL": mmr_replicator_acl,
1528 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1529 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1530 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1531 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1532 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1533 "OLC_MMR_CONFIG": olc_mmr_config,
1534 "REFINT_CONFIG": refint_config,
1535 "INDEX_CONFIG": index_config,
1536 "NOSYNC": nosync_config})
1538 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1539 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1540 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1542 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1543 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1545 setup_file(setup_path("cn=samba.ldif"),
1546 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1547 { "UUID": str(uuid.uuid4()),
1548 "LDAPTIME": timestring(int(time.time()))} )
1549 setup_file(setup_path("cn=samba-admin.ldif"),
1550 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1551 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1552 "UUID": str(uuid.uuid4()),
1553 "LDAPTIME": timestring(int(time.time()))} )
1555 if ol_mmr_urls is not None:
1556 setup_file(setup_path("cn=replicator.ldif"),
1557 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1558 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1559 "UUID": str(uuid.uuid4()),
1560 "LDAPTIME": timestring(int(time.time()))} )
1563 mapping = "schema-map-openldap-2.3"
1564 backend_schema = "backend-schema.schema"
1566 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1567 assert backend_schema_data is not None
1568 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1570 # now we generate the needed strings to start slapd automatically,
1571 # first ldapi_uri...
1572 if ldap_backend_extra_port is not None:
1573 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1574 # specified there as part of it's clue as to it's own name,
1575 # and not to replicate to itself
1576 if ol_mmr_urls is None:
1577 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1579 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1581 server_port_string = ""
1583 # Prepare the 'result' information - the commands to return in particular
1584 result.slapd_provision_command = [slapd_path]
1586 result.slapd_provision_command.append("-F" + paths.olcdir)
1588 result.slapd_provision_command.append("-h")
1590 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1591 result.slapd_command = list(result.slapd_provision_command)
1593 result.slapd_provision_command.append(result.ldapi_uri)
1594 result.slapd_provision_command.append("-d0")
1596 uris = result.ldapi_uri
1597 if server_port_string is not "":
1598 uris = uris + " " + server_port_string
1600 result.slapd_command.append(uris)
1602 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1603 result.credentials.set_username("samba-admin")
1605 # If we were just looking for crashes up to this point, it's a
1606 # good time to exit before we realise we don't have OpenLDAP on
1608 if ldap_dryrun_mode:
1611 # Finally, convert the configuration into cn=config style!
1612 if not os.path.isdir(paths.olcdir):
1613 os.makedirs(paths.olcdir, 0770)
1615 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1617 # We can't do this, as OpenLDAP is strange. It gives an error
1618 # output to the above, but does the conversion sucessfully...
1621 # raise("conversion from slapd.conf to cn=config failed")
1623 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1624 raise("conversion from slapd.conf to cn=config failed")
1626 # Don't confuse the admin by leaving the slapd.conf around
1627 os.remove(paths.slapdconf)
1630 def provision_fds_backend(result, paths=None, setup_path=None, names=None, message=None,
1631 hostname=None, ldapadminpass=None, root=None,
1633 ldap_backend_extra_port=None,
1637 ldap_dryrun_mode=False):
1639 if ldap_backend_extra_port is not None:
1640 serverport = "ServerPort=%d" % ldap_backend_extra_port
1644 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1646 "HOSTNAME": hostname,
1647 "DNSDOMAIN": names.dnsdomain,
1648 "LDAPDIR": paths.ldapdir,
1649 "DOMAINDN": names.domaindn,
1650 "LDAPMANAGERDN": names.ldapmanagerdn,
1651 "LDAPMANAGERPASS": ldapadminpass,
1652 "SERVERPORT": serverport})
1654 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1655 {"CONFIGDN": names.configdn,
1656 "SCHEMADN": names.schemadn,
1659 mapping = "schema-map-fedora-ds-1.0"
1660 backend_schema = "99_ad.ldif"
1662 # Build a schema file in Fedora DS format
1663 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1664 assert backend_schema_data is not None
1665 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1667 result.credentials.set_bind_dn(names.ldapmanagerdn)
1669 # Destory the target directory, or else setup-ds.pl will complain
1670 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1671 shutil.rmtree(fedora_ds_dir, True)
1673 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1674 #In the 'provision' command line, stay in the foreground so we can easily kill it
1675 result.slapd_provision_command.append("-d0")
1677 #the command for the final run is the normal script
1678 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1680 # If we were just looking for crashes up to this point, it's a
1681 # good time to exit before we realise we don't have Fedora DS on
1682 if ldap_dryrun_mode:
1685 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1686 if setup_ds_path is None:
1687 raise("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1688 if not os.path.exists(setup_ds_path):
1689 message (setup_ds_path)
1690 raise("Warning: Given Path to slapd does not exist!")
1692 # Run the Fedora DS setup utility
1693 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1695 raise("setup-ds failed")
1697 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1698 """Create a PHP LDAP admin configuration file.
1700 :param path: Path to write the configuration to.
1701 :param setup_path: Function to generate setup paths.
1703 setup_file(setup_path("phpldapadmin-config.php"), path,
1704 {"S4_LDAPI_URI": ldapi_uri})
1707 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1708 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1709 """Write out a DNS zone file, from the info in the current database.
1711 :param path: Path of the new zone file.
1712 :param setup_path: Setup path function.
1713 :param dnsdomain: DNS Domain name
1714 :param domaindn: DN of the Domain
1715 :param hostip: Local IPv4 IP
1716 :param hostip6: Local IPv6 IP
1717 :param hostname: Local hostname
1718 :param dnspass: Password for DNS
1719 :param realm: Realm name
1720 :param domainguid: GUID of the domain.
1721 :param hostguid: GUID of the host.
1723 assert isinstance(domainguid, str)
1725 if hostip6 is not None:
1726 hostip6_base_line = " IN AAAA " + hostip6
1727 hostip6_host_line = hostname + " IN AAAA " + hostip6
1729 hostip6_base_line = ""
1730 hostip6_host_line = ""
1732 if hostip is not None:
1733 hostip_base_line = " IN A " + hostip
1734 hostip_host_line = hostname + " IN A " + hostip
1736 hostip_base_line = ""
1737 hostip_host_line = ""
1739 setup_file(setup_path("provision.zone"), path, {
1740 "DNSPASS_B64": b64encode(dnspass),
1741 "HOSTNAME": hostname,
1742 "DNSDOMAIN": dnsdomain,
1744 "HOSTIP_BASE_LINE": hostip_base_line,
1745 "HOSTIP_HOST_LINE": hostip_host_line,
1746 "DOMAINGUID": domainguid,
1747 "DATESTRING": time.strftime("%Y%m%d%H"),
1748 "DEFAULTSITE": DEFAULTSITE,
1749 "HOSTGUID": hostguid,
1750 "HOSTIP6_BASE_LINE": hostip6_base_line,
1751 "HOSTIP6_HOST_LINE": hostip6_host_line,
1755 def create_named_conf(path, setup_path, realm, dnsdomain,
1757 """Write out a file containing zone statements suitable for inclusion in a
1758 named.conf file (including GSS-TSIG configuration).
1760 :param path: Path of the new named.conf file.
1761 :param setup_path: Setup path function.
1762 :param realm: Realm name
1763 :param dnsdomain: DNS Domain name
1764 :param private_dir: Path to private directory
1765 :param keytab_name: File name of DNS keytab file
1768 setup_file(setup_path("named.conf"), path, {
1769 "DNSDOMAIN": dnsdomain,
1771 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1772 "PRIVATE_DIR": private_dir
1775 def create_named_txt(path, setup_path, realm, dnsdomain,
1776 private_dir, keytab_name):
1777 """Write out a file containing zone statements suitable for inclusion in a
1778 named.conf file (including GSS-TSIG configuration).
1780 :param path: Path of the new named.conf file.
1781 :param setup_path: Setup path function.
1782 :param realm: Realm name
1783 :param dnsdomain: DNS Domain name
1784 :param private_dir: Path to private directory
1785 :param keytab_name: File name of DNS keytab file
1788 setup_file(setup_path("named.txt"), path, {
1789 "DNSDOMAIN": dnsdomain,
1791 "DNS_KEYTAB": keytab_name,
1792 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1793 "PRIVATE_DIR": private_dir
1796 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1797 """Write out a file containing zone statements suitable for inclusion in a
1798 named.conf file (including GSS-TSIG configuration).
1800 :param path: Path of the new named.conf file.
1801 :param setup_path: Setup path function.
1802 :param dnsdomain: DNS Domain name
1803 :param hostname: Local hostname
1804 :param realm: Realm name
1807 setup_file(setup_path("krb5.conf"), path, {
1808 "DNSDOMAIN": dnsdomain,
1809 "HOSTNAME": hostname,