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, admin_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"
58 class ProvisioningError(ValueError):
63 """Find the setup directory used by provision."""
64 dirname = os.path.dirname(__file__)
65 if "/site-packages/" in dirname:
66 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
67 for suffix in ["share/setup", "share/samba/setup", "setup"]:
68 ret = os.path.join(prefix, suffix)
69 if os.path.isdir(ret):
72 ret = os.path.join(dirname, "../../../setup")
73 if os.path.isdir(ret):
75 raise Exception("Unable to find setup directory.")
78 DEFAULTSITE = "Default-First-Site-Name"
80 class InvalidNetbiosName(Exception):
81 """A specified name was not a valid NetBIOS name."""
82 def __init__(self, name):
83 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
86 class ProvisionPaths(object):
99 self.dns_keytab = None
102 self.private_dir = None
104 self.slapdconf = None
105 self.modulesconf = None
106 self.memberofconf = None
107 self.fedoradsinf = None
108 self.fedoradspartitions = None
110 self.olmmrserveridsconf = None
111 self.olmmrsyncreplconf = None
114 self.olcseedldif = None
117 class ProvisionNames(object):
123 self.ldapmanagerdn = None
124 self.dnsdomain = None
126 self.netbiosname = None
133 class ProvisionResult(object):
140 class Schema(object):
141 def __init__(self, setup_path, schemadn=None,
143 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
145 :param samdb: Load a schema into a SamDB.
146 :param setup_path: Setup path function.
147 :param schemadn: DN of the schema
148 :param serverdn: DN of the server
150 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
154 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
155 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
156 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
157 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
158 check_all_substituted(self.schema_data)
160 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
161 {"SCHEMADN": schemadn,
162 "SERVERDN": serverdn,
164 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
165 {"SCHEMADN": schemadn
168 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
169 prefixmap = b64encode(prefixmap)
171 # We don't actually add this ldif, just parse it
172 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
173 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
176 # Return a hash with the forward attribute as a key and the back as the value
177 def get_linked_attributes(schemadn,schemaldb):
178 attrs = ["linkID", "lDAPDisplayName"]
179 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)
181 for i in range (0, len(res)):
182 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
183 target = schemaldb.searchone(basedn=schemadn,
184 expression=expression,
185 attribute="lDAPDisplayName",
187 if target is not None:
188 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
192 def get_dnsyntax_attributes(schemadn,schemaldb):
193 attrs = ["linkID", "lDAPDisplayName"]
194 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
196 for i in range (0, len(res)):
197 attributes.append(str(res[i]["lDAPDisplayName"]))
202 def check_install(lp, session_info, credentials):
203 """Check whether the current install seems ok.
205 :param lp: Loadparm context
206 :param session_info: Session information
207 :param credentials: Credentials
209 if lp.get("realm") == "":
210 raise Exception("Realm empty")
211 ldb = Ldb(lp.get("sam database"), session_info=session_info,
212 credentials=credentials, lp=lp)
213 if len(ldb.search("(cn=Administrator)")) != 1:
214 raise ProvisioningError("No administrator account found")
217 def findnss(nssfn, names):
218 """Find a user or group from a list of possibilities.
220 :param nssfn: NSS Function to try (should raise KeyError if not found)
221 :param names: Names to check.
222 :return: Value return by first names list.
229 raise KeyError("Unable to find user/group %r" % names)
232 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
233 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
236 def read_and_sub_file(file, subst_vars):
237 """Read a file and sub in variables found in it
239 :param file: File to be read (typically from setup directory)
240 param subst_vars: Optional variables to subsitute in the file.
242 data = open(file, 'r').read()
243 if subst_vars is not None:
244 data = substitute_var(data, subst_vars)
245 check_all_substituted(data)
249 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
250 """Setup a ldb in the private dir.
252 :param ldb: LDB file to import data into
253 :param ldif_path: Path of the LDIF file to load
254 :param subst_vars: Optional variables to subsitute in LDIF.
256 assert isinstance(ldif_path, str)
258 data = read_and_sub_file(ldif_path, subst_vars)
262 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
263 """Modify a ldb in the private dir.
265 :param ldb: LDB object.
266 :param ldif_path: LDIF file path.
267 :param subst_vars: Optional dictionary with substitution variables.
269 data = read_and_sub_file(ldif_path, subst_vars)
271 ldb.modify_ldif(data)
274 def setup_ldb(ldb, ldif_path, subst_vars):
275 """Import a LDIF a file into a LDB handle, optionally substituting variables.
277 :note: Either all LDIF data will be added or none (using transactions).
279 :param ldb: LDB file to import into.
280 :param ldif_path: Path to the LDIF file.
281 :param subst_vars: Dictionary with substitution variables.
283 assert ldb is not None
284 ldb.transaction_start()
286 setup_add_ldif(ldb, ldif_path, subst_vars)
288 ldb.transaction_cancel()
290 ldb.transaction_commit()
293 def setup_file(template, fname, subst_vars):
294 """Setup a file in the private dir.
296 :param template: Path of the template file.
297 :param fname: Path of the file to create.
298 :param subst_vars: Substitution variables.
302 if os.path.exists(f):
305 data = read_and_sub_file(template, subst_vars)
306 open(f, 'w').write(data)
309 def provision_paths_from_lp(lp, dnsdomain):
310 """Set the default paths for provisioning.
312 :param lp: Loadparm context.
313 :param dnsdomain: DNS Domain name
315 paths = ProvisionPaths()
316 paths.private_dir = lp.get("private dir")
317 paths.keytab = "secrets.keytab"
318 paths.dns_keytab = "dns.keytab"
320 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
321 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
322 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
323 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
324 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
325 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
326 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
327 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
328 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
329 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
330 paths.phpldapadminconfig = os.path.join(paths.private_dir,
331 "phpldapadmin-config.php")
332 paths.ldapdir = os.path.join(paths.private_dir,
334 paths.slapdconf = os.path.join(paths.ldapdir,
336 paths.slapdpid = os.path.join(paths.ldapdir,
338 paths.modulesconf = os.path.join(paths.ldapdir,
340 paths.memberofconf = os.path.join(paths.ldapdir,
342 paths.fedoradsinf = os.path.join(paths.ldapdir,
344 paths.fedoradspartitions = os.path.join(paths.ldapdir,
345 "fedorads-partitions.ldif")
346 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
347 "mmr_serverids.conf")
348 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
350 paths.olcdir = os.path.join(paths.ldapdir,
352 paths.olcseedldif = os.path.join(paths.ldapdir,
354 paths.hklm = "hklm.ldb"
355 paths.hkcr = "hkcr.ldb"
356 paths.hkcu = "hkcu.ldb"
357 paths.hku = "hku.ldb"
358 paths.hkpd = "hkpd.ldb"
359 paths.hkpt = "hkpt.ldb"
361 paths.sysvol = lp.get("path", "sysvol")
363 paths.netlogon = lp.get("path", "netlogon")
365 paths.smbconf = lp.configfile
370 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
371 serverrole=None, rootdn=None, domaindn=None, configdn=None,
372 schemadn=None, serverdn=None, sitename=None):
373 """Guess configuration settings to use."""
376 hostname = socket.gethostname().split(".")[0].lower()
378 netbiosname = hostname.upper()
379 if not valid_netbios_name(netbiosname):
380 raise InvalidNetbiosName(netbiosname)
382 hostname = hostname.lower()
384 if dnsdomain is None:
385 dnsdomain = lp.get("realm")
387 if serverrole is None:
388 serverrole = lp.get("server role")
390 assert dnsdomain is not None
391 realm = dnsdomain.upper()
393 if lp.get("realm").upper() != realm:
394 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
395 (lp.get("realm"), lp.configfile, realm))
397 dnsdomain = dnsdomain.lower()
399 if serverrole == "domain controller":
401 domain = lp.get("workgroup")
403 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
404 if lp.get("workgroup").upper() != domain.upper():
405 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
406 lp.get("workgroup"), domain)
410 domaindn = "CN=" + netbiosname
412 assert domain is not None
413 domain = domain.upper()
414 if not valid_netbios_name(domain):
415 raise InvalidNetbiosName(domain)
421 configdn = "CN=Configuration," + rootdn
423 schemadn = "CN=Schema," + configdn
428 names = ProvisionNames()
429 names.rootdn = rootdn
430 names.domaindn = domaindn
431 names.configdn = configdn
432 names.schemadn = schemadn
433 names.ldapmanagerdn = "CN=Manager," + rootdn
434 names.dnsdomain = dnsdomain
435 names.domain = domain
437 names.netbiosname = netbiosname
438 names.hostname = hostname
439 names.sitename = sitename
440 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
445 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
447 """Create a new smb.conf file based on a couple of basic settings.
449 assert smbconf is not None
451 hostname = socket.gethostname().split(".")[0].lower()
453 if serverrole is None:
454 serverrole = "standalone"
456 assert serverrole in ("domain controller", "member server", "standalone")
457 if serverrole == "domain controller":
459 elif serverrole == "member server":
460 smbconfsuffix = "member"
461 elif serverrole == "standalone":
462 smbconfsuffix = "standalone"
464 assert domain is not None
465 assert realm is not None
467 default_lp = param.LoadParm()
468 #Load non-existant file
469 if os.path.exists(smbconf):
470 default_lp.load(smbconf)
472 if targetdir is not None:
473 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
474 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
476 default_lp.set("lock dir", os.path.abspath(targetdir))
481 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
482 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
484 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
486 "HOSTNAME": hostname,
489 "SERVERROLE": serverrole,
490 "NETLOGONPATH": netlogon,
491 "SYSVOLPATH": sysvol,
492 "PRIVATEDIR_LINE": privatedir_line,
493 "LOCKDIR_LINE": lockdir_line
497 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
498 users_gid, wheel_gid):
499 """setup reasonable name mappings for sam names to unix names.
501 :param samdb: SamDB object.
502 :param idmap: IDmap db object.
503 :param sid: The domain sid.
504 :param domaindn: The domain DN.
505 :param root_uid: uid of the UNIX root user.
506 :param nobody_uid: uid of the UNIX nobody user.
507 :param users_gid: gid of the UNIX users group.
508 :param wheel_gid: gid of the UNIX wheel group."""
510 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
511 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
513 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
514 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
516 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
518 serverrole, ldap_backend=None,
520 """Setup the partitions for the SAM database.
522 Alternatively, provision() may call this, and then populate the database.
524 :note: This will wipe the Sam Database!
526 :note: This function always removes the local SAM LDB file. The erase
527 parameter controls whether to erase the existing data, which
528 may not be stored locally but in LDAP.
530 assert session_info is not None
532 # We use options=["modules:"] to stop the modules loading - we
533 # just want to wipe and re-initialise the database, not start it up
536 samdb = Ldb(url=samdb_path, session_info=session_info,
537 credentials=credentials, lp=lp, options=["modules:"])
539 samdb.erase_except_schema_controlled()
541 os.unlink(samdb_path)
542 samdb = Ldb(url=samdb_path, session_info=session_info,
543 credentials=credentials, lp=lp, options=["modules:"])
545 samdb.erase_except_schema_controlled()
548 #Add modules to the list to activate them by default
549 #beware often order is important
551 # Some Known ordering constraints:
552 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
553 # - objectclass must be before password_hash, because password_hash checks
554 # that the objectclass is of type person (filled in by objectclass
555 # module when expanding the objectclass list)
556 # - partition must be last
557 # - each partition has its own module list then
558 modules_list = ["rootdse",
575 "extended_dn_out_ldb"]
576 modules_list2 = ["show_deleted",
580 domaindn_ldb = "users.ldb"
581 configdn_ldb = "configuration.ldb"
582 schemadn_ldb = "schema.ldb"
583 if ldap_backend is not None:
584 domaindn_ldb = ldap_backend.ldapi_uri
585 configdn_ldb = ldap_backend.ldapi_uri
586 schemadn_ldb = ldap_backend.ldapi_uri
588 if ldap_backend.ldap_backend_type == "fedora-ds":
589 backend_modules = ["nsuniqueid", "paged_searches"]
590 # We can handle linked attributes here, as we don't have directory-side subtree operations
591 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
592 elif ldap_backend.ldap_backend_type == "openldap":
593 backend_modules = ["entryuuid", "paged_searches"]
594 # OpenLDAP handles subtree renames, so we don't want to do any of these things
595 tdb_modules_list = ["extended_dn_out_dereference"]
597 elif serverrole == "domain controller":
598 tdb_modules_list.insert(0, "repl_meta_data")
601 backend_modules = ["objectguid"]
603 if tdb_modules_list is None:
604 tdb_modules_list_as_string = ""
606 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
608 samdb.transaction_start()
610 message("Setting up sam.ldb partitions and settings")
611 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
612 "SCHEMADN": names.schemadn,
613 "SCHEMADN_LDB": schemadn_ldb,
614 "SCHEMADN_MOD2": ",objectguid",
615 "CONFIGDN": names.configdn,
616 "CONFIGDN_LDB": configdn_ldb,
617 "DOMAINDN": names.domaindn,
618 "DOMAINDN_LDB": domaindn_ldb,
619 "SCHEMADN_MOD": "schema_fsmo,instancetype",
620 "CONFIGDN_MOD": "naming_fsmo,instancetype",
621 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
622 "MODULES_LIST": ",".join(modules_list),
623 "TDB_MODULES_LIST": tdb_modules_list_as_string,
624 "MODULES_LIST2": ",".join(modules_list2),
625 "BACKEND_MOD": ",".join(backend_modules),
628 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
630 message("Setting up sam.ldb rootDSE")
631 setup_samdb_rootdse(samdb, setup_path, names)
634 samdb.transaction_cancel()
637 samdb.transaction_commit()
641 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
642 netbiosname, domainsid, keytab_path, samdb_url,
643 dns_keytab_path, dnspass, machinepass):
644 """Add DC-specific bits to a secrets database.
646 :param secretsdb: Ldb Handle to the secrets database
647 :param setup_path: Setup path function
648 :param machinepass: Machine password
650 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
651 "MACHINEPASS_B64": b64encode(machinepass),
654 "DNSDOMAIN": dnsdomain,
655 "DOMAINSID": str(domainsid),
656 "SECRETS_KEYTAB": keytab_path,
657 "NETBIOSNAME": netbiosname,
658 "SAM_LDB": samdb_url,
659 "DNS_KEYTAB": dns_keytab_path,
660 "DNSPASS_B64": b64encode(dnspass),
664 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
665 """Setup the secrets database.
667 :param path: Path to the secrets database.
668 :param setup_path: Get the path to a setup file.
669 :param session_info: Session info.
670 :param credentials: Credentials
671 :param lp: Loadparm context
672 :return: LDB handle for the created secrets database
674 if os.path.exists(path):
676 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
679 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
680 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
682 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
684 if credentials is not None and credentials.authentication_requested():
685 if credentials.get_bind_dn() is not None:
686 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
687 "LDAPMANAGERDN": credentials.get_bind_dn(),
688 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
691 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
692 "LDAPADMINUSER": credentials.get_username(),
693 "LDAPADMINREALM": credentials.get_realm(),
694 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
699 def setup_registry(path, setup_path, session_info, lp):
700 """Setup the registry.
702 :param path: Path to the registry database
703 :param setup_path: Function that returns the path to a setup.
704 :param session_info: Session information
705 :param credentials: Credentials
706 :param lp: Loadparm context
708 reg = registry.Registry()
709 hive = registry.open_ldb(path, session_info=session_info,
711 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
712 provision_reg = setup_path("provision.reg")
713 assert os.path.exists(provision_reg)
714 reg.diff_apply(provision_reg)
717 def setup_idmapdb(path, setup_path, session_info, lp):
718 """Setup the idmap database.
720 :param path: path to the idmap database
721 :param setup_path: Function that returns a path to a setup file
722 :param session_info: Session information
723 :param credentials: Credentials
724 :param lp: Loadparm context
726 if os.path.exists(path):
729 idmap_ldb = IDmapDB(path, session_info=session_info,
733 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
737 def setup_samdb_rootdse(samdb, setup_path, names):
738 """Setup the SamDB rootdse.
740 :param samdb: Sam Database handle
741 :param setup_path: Obtain setup path
743 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
744 "SCHEMADN": names.schemadn,
745 "NETBIOSNAME": names.netbiosname,
746 "DNSDOMAIN": names.dnsdomain,
747 "REALM": names.realm,
748 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
749 "DOMAINDN": names.domaindn,
750 "ROOTDN": names.rootdn,
751 "CONFIGDN": names.configdn,
752 "SERVERDN": names.serverdn,
756 def setup_self_join(samdb, names,
757 machinepass, dnspass,
758 domainsid, invocationid, setup_path,
759 policyguid, domainControllerFunctionality):
760 """Join a host to its own domain."""
761 assert isinstance(invocationid, str)
762 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
763 "CONFIGDN": names.configdn,
764 "SCHEMADN": names.schemadn,
765 "DOMAINDN": names.domaindn,
766 "SERVERDN": names.serverdn,
767 "INVOCATIONID": invocationid,
768 "NETBIOSNAME": names.netbiosname,
769 "DEFAULTSITE": names.sitename,
770 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
771 "MACHINEPASS_B64": b64encode(machinepass),
772 "DNSPASS_B64": b64encode(dnspass),
773 "REALM": names.realm,
774 "DOMAIN": names.domain,
775 "DNSDOMAIN": names.dnsdomain,
776 "SAMBA_VERSION_STRING": version,
777 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
779 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
780 "POLICYGUID": policyguid,
781 "DNSDOMAIN": names.dnsdomain,
782 "DOMAINSID": str(domainsid),
783 "DOMAINDN": names.domaindn})
785 # Setup fSMORoleOwner entries to point at the newly created DC entry
786 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
787 "DOMAINDN": names.domaindn,
788 "CONFIGDN": names.configdn,
789 "SCHEMADN": names.schemadn,
790 "DEFAULTSITE": names.sitename,
791 "SERVERDN": names.serverdn
795 def setup_samdb(path, setup_path, session_info, credentials, lp,
797 domainsid, domainguid, policyguid,
798 fill, adminpass, krbtgtpass,
799 machinepass, invocationid, dnspass,
800 serverrole, schema=None, ldap_backend=None):
801 """Setup a complete SAM Database.
803 :note: This will wipe the main SAM database file!
806 domainFunctionality = DS_BEHAVIOR_WIN2008
807 forestFunctionality = DS_BEHAVIOR_WIN2008
808 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
810 # Also wipes the database
811 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
812 credentials=credentials, session_info=session_info,
814 ldap_backend=ldap_backend, serverrole=serverrole)
817 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
819 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
820 samdb = Ldb(session_info=session_info,
821 credentials=credentials, lp=lp)
823 message("Pre-loading the Samba 4 and AD schema")
825 # Load the schema from the one we computed earlier
826 samdb.set_schema_from_ldb(schema.ldb)
828 # And now we can connect to the DB - the schema won't be loaded from the DB
832 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
837 samdb.transaction_start()
839 message("Erasing data from partitions")
840 # Load the schema (again). This time it will force a reindex,
841 # and will therefore make the erase_partitions() below
842 # computationally sane
843 samdb.set_schema_from_ldb(schema.ldb)
844 samdb.erase_partitions()
846 # Set the domain functionality levels onto the database.
847 # Various module (the password_hash module in particular) need
848 # to know what level of AD we are emulating.
850 # These will be fixed into the database via the database
851 # modifictions below, but we need them set from the start.
852 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
853 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
854 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
856 samdb.set_domain_sid(str(domainsid))
857 if serverrole == "domain controller":
858 samdb.set_invocation_id(invocationid)
860 message("Adding DomainDN: %s" % names.domaindn)
861 if serverrole == "domain controller":
862 domain_oc = "domainDNS"
864 domain_oc = "samba4LocalDomain"
866 #impersonate domain admin
867 admin_session_info = admin_session(lp, str(domainsid))
868 samdb.set_session_info(admin_session_info)
870 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
871 "DOMAINDN": names.domaindn,
872 "DOMAIN_OC": domain_oc
875 message("Modifying DomainDN: " + names.domaindn + "")
876 if domainguid is not None:
877 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
881 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
882 "LDAPTIME": timestring(int(time.time())),
883 "DOMAINSID": str(domainsid),
884 "SCHEMADN": names.schemadn,
885 "NETBIOSNAME": names.netbiosname,
886 "DEFAULTSITE": names.sitename,
887 "CONFIGDN": names.configdn,
888 "SERVERDN": names.serverdn,
889 "POLICYGUID": policyguid,
890 "DOMAINDN": names.domaindn,
891 "DOMAINGUID_MOD": domainguid_mod,
892 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
895 message("Adding configuration container")
896 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
897 "CONFIGDN": names.configdn,
899 message("Modifying configuration container")
900 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
901 "CONFIGDN": names.configdn,
902 "SCHEMADN": names.schemadn,
905 # The LDIF here was created when the Schema object was constructed
906 message("Setting up sam.ldb schema")
907 samdb.add_ldif(schema.schema_dn_add)
908 samdb.modify_ldif(schema.schema_dn_modify)
909 samdb.write_prefixes_from_schema()
910 samdb.add_ldif(schema.schema_data)
911 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
912 {"SCHEMADN": names.schemadn})
914 message("Setting up sam.ldb configuration data")
915 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
916 "CONFIGDN": names.configdn,
917 "NETBIOSNAME": names.netbiosname,
918 "DEFAULTSITE": names.sitename,
919 "DNSDOMAIN": names.dnsdomain,
920 "DOMAIN": names.domain,
921 "SCHEMADN": names.schemadn,
922 "DOMAINDN": names.domaindn,
923 "SERVERDN": names.serverdn,
924 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
927 message("Setting up display specifiers")
928 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
929 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
930 check_all_substituted(display_specifiers_ldif)
931 samdb.add_ldif(display_specifiers_ldif)
933 message("Adding users container")
934 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
935 "DOMAINDN": names.domaindn})
936 message("Modifying users container")
937 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
938 "DOMAINDN": names.domaindn})
939 message("Adding computers container")
940 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
941 "DOMAINDN": names.domaindn})
942 message("Modifying computers container")
943 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
944 "DOMAINDN": names.domaindn})
945 message("Setting up sam.ldb data")
946 setup_add_ldif(samdb, setup_path("provision.ldif"), {
947 "DOMAINDN": names.domaindn,
948 "NETBIOSNAME": names.netbiosname,
949 "DEFAULTSITE": names.sitename,
950 "CONFIGDN": names.configdn,
951 "SERVERDN": names.serverdn
954 if fill == FILL_FULL:
955 message("Setting up sam.ldb users and groups")
956 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
957 "DOMAINDN": names.domaindn,
958 "DOMAINSID": str(domainsid),
959 "CONFIGDN": names.configdn,
960 "ADMINPASS_B64": b64encode(adminpass),
961 "KRBTGTPASS_B64": b64encode(krbtgtpass),
964 if serverrole == "domain controller":
965 message("Setting up self join")
966 setup_self_join(samdb, names=names, invocationid=invocationid,
968 machinepass=machinepass,
969 domainsid=domainsid, policyguid=policyguid,
970 setup_path=setup_path,
971 domainControllerFunctionality=domainControllerFunctionality)
974 samdb.transaction_cancel()
977 samdb.transaction_commit()
982 FILL_NT4SYNC = "NT4SYNC"
986 def provision(setup_dir, message, session_info,
987 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
989 rootdn=None, domaindn=None, schemadn=None, configdn=None,
991 domain=None, hostname=None, hostip=None, hostip6=None,
992 domainsid=None, adminpass=None, ldapadminpass=None,
993 krbtgtpass=None, domainguid=None,
994 policyguid=None, invocationid=None, machinepass=None,
995 dnspass=None, root=None, nobody=None, users=None,
996 wheel=None, backup=None, aci=None, serverrole=None,
997 ldap_backend_extra_port=None, ldap_backend_type=None,
999 ol_mmr_urls=None, ol_olc=None,
1000 setup_ds_path=None, slapd_path=None, nosync=False,
1001 ldap_dryrun_mode=False):
1004 :note: caution, this wipes all existing data!
1007 def setup_path(file):
1008 return os.path.join(setup_dir, file)
1010 if domainsid is None:
1011 domainsid = security.random_sid()
1013 if policyguid is None:
1014 policyguid = str(uuid.uuid4())
1015 if adminpass is None:
1016 adminpass = glue.generate_random_str(12)
1017 if krbtgtpass is None:
1018 krbtgtpass = glue.generate_random_str(12)
1019 if machinepass is None:
1020 machinepass = glue.generate_random_str(12)
1022 dnspass = glue.generate_random_str(12)
1023 if ldapadminpass is None:
1024 #Make a new, random password between Samba and it's LDAP server
1025 ldapadminpass=glue.generate_random_str(12)
1028 root_uid = findnss_uid([root or "root"])
1029 nobody_uid = findnss_uid([nobody or "nobody"])
1030 users_gid = findnss_gid([users or "users"])
1032 wheel_gid = findnss_gid(["wheel", "adm"])
1034 wheel_gid = findnss_gid([wheel])
1036 if targetdir is not None:
1037 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1038 os.makedirs(os.path.join(targetdir, "etc"))
1039 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1040 elif smbconf is None:
1041 smbconf = param.default_path()
1043 # only install a new smb.conf if there isn't one there already
1044 if not os.path.exists(smbconf):
1045 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1048 lp = param.LoadParm()
1051 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1052 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1053 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1056 paths = provision_paths_from_lp(lp, names.dnsdomain)
1060 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1061 except socket.gaierror, (socket.EAI_NODATA, msg):
1066 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1067 except socket.gaierror, (socket.EAI_NODATA, msg):
1070 if serverrole is None:
1071 serverrole = lp.get("server role")
1073 assert serverrole in ("domain controller", "member server", "standalone")
1074 if invocationid is None and serverrole == "domain controller":
1075 invocationid = str(uuid.uuid4())
1077 if not os.path.exists(paths.private_dir):
1078 os.mkdir(paths.private_dir)
1080 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1082 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1084 provision_backend = None
1085 if ldap_backend_type:
1086 # We only support an LDAP backend over ldapi://
1088 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1089 lp=lp, credentials=credentials,
1091 message=message, hostname=hostname,
1092 root=root, schema=schema,
1093 ldap_backend_type=ldap_backend_type,
1094 ldapadminpass=ldapadminpass,
1095 ldap_backend_extra_port=ldap_backend_extra_port,
1096 ol_mmr_urls=ol_mmr_urls,
1097 slapd_path=slapd_path,
1098 setup_ds_path=setup_ds_path,
1099 ldap_dryrun_mode=ldap_dryrun_mode)
1101 # Now use the backend credentials to access the databases
1102 credentials = provision_backend.credentials
1104 # only install a new shares config db if there is none
1105 if not os.path.exists(paths.shareconf):
1106 message("Setting up share.ldb")
1107 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1108 credentials=credentials, lp=lp)
1109 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1112 message("Setting up secrets.ldb")
1113 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1114 session_info=session_info,
1115 credentials=credentials, lp=lp)
1117 message("Setting up the registry")
1118 setup_registry(paths.hklm, setup_path, session_info,
1121 message("Setting up idmap db")
1122 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1125 message("Setting up SAM db")
1126 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1127 credentials=credentials, lp=lp, names=names,
1129 domainsid=domainsid,
1130 schema=schema, domainguid=domainguid, policyguid=policyguid,
1132 adminpass=adminpass, krbtgtpass=krbtgtpass,
1133 invocationid=invocationid,
1134 machinepass=machinepass, dnspass=dnspass,
1135 serverrole=serverrole, ldap_backend=provision_backend)
1137 if serverrole == "domain controller":
1138 if paths.netlogon is None:
1139 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1140 message("Please either remove %s or see the template at %s" %
1141 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1142 assert(paths.netlogon is not None)
1144 if paths.sysvol is None:
1145 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1146 message("Please either remove %s or see the template at %s" %
1147 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1148 assert(paths.sysvol is not None)
1150 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1151 "{" + policyguid + "}")
1152 os.makedirs(policy_path, 0755)
1153 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1154 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1155 os.makedirs(os.path.join(policy_path, "User"), 0755)
1156 if not os.path.isdir(paths.netlogon):
1157 os.makedirs(paths.netlogon, 0755)
1159 if samdb_fill == FILL_FULL:
1160 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1161 root_uid=root_uid, nobody_uid=nobody_uid,
1162 users_gid=users_gid, wheel_gid=wheel_gid)
1164 message("Setting up sam.ldb rootDSE marking as synchronized")
1165 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1167 # Only make a zone file on the first DC, it should be replicated with DNS replication
1168 if serverrole == "domain controller":
1169 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1170 credentials=credentials, lp=lp)
1171 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1173 netbiosname=names.netbiosname,
1174 domainsid=domainsid,
1175 keytab_path=paths.keytab, samdb_url=paths.samdb,
1176 dns_keytab_path=paths.dns_keytab,
1177 dnspass=dnspass, machinepass=machinepass,
1178 dnsdomain=names.dnsdomain)
1180 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1181 assert isinstance(domainguid, str)
1182 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1183 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1184 scope=SCOPE_SUBTREE)
1185 assert isinstance(hostguid, str)
1187 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1188 domaindn=names.domaindn, hostip=hostip,
1189 hostip6=hostip6, hostname=names.hostname,
1190 dnspass=dnspass, realm=names.realm,
1191 domainguid=domainguid, hostguid=hostguid)
1193 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1194 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1196 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1197 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1198 keytab_name=paths.dns_keytab)
1199 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1200 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1202 create_krb5_conf(paths.krb5conf, setup_path,
1203 dnsdomain=names.dnsdomain, hostname=names.hostname,
1205 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1208 # if backend is openldap, terminate slapd after final provision and check its proper termination
1209 if provision_backend is not None and provision_backend.slapd is not None:
1210 if provision_backend.slapd.poll() is None:
1212 if hasattr(provision_backend.slapd, "terminate"):
1213 provision_backend.slapd.terminate()
1216 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1218 #and now wait for it to die
1219 provision_backend.slapd.communicate()
1221 # now display slapd_command_file.txt to show how slapd must be started next time
1222 message("Use later the following commandline to start slapd, then Samba:")
1223 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1224 message(slapd_command)
1225 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1227 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1228 "SLAPD_COMMAND" : slapd_command})
1231 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1234 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1236 message("Once the above files are installed, your Samba4 server will be ready to use")
1237 message("Server Role: %s" % serverrole)
1238 message("Hostname: %s" % names.hostname)
1239 message("NetBIOS Domain: %s" % names.domain)
1240 message("DNS Domain: %s" % names.dnsdomain)
1241 message("DOMAIN SID: %s" % str(domainsid))
1242 if samdb_fill == FILL_FULL:
1243 message("Admin password: %s" % adminpass)
1244 if provision_backend:
1245 if provision_backend.credentials.get_bind_dn() is not None:
1246 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1248 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1250 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1252 result = ProvisionResult()
1253 result.domaindn = domaindn
1254 result.paths = paths
1256 result.samdb = samdb
1261 def provision_become_dc(setup_dir=None,
1262 smbconf=None, targetdir=None, realm=None,
1263 rootdn=None, domaindn=None, schemadn=None,
1264 configdn=None, serverdn=None,
1265 domain=None, hostname=None, domainsid=None,
1266 adminpass=None, krbtgtpass=None, domainguid=None,
1267 policyguid=None, invocationid=None, machinepass=None,
1268 dnspass=None, root=None, nobody=None, users=None,
1269 wheel=None, backup=None, serverrole=None,
1270 ldap_backend=None, ldap_backend_type=None,
1271 sitename=None, debuglevel=1):
1274 """print a message if quiet is not set."""
1277 glue.set_debug_level(debuglevel)
1279 return provision(setup_dir, message, system_session(), None,
1280 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1281 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1282 configdn=configdn, serverdn=serverdn, domain=domain,
1283 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1284 machinepass=machinepass, serverrole="domain controller",
1288 def setup_db_config(setup_path, dbdir):
1289 """Setup a Berkeley database.
1291 :param setup_path: Setup path function.
1292 :param dbdir: Database directory."""
1293 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1294 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1295 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1296 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1298 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1299 {"LDAPDBDIR": dbdir})
1301 class ProvisionBackend(object):
1302 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1303 names=None, message=None,
1304 hostname=None, root=None,
1305 schema=None, ldapadminpass=None,
1306 ldap_backend_type=None, ldap_backend_extra_port=None,
1308 setup_ds_path=None, slapd_path=None,
1309 nosync=False, ldap_dryrun_mode=False):
1310 """Provision an LDAP backend for samba4
1312 This works for OpenLDAP and Fedora DS
1315 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1317 if not os.path.isdir(paths.ldapdir):
1318 os.makedirs(paths.ldapdir, 0700)
1320 if ldap_backend_type == "existing":
1321 #Check to see that this 'existing' LDAP backend in fact exists
1322 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1323 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1324 expression="(objectClass=OpenLDAProotDSE)")
1326 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1327 # This caused them to be set into the long-term database later in the script.
1328 self.credentials = credentials
1329 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1332 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1333 # if another instance of slapd is already running
1335 ldapi_db = Ldb(self.ldapi_uri)
1336 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1337 expression="(objectClass=OpenLDAProotDSE)");
1339 f = open(paths.slapdpid, "r")
1342 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1346 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1351 # Try to print helpful messages when the user has not specified the path to slapd
1352 if slapd_path is None:
1353 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1354 if not os.path.exists(slapd_path):
1355 message (slapd_path)
1356 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1358 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1360 os.unlink(schemadb_path)
1365 # Put the LDIF of the schema into a database so we can search on
1366 # it to generate schema-dependent configurations in Fedora DS and
1368 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1369 schema.ldb.connect(schemadb_path)
1370 schema.ldb.transaction_start()
1372 # These bits of LDIF are supplied when the Schema object is created
1373 schema.ldb.add_ldif(schema.schema_dn_add)
1374 schema.ldb.modify_ldif(schema.schema_dn_modify)
1375 schema.ldb.add_ldif(schema.schema_data)
1376 schema.ldb.transaction_commit()
1378 self.credentials = Credentials()
1379 self.credentials.guess(lp)
1380 #Kerberos to an ldapi:// backend makes no sense
1381 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1382 self.ldap_backend_type = ldap_backend_type
1384 if ldap_backend_type == "fedora-ds":
1385 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1386 names=names, message=message,
1388 ldapadminpass=ldapadminpass, root=root,
1390 ldap_backend_extra_port=ldap_backend_extra_port,
1391 setup_ds_path=setup_ds_path,
1392 slapd_path=slapd_path,
1394 ldap_dryrun_mode=ldap_dryrun_mode)
1396 elif ldap_backend_type == "openldap":
1397 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1398 names=names, message=message,
1400 ldapadminpass=ldapadminpass, root=root,
1402 ldap_backend_extra_port=ldap_backend_extra_port,
1403 ol_mmr_urls=ol_mmr_urls,
1404 slapd_path=slapd_path,
1406 ldap_dryrun_mode=ldap_dryrun_mode)
1408 raise ProvisioningError("Unknown LDAP backend type selected")
1410 self.credentials.set_password(ldapadminpass)
1412 # Now start the slapd, so we can provision onto it. We keep the
1413 # subprocess context around, to kill this off at the successful
1415 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1417 while self.slapd.poll() is None:
1418 # Wait until the socket appears
1420 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1421 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1422 expression="(objectClass=OpenLDAProotDSE)")
1423 # If we have got here, then we must have a valid connection to the LDAP server!
1429 raise ProvisioningError("slapd died before we could make a connection to it")
1432 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1434 hostname=None, ldapadminpass=None, root=None,
1436 ldap_backend_extra_port=None,
1438 slapd_path=None, nosync=False,
1439 ldap_dryrun_mode=False):
1441 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1444 nosync_config = "dbnosync"
1446 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1447 refint_attributes = ""
1448 memberof_config = "# Generated from Samba4 schema\n"
1449 for att in lnkattr.keys():
1450 if lnkattr[att] is not None:
1451 refint_attributes = refint_attributes + " " + att
1453 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1454 { "MEMBER_ATTR" : att ,
1455 "MEMBEROF_ATTR" : lnkattr[att] })
1457 refint_config = read_and_sub_file(setup_path("refint.conf"),
1458 { "LINK_ATTRS" : refint_attributes})
1460 attrs = ["linkID", "lDAPDisplayName"]
1461 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1463 for i in range (0, len(res)):
1464 index_attr = res[i]["lDAPDisplayName"][0]
1465 if index_attr == "objectGUID":
1466 index_attr = "entryUUID"
1468 index_config += "index " + index_attr + " eq\n"
1470 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1472 mmr_replicator_acl = ""
1473 mmr_serverids_config = ""
1474 mmr_syncrepl_schema_config = ""
1475 mmr_syncrepl_config_config = ""
1476 mmr_syncrepl_user_config = ""
1479 if ol_mmr_urls is not None:
1480 # For now, make these equal
1481 mmr_pass = ldapadminpass
1483 url_list=filter(None,ol_mmr_urls.split(' '))
1484 if (len(url_list) == 1):
1485 url_list=filter(None,ol_mmr_urls.split(','))
1488 mmr_on_config = "MirrorMode On"
1489 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1491 for url in url_list:
1493 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1494 { "SERVERID" : str(serverid),
1495 "LDAPSERVER" : url })
1498 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1500 "MMRDN": names.schemadn,
1502 "MMR_PASSWORD": mmr_pass})
1505 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1507 "MMRDN": names.configdn,
1509 "MMR_PASSWORD": mmr_pass})
1512 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1514 "MMRDN": names.domaindn,
1516 "MMR_PASSWORD": mmr_pass })
1517 # OpenLDAP cn=config initialisation
1518 olc_syncrepl_config = ""
1520 # if mmr = yes, generate cn=config-replication directives
1521 # and olc_seed.lif for the other mmr-servers
1522 if ol_mmr_urls is not None:
1524 olc_serverids_config = ""
1525 olc_syncrepl_seed_config = ""
1526 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1528 for url in url_list:
1530 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1531 { "SERVERID" : str(serverid),
1532 "LDAPSERVER" : url })
1535 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1538 "MMR_PASSWORD": mmr_pass})
1540 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1542 "LDAPSERVER" : url})
1544 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1545 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1546 "OLC_PW": ldapadminpass,
1547 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1550 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1551 {"DNSDOMAIN": names.dnsdomain,
1552 "LDAPDIR": paths.ldapdir,
1553 "DOMAINDN": names.domaindn,
1554 "CONFIGDN": names.configdn,
1555 "SCHEMADN": names.schemadn,
1556 "MEMBEROF_CONFIG": memberof_config,
1557 "MIRRORMODE": mmr_on_config,
1558 "REPLICATOR_ACL": mmr_replicator_acl,
1559 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1560 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1561 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1562 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1563 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1564 "OLC_MMR_CONFIG": olc_mmr_config,
1565 "REFINT_CONFIG": refint_config,
1566 "INDEX_CONFIG": index_config,
1567 "NOSYNC": nosync_config})
1569 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1570 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1571 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1573 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1574 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1576 setup_file(setup_path("cn=samba.ldif"),
1577 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1578 { "UUID": str(uuid.uuid4()),
1579 "LDAPTIME": timestring(int(time.time()))} )
1580 setup_file(setup_path("cn=samba-admin.ldif"),
1581 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1582 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1583 "UUID": str(uuid.uuid4()),
1584 "LDAPTIME": timestring(int(time.time()))} )
1586 if ol_mmr_urls is not None:
1587 setup_file(setup_path("cn=replicator.ldif"),
1588 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1589 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1590 "UUID": str(uuid.uuid4()),
1591 "LDAPTIME": timestring(int(time.time()))} )
1594 mapping = "schema-map-openldap-2.3"
1595 backend_schema = "backend-schema.schema"
1597 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1598 assert backend_schema_data is not None
1599 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1601 # now we generate the needed strings to start slapd automatically,
1602 # first ldapi_uri...
1603 if ldap_backend_extra_port is not None:
1604 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1605 # specified there as part of it's clue as to it's own name,
1606 # and not to replicate to itself
1607 if ol_mmr_urls is None:
1608 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1610 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1612 server_port_string = ""
1614 # Prepare the 'result' information - the commands to return in particular
1615 result.slapd_provision_command = [slapd_path]
1617 result.slapd_provision_command.append("-F" + paths.olcdir)
1619 result.slapd_provision_command.append("-h")
1621 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1622 result.slapd_command = list(result.slapd_provision_command)
1624 result.slapd_provision_command.append(result.ldapi_uri)
1625 result.slapd_provision_command.append("-d0")
1627 uris = result.ldapi_uri
1628 if server_port_string is not "":
1629 uris = uris + " " + server_port_string
1631 result.slapd_command.append(uris)
1633 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1634 result.credentials.set_username("samba-admin")
1636 # If we were just looking for crashes up to this point, it's a
1637 # good time to exit before we realise we don't have OpenLDAP on
1639 if ldap_dryrun_mode:
1642 # Finally, convert the configuration into cn=config style!
1643 if not os.path.isdir(paths.olcdir):
1644 os.makedirs(paths.olcdir, 0770)
1646 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1648 # We can't do this, as OpenLDAP is strange. It gives an error
1649 # output to the above, but does the conversion sucessfully...
1652 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1654 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1655 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1657 # Don't confuse the admin by leaving the slapd.conf around
1658 os.remove(paths.slapdconf)
1661 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1663 hostname=None, ldapadminpass=None, root=None,
1665 ldap_backend_extra_port=None,
1669 ldap_dryrun_mode=False):
1671 if ldap_backend_extra_port is not None:
1672 serverport = "ServerPort=%d" % ldap_backend_extra_port
1676 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1678 "HOSTNAME": hostname,
1679 "DNSDOMAIN": names.dnsdomain,
1680 "LDAPDIR": paths.ldapdir,
1681 "DOMAINDN": names.domaindn,
1682 "LDAPMANAGERDN": names.ldapmanagerdn,
1683 "LDAPMANAGERPASS": ldapadminpass,
1684 "SERVERPORT": serverport})
1686 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1687 {"CONFIGDN": names.configdn,
1688 "SCHEMADN": names.schemadn,
1691 mapping = "schema-map-fedora-ds-1.0"
1692 backend_schema = "99_ad.ldif"
1694 # Build a schema file in Fedora DS format
1695 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1696 assert backend_schema_data is not None
1697 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1699 result.credentials.set_bind_dn(names.ldapmanagerdn)
1701 # Destory the target directory, or else setup-ds.pl will complain
1702 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1703 shutil.rmtree(fedora_ds_dir, True)
1705 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1706 #In the 'provision' command line, stay in the foreground so we can easily kill it
1707 result.slapd_provision_command.append("-d0")
1709 #the command for the final run is the normal script
1710 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1712 # If we were just looking for crashes up to this point, it's a
1713 # good time to exit before we realise we don't have Fedora DS on
1714 if ldap_dryrun_mode:
1717 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1718 if setup_ds_path is None:
1719 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\"!")
1720 if not os.path.exists(setup_ds_path):
1721 message (setup_ds_path)
1722 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1724 # Run the Fedora DS setup utility
1725 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1727 raise ProvisioningError("setup-ds failed")
1729 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1730 """Create a PHP LDAP admin configuration file.
1732 :param path: Path to write the configuration to.
1733 :param setup_path: Function to generate setup paths.
1735 setup_file(setup_path("phpldapadmin-config.php"), path,
1736 {"S4_LDAPI_URI": ldapi_uri})
1739 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1740 hostip, hostip6, hostname, dnspass, realm, domainguid,
1742 """Write out a DNS zone file, from the info in the current database.
1744 :param path: Path of the new zone file.
1745 :param setup_path: Setup path function.
1746 :param dnsdomain: DNS Domain name
1747 :param domaindn: DN of the Domain
1748 :param hostip: Local IPv4 IP
1749 :param hostip6: Local IPv6 IP
1750 :param hostname: Local hostname
1751 :param dnspass: Password for DNS
1752 :param realm: Realm name
1753 :param domainguid: GUID of the domain.
1754 :param hostguid: GUID of the host.
1756 assert isinstance(domainguid, str)
1758 if hostip6 is not None:
1759 hostip6_base_line = " IN AAAA " + hostip6
1760 hostip6_host_line = hostname + " IN AAAA " + hostip6
1762 hostip6_base_line = ""
1763 hostip6_host_line = ""
1765 if hostip is not None:
1766 hostip_base_line = " IN A " + hostip
1767 hostip_host_line = hostname + " IN A " + hostip
1769 hostip_base_line = ""
1770 hostip_host_line = ""
1772 setup_file(setup_path("provision.zone"), path, {
1773 "DNSPASS_B64": b64encode(dnspass),
1774 "HOSTNAME": hostname,
1775 "DNSDOMAIN": dnsdomain,
1777 "HOSTIP_BASE_LINE": hostip_base_line,
1778 "HOSTIP_HOST_LINE": hostip_host_line,
1779 "DOMAINGUID": domainguid,
1780 "DATESTRING": time.strftime("%Y%m%d%H"),
1781 "DEFAULTSITE": DEFAULTSITE,
1782 "HOSTGUID": hostguid,
1783 "HOSTIP6_BASE_LINE": hostip6_base_line,
1784 "HOSTIP6_HOST_LINE": hostip6_host_line,
1788 def create_named_conf(path, setup_path, realm, dnsdomain,
1790 """Write out a file containing zone statements suitable for inclusion in a
1791 named.conf file (including GSS-TSIG configuration).
1793 :param path: Path of the new named.conf file.
1794 :param setup_path: Setup path function.
1795 :param realm: Realm name
1796 :param dnsdomain: DNS Domain name
1797 :param private_dir: Path to private directory
1798 :param keytab_name: File name of DNS keytab file
1801 setup_file(setup_path("named.conf"), path, {
1802 "DNSDOMAIN": dnsdomain,
1804 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1805 "PRIVATE_DIR": private_dir
1808 def create_named_txt(path, setup_path, realm, dnsdomain,
1809 private_dir, keytab_name):
1810 """Write out a file containing zone statements suitable for inclusion in a
1811 named.conf file (including GSS-TSIG configuration).
1813 :param path: Path of the new named.conf file.
1814 :param setup_path: Setup path function.
1815 :param realm: Realm name
1816 :param dnsdomain: DNS Domain name
1817 :param private_dir: Path to private directory
1818 :param keytab_name: File name of DNS keytab file
1821 setup_file(setup_path("named.txt"), path, {
1822 "DNSDOMAIN": dnsdomain,
1824 "DNS_KEYTAB": keytab_name,
1825 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1826 "PRIVATE_DIR": private_dir
1829 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1830 """Write out a file containing zone statements suitable for inclusion in a
1831 named.conf file (including GSS-TSIG configuration).
1833 :param path: Path of the new named.conf file.
1834 :param setup_path: Setup path function.
1835 :param dnsdomain: DNS Domain name
1836 :param hostname: Local hostname
1837 :param realm: Realm name
1840 setup_file(setup_path("krb5.conf"), path, {
1841 "DNSDOMAIN": dnsdomain,
1842 "HOSTNAME": hostname,