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
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
39 from auth import system_session
40 from samba import version, Ldb, substitute_var, valid_netbios_name, check_all_substituted, \
42 from samba.samdb import SamDB
43 from samba.idmap import IDmapDB
44 from samba.dcerpc import security
46 from ldb import SCOPE_SUBTREE, LdbError, timestring
47 from ms_schema import read_ms_schema
49 __docformat__ = "restructuredText"
53 """Find the setup directory used by provision."""
54 dirname = os.path.dirname(__file__)
55 if "/site-packages/" in dirname:
56 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
57 for suffix in ["share/setup", "share/samba/setup", "setup"]:
58 ret = os.path.join(prefix, suffix)
59 if os.path.isdir(ret):
62 ret = os.path.join(dirname, "../../../setup")
63 if os.path.isdir(ret):
65 raise Exception("Unable to find setup directory.")
68 DEFAULTSITE = "Default-First-Site-Name"
70 class InvalidNetbiosName(Exception):
71 """A specified name was not a valid NetBIOS name."""
72 def __init__(self, name):
73 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
76 class ProvisionPaths(object):
89 self.dns_keytab = None
92 self.private_dir = None
95 self.modulesconf = None
96 self.memberofconf = None
97 self.fedoradsinf = None
98 self.fedoradspartitions = None
100 self.olmmrserveridsconf = None
101 self.olmmrsyncreplconf = None
103 self.olslaptest = None
104 self.olcseedldif = None
107 class ProvisionNames(object):
113 self.ldapmanagerdn = None
114 self.dnsdomain = None
116 self.netbiosname = None
123 class ProvisionResult(object):
130 def check_install(lp, session_info, credentials):
131 """Check whether the current install seems ok.
133 :param lp: Loadparm context
134 :param session_info: Session information
135 :param credentials: Credentials
137 if lp.get("realm") == "":
138 raise Exception("Realm empty")
139 ldb = Ldb(lp.get("sam database"), session_info=session_info,
140 credentials=credentials, lp=lp)
141 if len(ldb.search("(cn=Administrator)")) != 1:
142 raise "No administrator account found"
145 def findnss(nssfn, names):
146 """Find a user or group from a list of possibilities.
148 :param nssfn: NSS Function to try (should raise KeyError if not found)
149 :param names: Names to check.
150 :return: Value return by first names list.
157 raise KeyError("Unable to find user/group %r" % names)
160 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
161 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
164 def read_and_sub_file(file, subst_vars):
165 """Read a file and sub in variables found in it
167 :param file: File to be read (typically from setup directory)
168 param subst_vars: Optional variables to subsitute in the file.
170 data = open(file, 'r').read()
171 if subst_vars is not None:
172 data = substitute_var(data, subst_vars)
173 check_all_substituted(data)
177 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
178 """Setup a ldb in the private dir.
180 :param ldb: LDB file to import data into
181 :param ldif_path: Path of the LDIF file to load
182 :param subst_vars: Optional variables to subsitute in LDIF.
184 assert isinstance(ldif_path, str)
186 data = read_and_sub_file(ldif_path, subst_vars)
190 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
191 """Modify a ldb in the private dir.
193 :param ldb: LDB object.
194 :param ldif_path: LDIF file path.
195 :param subst_vars: Optional dictionary with substitution variables.
197 data = read_and_sub_file(ldif_path, subst_vars)
199 ldb.modify_ldif(data)
202 def setup_ldb(ldb, ldif_path, subst_vars):
203 """Import a LDIF a file into a LDB handle, optionally substituting variables.
205 :note: Either all LDIF data will be added or none (using transactions).
207 :param ldb: LDB file to import into.
208 :param ldif_path: Path to the LDIF file.
209 :param subst_vars: Dictionary with substitution variables.
211 assert ldb is not None
212 ldb.transaction_start()
214 setup_add_ldif(ldb, ldif_path, subst_vars)
216 ldb.transaction_cancel()
218 ldb.transaction_commit()
221 def setup_file(template, fname, subst_vars):
222 """Setup a file in the private dir.
224 :param template: Path of the template file.
225 :param fname: Path of the file to create.
226 :param subst_vars: Substitution variables.
230 if os.path.exists(f):
233 data = read_and_sub_file(template, subst_vars)
234 open(f, 'w').write(data)
237 def provision_paths_from_lp(lp, dnsdomain):
238 """Set the default paths for provisioning.
240 :param lp: Loadparm context.
241 :param dnsdomain: DNS Domain name
243 paths = ProvisionPaths()
244 paths.private_dir = lp.get("private dir")
245 paths.keytab = "secrets.keytab"
246 paths.dns_keytab = "dns.keytab"
248 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
249 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
250 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
251 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
252 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
253 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
254 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
255 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
256 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
257 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
258 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
259 paths.phpldapadminconfig = os.path.join(paths.private_dir,
260 "phpldapadmin-config.php")
261 paths.ldapdir = os.path.join(paths.private_dir,
263 paths.slapdconf = os.path.join(paths.ldapdir,
265 paths.modulesconf = os.path.join(paths.ldapdir,
267 paths.memberofconf = os.path.join(paths.ldapdir,
269 paths.fedoradsinf = os.path.join(paths.ldapdir,
271 paths.fedoradspartitions = os.path.join(paths.ldapdir,
272 "fedorads-partitions.ldif")
273 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
274 "mmr_serverids.conf")
275 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
277 paths.olcdir = os.path.join(paths.ldapdir,
279 paths.olcseedldif = os.path.join(paths.ldapdir,
281 paths.hklm = "hklm.ldb"
282 paths.hkcr = "hkcr.ldb"
283 paths.hkcu = "hkcu.ldb"
284 paths.hku = "hku.ldb"
285 paths.hkpd = "hkpd.ldb"
286 paths.hkpt = "hkpt.ldb"
288 paths.sysvol = lp.get("path", "sysvol")
290 paths.netlogon = lp.get("path", "netlogon")
292 paths.smbconf = lp.configfile
297 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
298 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
300 """Guess configuration settings to use."""
303 hostname = socket.gethostname().split(".")[0].lower()
305 netbiosname = hostname.upper()
306 if not valid_netbios_name(netbiosname):
307 raise InvalidNetbiosName(netbiosname)
309 hostname = hostname.lower()
311 if dnsdomain is None:
312 dnsdomain = lp.get("realm")
314 if serverrole is None:
315 serverrole = lp.get("server role")
317 assert dnsdomain is not None
318 realm = dnsdomain.upper()
320 if lp.get("realm").upper() != realm:
321 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
322 (lp.get("realm"), lp.configfile, realm))
324 dnsdomain = dnsdomain.lower()
326 if serverrole == "domain controller":
328 domain = lp.get("workgroup")
330 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
331 if lp.get("workgroup").upper() != domain.upper():
332 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
333 lp.get("workgroup"), domain)
337 domaindn = "CN=" + netbiosname
339 assert domain is not None
340 domain = domain.upper()
341 if not valid_netbios_name(domain):
342 raise InvalidNetbiosName(domain)
348 configdn = "CN=Configuration," + rootdn
350 schemadn = "CN=Schema," + configdn
355 names = ProvisionNames()
356 names.rootdn = rootdn
357 names.domaindn = domaindn
358 names.configdn = configdn
359 names.schemadn = schemadn
360 names.ldapmanagerdn = "CN=Manager," + rootdn
361 names.dnsdomain = dnsdomain
362 names.domain = domain
364 names.netbiosname = netbiosname
365 names.hostname = hostname
366 names.sitename = sitename
367 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
372 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
374 """Create a new smb.conf file based on a couple of basic settings.
376 assert smbconf is not None
378 hostname = socket.gethostname().split(".")[0].lower()
380 if serverrole is None:
381 serverrole = "standalone"
383 assert serverrole in ("domain controller", "member server", "standalone")
384 if serverrole == "domain controller":
386 elif serverrole == "member server":
387 smbconfsuffix = "member"
388 elif serverrole == "standalone":
389 smbconfsuffix = "standalone"
391 assert domain is not None
392 assert realm is not None
394 default_lp = param.LoadParm()
395 #Load non-existant file
396 if os.path.exists(smbconf):
397 default_lp.load(smbconf)
399 if targetdir is not None:
400 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
401 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
403 default_lp.set("lock dir", os.path.abspath(targetdir))
408 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
409 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
411 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
413 "HOSTNAME": hostname,
416 "SERVERROLE": serverrole,
417 "NETLOGONPATH": netlogon,
418 "SYSVOLPATH": sysvol,
419 "PRIVATEDIR_LINE": privatedir_line,
420 "LOCKDIR_LINE": lockdir_line
424 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
425 users_gid, wheel_gid):
426 """setup reasonable name mappings for sam names to unix names.
428 :param samdb: SamDB object.
429 :param idmap: IDmap db object.
430 :param sid: The domain sid.
431 :param domaindn: The domain DN.
432 :param root_uid: uid of the UNIX root user.
433 :param nobody_uid: uid of the UNIX nobody user.
434 :param users_gid: gid of the UNIX users group.
435 :param wheel_gid: gid of the UNIX wheel group."""
436 # add some foreign sids if they are not present already
437 samdb.add_stock_foreign_sids()
439 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
440 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
442 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
443 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
446 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
448 serverrole, ldap_backend=None,
449 ldap_backend_type=None, erase=False):
450 """Setup the partitions for the SAM database.
452 Alternatively, provision() may call this, and then populate the database.
454 :note: This will wipe the Sam Database!
456 :note: This function always removes the local SAM LDB file. The erase
457 parameter controls whether to erase the existing data, which
458 may not be stored locally but in LDAP.
460 assert session_info is not None
463 samdb = SamDB(samdb_path, session_info=session_info,
464 credentials=credentials, lp=lp)
468 os.unlink(samdb_path)
469 samdb = SamDB(samdb_path, session_info=session_info,
470 credentials=credentials, lp=lp)
475 #Add modules to the list to activate them by default
476 #beware often order is important
478 # Some Known ordering constraints:
479 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
480 # - objectclass must be before password_hash, because password_hash checks
481 # that the objectclass is of type person (filled in by objectclass
482 # module when expanding the objectclass list)
483 # - partition must be last
484 # - each partition has its own module list then
485 modules_list = ["rootdse",
503 "extended_dn_out_ldb"]
504 modules_list2 = ["show_deleted",
507 domaindn_ldb = "users.ldb"
508 if ldap_backend is not None:
509 domaindn_ldb = ldap_backend
510 configdn_ldb = "configuration.ldb"
511 if ldap_backend is not None:
512 configdn_ldb = ldap_backend
513 schemadn_ldb = "schema.ldb"
514 if ldap_backend is not None:
515 schema_ldb = ldap_backend
516 schemadn_ldb = ldap_backend
518 if ldap_backend_type == "fedora-ds":
519 backend_modules = ["nsuniqueid", "paged_searches"]
520 # We can handle linked attributes here, as we don't have directory-side subtree operations
521 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
522 elif ldap_backend_type == "openldap":
523 backend_modules = ["entryuuid", "paged_searches"]
524 # OpenLDAP handles subtree renames, so we don't want to do any of these things
525 tdb_modules_list = ["extended_dn_out_dereference"]
526 elif ldap_backend is not None:
527 raise "LDAP Backend specified, but LDAP Backend Type not specified"
528 elif serverrole == "domain controller":
529 backend_modules = ["repl_meta_data"]
531 backend_modules = ["objectguid"]
533 if tdb_modules_list is None:
534 tdb_modules_list_as_string = ""
536 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
538 samdb.transaction_start()
540 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
541 "SCHEMADN": names.schemadn,
542 "SCHEMADN_LDB": schemadn_ldb,
543 "SCHEMADN_MOD2": ",objectguid",
544 "CONFIGDN": names.configdn,
545 "CONFIGDN_LDB": configdn_ldb,
546 "DOMAINDN": names.domaindn,
547 "DOMAINDN_LDB": domaindn_ldb,
548 "SCHEMADN_MOD": "schema_fsmo,instancetype",
549 "CONFIGDN_MOD": "naming_fsmo,instancetype",
550 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
551 "MODULES_LIST": ",".join(modules_list),
552 "TDB_MODULES_LIST": tdb_modules_list_as_string,
553 "MODULES_LIST2": ",".join(modules_list2),
554 "BACKEND_MOD": ",".join(backend_modules),
558 samdb.transaction_cancel()
561 samdb.transaction_commit()
563 samdb = SamDB(samdb_path, session_info=session_info,
564 credentials=credentials, lp=lp)
566 samdb.transaction_start()
568 message("Setting up sam.ldb attributes")
569 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
571 message("Setting up sam.ldb rootDSE")
572 setup_samdb_rootdse(samdb, setup_path, names)
575 message("Erasing data from partitions")
576 samdb.erase_partitions()
579 samdb.transaction_cancel()
582 samdb.transaction_commit()
587 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
588 netbiosname, domainsid, keytab_path, samdb_url,
589 dns_keytab_path, dnspass, machinepass):
590 """Add DC-specific bits to a secrets database.
592 :param secretsdb: Ldb Handle to the secrets database
593 :param setup_path: Setup path function
594 :param machinepass: Machine password
596 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
597 "MACHINEPASS_B64": b64encode(machinepass),
600 "DNSDOMAIN": dnsdomain,
601 "DOMAINSID": str(domainsid),
602 "SECRETS_KEYTAB": keytab_path,
603 "NETBIOSNAME": netbiosname,
604 "SAM_LDB": samdb_url,
605 "DNS_KEYTAB": dns_keytab_path,
606 "DNSPASS_B64": b64encode(dnspass),
610 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
611 """Setup the secrets database.
613 :param path: Path to the secrets database.
614 :param setup_path: Get the path to a setup file.
615 :param session_info: Session info.
616 :param credentials: Credentials
617 :param lp: Loadparm context
618 :return: LDB handle for the created secrets database
620 if os.path.exists(path):
622 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
625 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
626 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
628 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
630 if credentials is not None and credentials.authentication_requested():
631 if credentials.get_bind_dn() is not None:
632 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
633 "LDAPMANAGERDN": credentials.get_bind_dn(),
634 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
637 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
638 "LDAPADMINUSER": credentials.get_username(),
639 "LDAPADMINREALM": credentials.get_realm(),
640 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
646 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
647 """Setup the templates database.
649 :param path: Path to the database.
650 :param setup_path: Function for obtaining the path to setup files.
651 :param session_info: Session info
652 :param credentials: Credentials
653 :param lp: Loadparm context
655 templates_ldb = SamDB(path, session_info=session_info,
656 credentials=credentials, lp=lp)
659 templates_ldb.erase()
660 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
664 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
666 templates_ldb = SamDB(path, session_info=session_info,
667 credentials=credentials, lp=lp)
669 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
672 def setup_registry(path, setup_path, session_info, credentials, lp):
673 """Setup the registry.
675 :param path: Path to the registry database
676 :param setup_path: Function that returns the path to a setup.
677 :param session_info: Session information
678 :param credentials: Credentials
679 :param lp: Loadparm context
681 reg = registry.Registry()
682 hive = registry.open_ldb(path, session_info=session_info,
683 credentials=credentials, lp_ctx=lp)
684 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
685 provision_reg = setup_path("provision.reg")
686 assert os.path.exists(provision_reg)
687 reg.diff_apply(provision_reg)
690 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
691 """Setup the idmap database.
693 :param path: path to the idmap database
694 :param setup_path: Function that returns a path to a setup file
695 :param session_info: Session information
696 :param credentials: Credentials
697 :param lp: Loadparm context
699 if os.path.exists(path):
702 idmap_ldb = IDmapDB(path, session_info=session_info,
703 credentials=credentials, lp=lp)
706 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
710 def setup_samdb_rootdse(samdb, setup_path, names):
711 """Setup the SamDB rootdse.
713 :param samdb: Sam Database handle
714 :param setup_path: Obtain setup path
716 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
717 "SCHEMADN": names.schemadn,
718 "NETBIOSNAME": names.netbiosname,
719 "DNSDOMAIN": names.dnsdomain,
720 "REALM": names.realm,
721 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
722 "DOMAINDN": names.domaindn,
723 "ROOTDN": names.rootdn,
724 "CONFIGDN": names.configdn,
725 "SERVERDN": names.serverdn,
729 def setup_self_join(samdb, names,
730 machinepass, dnspass,
731 domainsid, invocationid, setup_path,
732 policyguid, domainControllerFunctionality):
733 """Join a host to its own domain."""
734 assert isinstance(invocationid, str)
735 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
736 "CONFIGDN": names.configdn,
737 "SCHEMADN": names.schemadn,
738 "DOMAINDN": names.domaindn,
739 "SERVERDN": names.serverdn,
740 "INVOCATIONID": invocationid,
741 "NETBIOSNAME": names.netbiosname,
742 "DEFAULTSITE": names.sitename,
743 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
744 "MACHINEPASS_B64": b64encode(machinepass),
745 "DNSPASS_B64": b64encode(dnspass),
746 "REALM": names.realm,
747 "DOMAIN": names.domain,
748 "DNSDOMAIN": names.dnsdomain,
749 "SAMBA_VERSION_STRING": version,
750 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
751 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
752 "POLICYGUID": policyguid,
753 "DNSDOMAIN": names.dnsdomain,
754 "DOMAINSID": str(domainsid),
755 "DOMAINDN": names.domaindn})
758 def setup_samdb(path, setup_path, session_info, credentials, lp,
760 domainsid, aci, domainguid, policyguid,
761 fill, adminpass, krbtgtpass,
762 machinepass, invocationid, dnspass,
763 serverrole, ldap_backend=None,
764 ldap_backend_type=None):
765 """Setup a complete SAM Database.
767 :note: This will wipe the main SAM database file!
770 domainFunctionality = DS_BEHAVIOR_WIN2008
771 forestFunctionality = DS_BEHAVIOR_WIN2008
772 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
774 erase = (fill != FILL_DRS)
776 # Also wipes the database
777 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
778 credentials=credentials, session_info=session_info,
780 ldap_backend=ldap_backend, serverrole=serverrole,
781 ldap_backend_type=ldap_backend_type, erase=erase)
783 samdb = SamDB(path, session_info=session_info,
784 credentials=credentials, lp=lp)
788 message("Pre-loading the Samba 4 and AD schema")
790 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
791 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
792 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
794 samdb.set_domain_sid(str(domainsid))
795 if serverrole == "domain controller":
796 samdb.set_invocation_id(invocationid)
798 schema_data = load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
799 names.configdn, names.sitename, names.serverdn)
800 samdb.transaction_start()
803 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
804 if serverrole == "domain controller":
805 domain_oc = "domainDNS"
807 domain_oc = "samba4LocalDomain"
809 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
810 "DOMAINDN": names.domaindn,
812 "DOMAIN_OC": domain_oc
815 message("Modifying DomainDN: " + names.domaindn + "")
816 if domainguid is not None:
817 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
821 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
822 "LDAPTIME": timestring(int(time.time())),
823 "DOMAINSID": str(domainsid),
824 "SCHEMADN": names.schemadn,
825 "NETBIOSNAME": names.netbiosname,
826 "DEFAULTSITE": names.sitename,
827 "CONFIGDN": names.configdn,
828 "SERVERDN": names.serverdn,
829 "POLICYGUID": policyguid,
830 "DOMAINDN": names.domaindn,
831 "DOMAINGUID_MOD": domainguid_mod,
832 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
835 message("Adding configuration container (permitted to fail)")
836 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
837 "CONFIGDN": names.configdn,
840 message("Modifying configuration container")
841 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
842 "CONFIGDN": names.configdn,
843 "SCHEMADN": names.schemadn,
846 message("Adding schema container (permitted to fail)")
847 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
848 "SCHEMADN": names.schemadn,
851 message("Modifying schema container")
853 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
855 setup_modify_ldif(samdb,
856 setup_path("provision_schema_basedn_modify.ldif"), {
857 "SCHEMADN": names.schemadn,
858 "NETBIOSNAME": names.netbiosname,
859 "DEFAULTSITE": names.sitename,
860 "CONFIGDN": names.configdn,
861 "SERVERDN": names.serverdn,
862 "PREFIXMAP_B64": b64encode(prefixmap)
865 message("Setting up sam.ldb schema")
866 samdb.add_ldif(schema_data)
867 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
868 {"SCHEMADN": names.schemadn})
870 message("Setting up sam.ldb configuration data")
871 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
872 "CONFIGDN": names.configdn,
873 "NETBIOSNAME": names.netbiosname,
874 "DEFAULTSITE": names.sitename,
875 "DNSDOMAIN": names.dnsdomain,
876 "DOMAIN": names.domain,
877 "SCHEMADN": names.schemadn,
878 "DOMAINDN": names.domaindn,
879 "SERVERDN": names.serverdn,
880 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
883 message("Setting up display specifiers")
884 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
885 {"CONFIGDN": names.configdn})
887 message("Adding users container (permitted to fail)")
888 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
889 "DOMAINDN": names.domaindn})
890 message("Modifying users container")
891 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
892 "DOMAINDN": names.domaindn})
893 message("Adding computers container (permitted to fail)")
894 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
895 "DOMAINDN": names.domaindn})
896 message("Modifying computers container")
897 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
898 "DOMAINDN": names.domaindn})
899 message("Setting up sam.ldb data")
900 setup_add_ldif(samdb, setup_path("provision.ldif"), {
901 "DOMAINDN": names.domaindn,
902 "NETBIOSNAME": names.netbiosname,
903 "DEFAULTSITE": names.sitename,
904 "CONFIGDN": names.configdn,
905 "SERVERDN": names.serverdn
908 if fill == FILL_FULL:
909 message("Setting up sam.ldb users and groups")
910 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
911 "DOMAINDN": names.domaindn,
912 "DOMAINSID": str(domainsid),
913 "CONFIGDN": names.configdn,
914 "ADMINPASS_B64": b64encode(adminpass),
915 "KRBTGTPASS_B64": b64encode(krbtgtpass),
918 if serverrole == "domain controller":
919 message("Setting up self join")
920 setup_self_join(samdb, names=names, invocationid=invocationid,
922 machinepass=machinepass,
923 domainsid=domainsid, policyguid=policyguid,
924 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
927 samdb.transaction_cancel()
930 samdb.transaction_commit()
935 FILL_NT4SYNC = "NT4SYNC"
938 def provision(setup_dir, message, session_info,
939 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
940 rootdn=None, domaindn=None, schemadn=None, configdn=None,
942 domain=None, hostname=None, hostip=None, hostip6=None,
943 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
944 policyguid=None, invocationid=None, machinepass=None,
945 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
946 wheel=None, backup=None, aci=None, serverrole=None,
947 ldap_backend=None, ldap_backend_type=None, sitename=None):
950 :note: caution, this wipes all existing data!
953 def setup_path(file):
954 return os.path.join(setup_dir, file)
956 if domainsid is None:
957 domainsid = security.random_sid()
959 if policyguid is None:
960 policyguid = str(uuid.uuid4())
961 if adminpass is None:
962 adminpass = glue.generate_random_str(12)
963 if krbtgtpass is None:
964 krbtgtpass = glue.generate_random_str(12)
965 if machinepass is None:
966 machinepass = glue.generate_random_str(12)
968 dnspass = glue.generate_random_str(12)
969 root_uid = findnss_uid([root or "root"])
970 nobody_uid = findnss_uid([nobody or "nobody"])
971 users_gid = findnss_gid([users or "users"])
973 wheel_gid = findnss_gid(["wheel", "adm"])
975 wheel_gid = findnss_gid([wheel])
977 aci = "# no aci for local ldb"
979 if targetdir is not None:
980 if (not os.path.exists(os.path.join(targetdir, "etc"))):
981 os.makedirs(os.path.join(targetdir, "etc"))
982 smbconf = os.path.join(targetdir, "etc", "smb.conf")
983 elif smbconf is None:
984 smbconf = param.default_path()
986 # only install a new smb.conf if there isn't one there already
987 if not os.path.exists(smbconf):
988 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
991 lp = param.LoadParm()
994 names = guess_names(lp=lp, hostname=hostname, domain=domain,
995 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
996 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
999 paths = provision_paths_from_lp(lp, names.dnsdomain)
1003 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1004 except socket.gaierror, (socket.EAI_NODATA, msg):
1009 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1010 except socket.gaierror, (socket.EAI_NODATA, msg):
1013 if serverrole is None:
1014 serverrole = lp.get("server role")
1016 assert serverrole in ("domain controller", "member server", "standalone")
1017 if invocationid is None and serverrole == "domain controller":
1018 invocationid = str(uuid.uuid4())
1020 if not os.path.exists(paths.private_dir):
1021 os.mkdir(paths.private_dir)
1023 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1025 if ldap_backend is not None:
1026 if ldap_backend == "ldapi":
1027 # provision-backend will set this path suggested slapd command line / fedorads.inf
1028 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1030 # only install a new shares config db if there is none
1031 if not os.path.exists(paths.shareconf):
1032 message("Setting up share.ldb")
1033 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1034 credentials=credentials, lp=lp)
1035 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1038 message("Setting up secrets.ldb")
1039 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1040 session_info=session_info,
1041 credentials=credentials, lp=lp)
1043 message("Setting up the registry")
1044 setup_registry(paths.hklm, setup_path, session_info,
1045 credentials=credentials, lp=lp)
1047 message("Setting up templates db")
1048 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1049 credentials=credentials, lp=lp)
1051 message("Setting up idmap db")
1052 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1053 credentials=credentials, lp=lp)
1055 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1056 credentials=credentials, lp=lp, names=names,
1058 domainsid=domainsid,
1059 aci=aci, domainguid=domainguid, policyguid=policyguid,
1061 adminpass=adminpass, krbtgtpass=krbtgtpass,
1062 invocationid=invocationid,
1063 machinepass=machinepass, dnspass=dnspass,
1064 serverrole=serverrole, ldap_backend=ldap_backend,
1065 ldap_backend_type=ldap_backend_type)
1067 if serverrole == "domain controller":
1068 if paths.netlogon is None:
1069 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1070 message("Please either remove %s or see the template at %s" %
1071 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1072 assert(paths.netlogon is not None)
1074 if paths.sysvol is None:
1075 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1076 message("Please either remove %s or see the template at %s" %
1077 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1078 assert(paths.sysvol is not None)
1080 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1081 "{" + policyguid + "}")
1082 os.makedirs(policy_path, 0755)
1083 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1084 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1085 os.makedirs(os.path.join(policy_path, "User"), 0755)
1086 if not os.path.isdir(paths.netlogon):
1087 os.makedirs(paths.netlogon, 0755)
1089 if samdb_fill == FILL_FULL:
1090 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1091 root_uid=root_uid, nobody_uid=nobody_uid,
1092 users_gid=users_gid, wheel_gid=wheel_gid)
1094 message("Setting up sam.ldb rootDSE marking as synchronized")
1095 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1097 # Only make a zone file on the first DC, it should be replicated with DNS replication
1098 if serverrole == "domain controller":
1099 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1100 credentials=credentials, lp=lp)
1101 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1102 netbiosname=names.netbiosname, domainsid=domainsid,
1103 keytab_path=paths.keytab, samdb_url=paths.samdb,
1104 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1105 machinepass=machinepass, dnsdomain=names.dnsdomain)
1107 samdb = SamDB(paths.samdb, session_info=session_info,
1108 credentials=credentials, lp=lp)
1110 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1111 assert isinstance(domainguid, str)
1112 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1113 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1114 scope=SCOPE_SUBTREE)
1115 assert isinstance(hostguid, str)
1117 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1118 domaindn=names.domaindn, hostip=hostip,
1119 hostip6=hostip6, hostname=names.hostname,
1120 dnspass=dnspass, realm=names.realm,
1121 domainguid=domainguid, hostguid=hostguid)
1123 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1124 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1126 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1127 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1128 keytab_name=paths.dns_keytab)
1129 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1130 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1132 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1133 hostname=names.hostname, realm=names.realm)
1134 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1136 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1139 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1141 message("Once the above files are installed, your Samba4 server will be ready to use")
1142 message("Server Role: %s" % serverrole)
1143 message("Hostname: %s" % names.hostname)
1144 message("NetBIOS Domain: %s" % names.domain)
1145 message("DNS Domain: %s" % names.dnsdomain)
1146 message("DOMAIN SID: %s" % str(domainsid))
1147 if samdb_fill == FILL_FULL:
1148 message("Admin password: %s" % adminpass)
1150 result = ProvisionResult()
1151 result.domaindn = domaindn
1152 result.paths = paths
1154 result.samdb = samdb
1158 def provision_become_dc(setup_dir=None,
1159 smbconf=None, targetdir=None, realm=None,
1160 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1162 domain=None, hostname=None, domainsid=None,
1163 adminpass=None, krbtgtpass=None, domainguid=None,
1164 policyguid=None, invocationid=None, machinepass=None,
1165 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1166 wheel=None, backup=None, aci=None, serverrole=None,
1167 ldap_backend=None, ldap_backend_type=None, sitename=None):
1170 """print a message if quiet is not set."""
1173 return provision(setup_dir, message, system_session(), None,
1174 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1175 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1176 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1179 def setup_db_config(setup_path, dbdir):
1180 """Setup a Berkeley database.
1182 :param setup_path: Setup path function.
1183 :param dbdir: Database directory."""
1184 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1185 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1186 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1187 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1189 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1190 {"LDAPDBDIR": dbdir})
1194 def provision_backend(setup_dir=None, message=None,
1195 smbconf=None, targetdir=None, realm=None,
1196 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1197 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1198 ldap_backend_type=None, ldap_backend_port=None,
1199 ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
1201 def setup_path(file):
1202 return os.path.join(setup_dir, file)
1204 if hostname is None:
1205 hostname = socket.gethostname().split(".")[0].lower()
1208 root = findnss(pwd.getpwnam, ["root"])[0]
1210 if adminpass is None:
1211 adminpass = glue.generate_random_str(12)
1213 if targetdir is not None:
1214 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1215 os.makedirs(os.path.join(targetdir, "etc"))
1216 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1217 elif smbconf is None:
1218 smbconf = param.default_path()
1219 assert smbconf is not None
1221 # only install a new smb.conf if there isn't one there already
1222 if not os.path.exists(smbconf):
1223 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1226 # openldap-online-configuration: validation of olc and slaptest
1227 if ol_olc == "yes" and ol_slaptest is None:
1228 sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slaptest-Binary!")
1230 if ol_olc == "yes" and ol_slaptest is not None:
1231 ol_slaptest = ol_slaptest + "/slaptest"
1232 if not os.path.exists(ol_slaptest):
1233 message (ol_slaptest)
1234 sys.exit("Warning: Given Path to slaptest-Binary does not exist!")
1239 lp = param.LoadParm()
1242 if serverrole is None:
1243 serverrole = lp.get("server role")
1245 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1246 dnsdomain=realm, serverrole=serverrole,
1247 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1250 paths = provision_paths_from_lp(lp, names.dnsdomain)
1252 if not os.path.isdir(paths.ldapdir):
1253 os.makedirs(paths.ldapdir, 0700)
1254 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1256 os.unlink(schemadb_path)
1260 schemadb = SamDB(schemadb_path, lp=lp)
1261 schemadb.transaction_start()
1264 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1266 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1267 {"SCHEMADN": names.schemadn,
1270 setup_modify_ldif(schemadb,
1271 setup_path("provision_schema_basedn_modify.ldif"), \
1272 {"SCHEMADN": names.schemadn,
1273 "NETBIOSNAME": names.netbiosname,
1274 "DEFAULTSITE": DEFAULTSITE,
1275 "CONFIGDN": names.configdn,
1276 "SERVERDN": names.serverdn,
1277 "PREFIXMAP_B64": b64encode(prefixmap)
1280 data = load_schema(setup_path, schemadb, names.schemadn, names.netbiosname,
1281 names.configdn, DEFAULTSITE, names.serverdn)
1282 schemadb.add_ldif(data)
1284 schemadb.transaction_cancel()
1286 schemadb.transaction_commit()
1288 if ldap_backend_type == "fedora-ds":
1289 if ldap_backend_port is not None:
1290 serverport = "ServerPort=%d" % ldap_backend_port
1294 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1296 "HOSTNAME": hostname,
1297 "DNSDOMAIN": names.dnsdomain,
1298 "LDAPDIR": paths.ldapdir,
1299 "DOMAINDN": names.domaindn,
1300 "LDAPMANAGERDN": names.ldapmanagerdn,
1301 "LDAPMANAGERPASS": adminpass,
1302 "SERVERPORT": serverport})
1304 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1305 {"CONFIGDN": names.configdn,
1306 "SCHEMADN": names.schemadn,
1309 mapping = "schema-map-fedora-ds-1.0"
1310 backend_schema = "99_ad.ldif"
1312 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1314 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1316 elif ldap_backend_type == "openldap":
1317 attrs = ["linkID", "lDAPDisplayName"]
1318 res = schemadb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
1320 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1321 refint_attributes = ""
1322 for i in range (0, len(res)):
1323 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1324 target = schemadb.searchone(basedn=names.schemadn,
1325 expression=expression,
1326 attribute="lDAPDisplayName",
1327 scope=SCOPE_SUBTREE)
1328 if target is not None:
1329 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1331 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1332 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1333 "MEMBEROF_ATTR" : str(target) })
1335 refint_config = read_and_sub_file(setup_path("refint.conf"),
1336 { "LINK_ATTRS" : refint_attributes})
1338 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1340 mmr_replicator_acl = ""
1341 mmr_serverids_config = ""
1342 mmr_syncrepl_schema_config = ""
1343 mmr_syncrepl_config_config = ""
1344 mmr_syncrepl_user_config = ""
1347 if ol_mmr_urls is not None:
1348 # For now, make these equal
1349 mmr_pass = adminpass
1351 url_list=filter(None,ol_mmr_urls.split(' '))
1352 if (len(url_list) == 1):
1353 url_list=filter(None,ol_mmr_urls.split(','))
1356 mmr_on_config = "MirrorMode On"
1357 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1359 for url in url_list:
1361 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1362 { "SERVERID" : str(serverid),
1363 "LDAPSERVER" : url })
1366 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1368 "MMRDN": names.schemadn,
1370 "MMR_PASSWORD": mmr_pass})
1373 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1375 "MMRDN": names.configdn,
1377 "MMR_PASSWORD": mmr_pass})
1380 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1382 "MMRDN": names.domaindn,
1384 "MMR_PASSWORD": mmr_pass })
1386 olc_config_pass = ""
1388 olc_syncrepl_config = ""
1391 olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
1392 { "OLC_PW": adminpass })
1393 olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
1395 # if olc = yes + mmr = yes, generate cn=config-replication directives
1396 # and olc_seed.lif for the other mmr-servers
1397 if ol_olc == "yes" and ol_mmr_urls is not None:
1399 olc_serverids_config = ""
1400 olc_syncrepl_config = ""
1401 olc_syncrepl_seed_config = ""
1403 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1405 for url in url_list:
1407 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1408 { "SERVERID" : str(serverid),
1409 "LDAPSERVER" : url })
1412 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1415 "MMR_PASSWORD": adminpass})
1417 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1419 "LDAPSERVER" : url})
1421 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1422 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1423 "OLC_PW": adminpass,
1424 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1429 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1430 {"DNSDOMAIN": names.dnsdomain,
1431 "LDAPDIR": paths.ldapdir,
1432 "DOMAINDN": names.domaindn,
1433 "CONFIGDN": names.configdn,
1434 "SCHEMADN": names.schemadn,
1435 "MEMBEROF_CONFIG": memberof_config,
1436 "MIRRORMODE": mmr_on_config,
1437 "REPLICATOR_ACL": mmr_replicator_acl,
1438 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1439 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1440 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1441 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1442 "OLC_CONFIG_PASS": olc_config_pass,
1443 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1444 "OLC_CONFIG_ACL": olc_config_acl,
1445 "OLC_MMR_CONFIG": olc_mmr_config,
1446 "REFINT_CONFIG": refint_config})
1447 setup_file(setup_path("modules.conf"), paths.modulesconf,
1448 {"REALM": names.realm})
1450 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1451 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1452 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1454 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1455 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1457 setup_file(setup_path("cn=samba.ldif"),
1458 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1459 { "UUID": str(uuid.uuid4()),
1460 "LDAPTIME": timestring(int(time.time()))} )
1461 setup_file(setup_path("cn=samba-admin.ldif"),
1462 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1463 {"LDAPADMINPASS_B64": b64encode(adminpass),
1464 "UUID": str(uuid.uuid4()),
1465 "LDAPTIME": timestring(int(time.time()))} )
1467 if ol_mmr_urls is not None:
1468 setup_file(setup_path("cn=replicator.ldif"),
1469 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1470 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1471 "UUID": str(uuid.uuid4()),
1472 "LDAPTIME": timestring(int(time.time()))} )
1475 mapping = "schema-map-openldap-2.3"
1476 backend_schema = "backend-schema.schema"
1478 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1479 if ldap_backend_port is not None:
1480 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1482 server_port_string = ""
1484 if ol_olc != "yes" and ol_mmr_urls is None:
1485 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1487 if ol_olc == "yes" and ol_mmr_urls is None:
1488 slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1490 if ol_olc != "yes" and ol_mmr_urls is not None:
1491 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1493 if ol_olc == "yes" and ol_mmr_urls is not None:
1494 slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1497 ldapuser = "--username=samba-admin"
1500 backend_schema_data = schemadb.convert_schema_to_openldap(ldap_backend_type, open(setup_path(mapping), 'r').read())
1501 assert backend_schema_data is not None
1502 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1504 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1505 message("Server Role: %s" % serverrole)
1506 message("Hostname: %s" % names.hostname)
1507 message("DNS Domain: %s" % names.dnsdomain)
1508 message("Base DN: %s" % names.domaindn)
1510 if ldap_backend_type == "openldap":
1511 message("LDAP admin user: samba-admin")
1513 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1515 message("LDAP admin password: %s" % adminpass)
1516 message(slapdcommand)
1517 if ol_olc == "yes" or ol_mmr_urls is not None:
1518 message("Attention to slapd-Port: <PORT> must be different than 389!")
1519 assert isinstance(ldap_backend_type, str)
1520 assert isinstance(ldapuser, str)
1521 assert isinstance(adminpass, str)
1522 assert isinstance(names.dnsdomain, str)
1523 assert isinstance(names.domain, str)
1524 assert isinstance(serverrole, str)
1525 args = ["--ldap-backend=ldapi",
1526 "--ldap-backend-type=" + ldap_backend_type,
1527 "--password=" + adminpass,
1529 "--realm=" + names.dnsdomain,
1530 "--domain=" + names.domain,
1531 "--server-role='" + serverrole + "'"]
1532 message("Run provision with: " + " ".join(args))
1535 # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d
1537 if not os.path.isdir(paths.olcdir):
1538 os.makedirs(paths.olcdir, 0770)
1539 paths.olslaptest = str(ol_slaptest)
1540 olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir + " >/dev/null 2>&1"
1541 os.system(olc_command)
1542 os.remove(paths.slapdconf)
1543 # use line below for debugging during olc-conversion with slaptest, instead of olc_command above
1544 #olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir"
1547 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1548 """Create a PHP LDAP admin configuration file.
1550 :param path: Path to write the configuration to.
1551 :param setup_path: Function to generate setup paths.
1553 setup_file(setup_path("phpldapadmin-config.php"), path,
1554 {"S4_LDAPI_URI": ldapi_uri})
1557 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1558 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1559 """Write out a DNS zone file, from the info in the current database.
1561 :param path: Path of the new zone file.
1562 :param setup_path: Setup path function.
1563 :param dnsdomain: DNS Domain name
1564 :param domaindn: DN of the Domain
1565 :param hostip: Local IPv4 IP
1566 :param hostip6: Local IPv6 IP
1567 :param hostname: Local hostname
1568 :param dnspass: Password for DNS
1569 :param realm: Realm name
1570 :param domainguid: GUID of the domain.
1571 :param hostguid: GUID of the host.
1573 assert isinstance(domainguid, str)
1575 if hostip6 is not None:
1576 hostip6_base_line = " IN AAAA " + hostip6
1577 hostip6_host_line = hostname + " IN AAAA " + hostip6
1579 hostip6_base_line = ""
1580 hostip6_host_line = ""
1582 if hostip is not None:
1583 hostip_base_line = " IN A " + hostip
1584 hostip_host_line = hostname + " IN A " + hostip
1586 hostip_base_line = ""
1587 hostip_host_line = ""
1589 setup_file(setup_path("provision.zone"), path, {
1590 "DNSPASS_B64": b64encode(dnspass),
1591 "HOSTNAME": hostname,
1592 "DNSDOMAIN": dnsdomain,
1594 "HOSTIP_BASE_LINE": hostip_base_line,
1595 "HOSTIP_HOST_LINE": hostip_host_line,
1596 "DOMAINGUID": domainguid,
1597 "DATESTRING": time.strftime("%Y%m%d%H"),
1598 "DEFAULTSITE": DEFAULTSITE,
1599 "HOSTGUID": hostguid,
1600 "HOSTIP6_BASE_LINE": hostip6_base_line,
1601 "HOSTIP6_HOST_LINE": hostip6_host_line,
1605 def create_named_conf(path, setup_path, realm, dnsdomain,
1607 """Write out a file containing zone statements suitable for inclusion in a
1608 named.conf file (including GSS-TSIG configuration).
1610 :param path: Path of the new named.conf file.
1611 :param setup_path: Setup path function.
1612 :param realm: Realm name
1613 :param dnsdomain: DNS Domain name
1614 :param private_dir: Path to private directory
1615 :param keytab_name: File name of DNS keytab file
1618 setup_file(setup_path("named.conf"), path, {
1619 "DNSDOMAIN": dnsdomain,
1621 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1622 "PRIVATE_DIR": private_dir
1625 def create_named_txt(path, setup_path, realm, dnsdomain,
1626 private_dir, keytab_name):
1627 """Write out a file containing zone statements suitable for inclusion in a
1628 named.conf file (including GSS-TSIG configuration).
1630 :param path: Path of the new named.conf file.
1631 :param setup_path: Setup path function.
1632 :param realm: Realm name
1633 :param dnsdomain: DNS Domain name
1634 :param private_dir: Path to private directory
1635 :param keytab_name: File name of DNS keytab file
1638 setup_file(setup_path("named.txt"), path, {
1639 "DNSDOMAIN": dnsdomain,
1641 "DNS_KEYTAB": keytab_name,
1642 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1643 "PRIVATE_DIR": private_dir
1646 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1647 """Write out a file containing zone statements suitable for inclusion in a
1648 named.conf file (including GSS-TSIG configuration).
1650 :param path: Path of the new named.conf file.
1651 :param setup_path: Setup path function.
1652 :param dnsdomain: DNS Domain name
1653 :param hostname: Local hostname
1654 :param realm: Realm name
1657 setup_file(setup_path("krb5.conf"), path, {
1658 "DNSDOMAIN": dnsdomain,
1659 "HOSTNAME": hostname,
1664 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1666 """Load schema for the SamDB.
1668 :param samdb: Load a schema into a SamDB.
1669 :param setup_path: Setup path function.
1670 :param schemadn: DN of the schema
1671 :param netbiosname: NetBIOS name of the host.
1672 :param configdn: DN of the configuration
1673 :param serverdn: DN of the server
1675 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
1677 schema_data = get_schema_data(setup_path, {"SCHEMADN": schemadn})
1678 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1679 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1680 check_all_substituted(schema_data)
1681 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1682 prefixmap = b64encode(prefixmap)
1684 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1685 head_data = substitute_var(head_data, {
1686 "SCHEMADN": schemadn,
1687 "NETBIOSNAME": netbiosname,
1688 "CONFIGDN": configdn,
1689 "DEFAULTSITE": sitename,
1690 "PREFIXMAP_B64": prefixmap,
1691 "SERVERDN": serverdn,
1693 check_all_substituted(head_data)
1694 samdb.attach_schema_from_ldif(head_data, schema_data)
1697 def get_schema_data(setup_path, subst_vars = None):
1698 """Get schema data from the AD schema files instead of schema.ldif.
1700 :param setup_path: Setup path function.
1701 :param subst_vars: Optional variables to substitute in the file.
1703 Returns the schema data after substitution
1706 # this data used to be read from schema.ldif
1708 data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
1709 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
1711 if subst_vars is not None:
1712 data = substitute_var(data, subst_vars)
1713 check_all_substituted(data)