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 Ldb, substitute_var, valid_netbios_name, check_all_substituted
41 from samba.samdb import SamDB
42 from samba.idmap import IDmapDB
43 from samba.dcerpc import security
45 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
46 timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
48 __docformat__ = "restructuredText"
52 """Find the setup directory used by provision."""
53 dirname = os.path.dirname(__file__)
54 if "/site-packages/" in dirname:
55 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
56 for suffix in ["share/setup", "share/samba/setup", "setup"]:
57 ret = os.path.join(prefix, suffix)
58 if os.path.isdir(ret):
61 ret = os.path.join(dirname, "../../../setup")
62 if os.path.isdir(ret):
64 raise Exception("Unable to find setup directory.")
67 DEFAULTSITE = "Default-First-Site-Name"
69 class InvalidNetbiosName(Exception):
70 """A specified name was not a valid NetBIOS name."""
71 def __init__(self, name):
72 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
75 class ProvisionPaths(object):
88 self.dns_keytab = None
91 self.private_dir = None
94 self.modulesconf = None
95 self.memberofconf = None
96 self.fedoradsinf = None
97 self.fedoradspartitions = None
99 self.olmmrserveridsconf = None
100 self.olmmrsyncreplconf = None
102 self.olslaptest = None
103 self.olcseedldif = None
106 class ProvisionNames(object):
112 self.ldapmanagerdn = None
113 self.dnsdomain = None
115 self.netbiosname = None
122 class ProvisionResult(object):
129 def check_install(lp, session_info, credentials):
130 """Check whether the current install seems ok.
132 :param lp: Loadparm context
133 :param session_info: Session information
134 :param credentials: Credentials
136 if lp.get("realm") == "":
137 raise Exception("Realm empty")
138 ldb = Ldb(lp.get("sam database"), session_info=session_info,
139 credentials=credentials, lp=lp)
140 if len(ldb.search("(cn=Administrator)")) != 1:
141 raise "No administrator account found"
144 def findnss(nssfn, names):
145 """Find a user or group from a list of possibilities.
147 :param nssfn: NSS Function to try (should raise KeyError if not found)
148 :param names: Names to check.
149 :return: Value return by first names list.
156 raise KeyError("Unable to find user/group %r" % names)
159 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
160 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
163 def read_and_sub_file(file, subst_vars):
164 """Read a file and sub in variables found in it
166 :param file: File to be read (typically from setup directory)
167 param subst_vars: Optional variables to subsitute in the file.
169 data = open(file, 'r').read()
170 if subst_vars is not None:
171 data = substitute_var(data, subst_vars)
172 check_all_substituted(data)
176 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
177 """Setup a ldb in the private dir.
179 :param ldb: LDB file to import data into
180 :param ldif_path: Path of the LDIF file to load
181 :param subst_vars: Optional variables to subsitute in LDIF.
183 assert isinstance(ldif_path, str)
185 data = read_and_sub_file(ldif_path, subst_vars)
189 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
190 """Modify a ldb in the private dir.
192 :param ldb: LDB object.
193 :param ldif_path: LDIF file path.
194 :param subst_vars: Optional dictionary with substitution variables.
196 data = read_and_sub_file(ldif_path, subst_vars)
198 ldb.modify_ldif(data)
201 def setup_ldb(ldb, ldif_path, subst_vars):
202 """Import a LDIF a file into a LDB handle, optionally substituting variables.
204 :note: Either all LDIF data will be added or none (using transactions).
206 :param ldb: LDB file to import into.
207 :param ldif_path: Path to the LDIF file.
208 :param subst_vars: Dictionary with substitution variables.
210 assert ldb is not None
211 ldb.transaction_start()
213 setup_add_ldif(ldb, ldif_path, subst_vars)
215 ldb.transaction_cancel()
217 ldb.transaction_commit()
220 def setup_file(template, fname, subst_vars):
221 """Setup a file in the private dir.
223 :param template: Path of the template file.
224 :param fname: Path of the file to create.
225 :param subst_vars: Substitution variables.
229 if os.path.exists(f):
232 data = read_and_sub_file(template, subst_vars)
233 open(f, 'w').write(data)
236 def provision_paths_from_lp(lp, dnsdomain):
237 """Set the default paths for provisioning.
239 :param lp: Loadparm context.
240 :param dnsdomain: DNS Domain name
242 paths = ProvisionPaths()
243 paths.private_dir = lp.get("private dir")
244 paths.keytab = "secrets.keytab"
245 paths.dns_keytab = "dns.keytab"
247 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
248 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
249 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
250 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
251 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
252 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
253 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
254 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
255 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
256 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
257 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
258 paths.phpldapadminconfig = os.path.join(paths.private_dir,
259 "phpldapadmin-config.php")
260 paths.ldapdir = os.path.join(paths.private_dir,
262 paths.slapdconf = os.path.join(paths.ldapdir,
264 paths.modulesconf = os.path.join(paths.ldapdir,
266 paths.memberofconf = os.path.join(paths.ldapdir,
268 paths.fedoradsinf = os.path.join(paths.ldapdir,
270 paths.fedoradspartitions = os.path.join(paths.ldapdir,
271 "fedorads-partitions.ldif")
272 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
273 "mmr_serverids.conf")
274 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
276 paths.olcdir = os.path.join(paths.ldapdir,
278 paths.olcseedldif = os.path.join(paths.ldapdir,
280 paths.hklm = "hklm.ldb"
281 paths.hkcr = "hkcr.ldb"
282 paths.hkcu = "hkcu.ldb"
283 paths.hku = "hku.ldb"
284 paths.hkpd = "hkpd.ldb"
285 paths.hkpt = "hkpt.ldb"
287 paths.sysvol = lp.get("path", "sysvol")
289 paths.netlogon = lp.get("path", "netlogon")
291 paths.smbconf = lp.configfile
296 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
297 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
299 """Guess configuration settings to use."""
302 hostname = socket.gethostname().split(".")[0].lower()
304 netbiosname = hostname.upper()
305 if not valid_netbios_name(netbiosname):
306 raise InvalidNetbiosName(netbiosname)
308 hostname = hostname.lower()
310 if dnsdomain is None:
311 dnsdomain = lp.get("realm")
313 if serverrole is None:
314 serverrole = lp.get("server role")
316 assert dnsdomain is not None
317 realm = dnsdomain.upper()
319 if lp.get("realm").upper() != realm:
320 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
321 (lp.get("realm"), lp.configfile, realm))
323 dnsdomain = dnsdomain.lower()
325 if serverrole == "domain controller":
327 domain = lp.get("workgroup")
329 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
330 if lp.get("workgroup").upper() != domain.upper():
331 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
332 lp.get("workgroup"), domain)
336 domaindn = "CN=" + netbiosname
338 assert domain is not None
339 domain = domain.upper()
340 if not valid_netbios_name(domain):
341 raise InvalidNetbiosName(domain)
347 configdn = "CN=Configuration," + rootdn
349 schemadn = "CN=Schema," + configdn
354 names = ProvisionNames()
355 names.rootdn = rootdn
356 names.domaindn = domaindn
357 names.configdn = configdn
358 names.schemadn = schemadn
359 names.ldapmanagerdn = "CN=Manager," + rootdn
360 names.dnsdomain = dnsdomain
361 names.domain = domain
363 names.netbiosname = netbiosname
364 names.hostname = hostname
365 names.sitename = sitename
366 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
371 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
373 """Create a new smb.conf file based on a couple of basic settings.
375 assert smbconf is not None
377 hostname = socket.gethostname().split(".")[0].lower()
379 if serverrole is None:
380 serverrole = "standalone"
382 assert serverrole in ("domain controller", "member server", "standalone")
383 if serverrole == "domain controller":
385 elif serverrole == "member server":
386 smbconfsuffix = "member"
387 elif serverrole == "standalone":
388 smbconfsuffix = "standalone"
390 assert domain is not None
391 assert realm is not None
393 default_lp = param.LoadParm()
394 #Load non-existant file
395 if os.path.exists(smbconf):
396 default_lp.load(smbconf)
398 if targetdir is not None:
399 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
400 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
402 default_lp.set("lock dir", os.path.abspath(targetdir))
407 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
408 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
410 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
412 "HOSTNAME": hostname,
415 "SERVERROLE": serverrole,
416 "NETLOGONPATH": netlogon,
417 "SYSVOLPATH": sysvol,
418 "PRIVATEDIR_LINE": privatedir_line,
419 "LOCKDIR_LINE": lockdir_line
423 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
424 users_gid, wheel_gid):
425 """setup reasonable name mappings for sam names to unix names.
427 :param samdb: SamDB object.
428 :param idmap: IDmap db object.
429 :param sid: The domain sid.
430 :param domaindn: The domain DN.
431 :param root_uid: uid of the UNIX root user.
432 :param nobody_uid: uid of the UNIX nobody user.
433 :param users_gid: gid of the UNIX users group.
434 :param wheel_gid: gid of the UNIX wheel group."""
435 # add some foreign sids if they are not present already
436 samdb.add_stock_foreign_sids()
438 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
439 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
441 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
442 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
445 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
447 serverrole, ldap_backend=None,
448 ldap_backend_type=None, erase=False):
449 """Setup the partitions for the SAM database.
451 Alternatively, provision() may call this, and then populate the database.
453 :note: This will wipe the Sam Database!
455 :note: This function always removes the local SAM LDB file. The erase
456 parameter controls whether to erase the existing data, which
457 may not be stored locally but in LDAP.
459 assert session_info is not None
462 samdb = SamDB(samdb_path, session_info=session_info,
463 credentials=credentials, lp=lp)
467 os.unlink(samdb_path)
468 samdb = SamDB(samdb_path, session_info=session_info,
469 credentials=credentials, lp=lp)
474 #Add modules to the list to activate them by default
475 #beware often order is important
477 # Some Known ordering constraints:
478 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
479 # - objectclass must be before password_hash, because password_hash checks
480 # that the objectclass is of type person (filled in by objectclass
481 # module when expanding the objectclass list)
482 # - partition must be last
483 # - each partition has its own module list then
484 modules_list = ["rootdse",
502 "extended_dn_out_ldb"]
503 modules_list2 = ["show_deleted",
506 domaindn_ldb = "users.ldb"
507 if ldap_backend is not None:
508 domaindn_ldb = ldap_backend
509 configdn_ldb = "configuration.ldb"
510 if ldap_backend is not None:
511 configdn_ldb = ldap_backend
512 schemadn_ldb = "schema.ldb"
513 if ldap_backend is not None:
514 schema_ldb = ldap_backend
515 schemadn_ldb = ldap_backend
517 if ldap_backend_type == "fedora-ds":
518 backend_modules = ["nsuniqueid", "paged_searches"]
519 # We can handle linked attributes here, as we don't have directory-side subtree operations
520 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
521 elif ldap_backend_type == "openldap":
522 backend_modules = ["entryuuid", "paged_searches"]
523 # OpenLDAP handles subtree renames, so we don't want to do any of these things
524 tdb_modules_list = ["extended_dn_out_dereference"]
525 elif ldap_backend is not None:
526 raise "LDAP Backend specified, but LDAP Backend Type not specified"
527 elif serverrole == "domain controller":
528 backend_modules = ["repl_meta_data"]
530 backend_modules = ["objectguid"]
532 if tdb_modules_list is None:
533 tdb_modules_list_as_string = ""
535 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
537 samdb.transaction_start()
539 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
540 "SCHEMADN": names.schemadn,
541 "SCHEMADN_LDB": schemadn_ldb,
542 "SCHEMADN_MOD2": ",objectguid",
543 "CONFIGDN": names.configdn,
544 "CONFIGDN_LDB": configdn_ldb,
545 "DOMAINDN": names.domaindn,
546 "DOMAINDN_LDB": domaindn_ldb,
547 "SCHEMADN_MOD": "schema_fsmo,instancetype",
548 "CONFIGDN_MOD": "naming_fsmo,instancetype",
549 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
550 "MODULES_LIST": ",".join(modules_list),
551 "TDB_MODULES_LIST": tdb_modules_list_as_string,
552 "MODULES_LIST2": ",".join(modules_list2),
553 "BACKEND_MOD": ",".join(backend_modules),
557 samdb.transaction_cancel()
560 samdb.transaction_commit()
562 samdb = SamDB(samdb_path, session_info=session_info,
563 credentials=credentials, lp=lp)
565 samdb.transaction_start()
567 message("Setting up sam.ldb attributes")
568 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
570 message("Setting up sam.ldb rootDSE")
571 setup_samdb_rootdse(samdb, setup_path, names)
574 message("Erasing data from partitions")
575 samdb.erase_partitions()
578 samdb.transaction_cancel()
581 samdb.transaction_commit()
586 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
587 netbiosname, domainsid, keytab_path, samdb_url,
588 dns_keytab_path, dnspass, machinepass):
589 """Add DC-specific bits to a secrets database.
591 :param secretsdb: Ldb Handle to the secrets database
592 :param setup_path: Setup path function
593 :param machinepass: Machine password
595 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
596 "MACHINEPASS_B64": b64encode(machinepass),
599 "DNSDOMAIN": dnsdomain,
600 "DOMAINSID": str(domainsid),
601 "SECRETS_KEYTAB": keytab_path,
602 "NETBIOSNAME": netbiosname,
603 "SAM_LDB": samdb_url,
604 "DNS_KEYTAB": dns_keytab_path,
605 "DNSPASS_B64": b64encode(dnspass),
609 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
610 """Setup the secrets database.
612 :param path: Path to the secrets database.
613 :param setup_path: Get the path to a setup file.
614 :param session_info: Session info.
615 :param credentials: Credentials
616 :param lp: Loadparm context
617 :return: LDB handle for the created secrets database
619 if os.path.exists(path):
621 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
624 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
625 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
627 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
629 if credentials is not None and credentials.authentication_requested():
630 if credentials.get_bind_dn() is not None:
631 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
632 "LDAPMANAGERDN": credentials.get_bind_dn(),
633 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
636 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
637 "LDAPADMINUSER": credentials.get_username(),
638 "LDAPADMINREALM": credentials.get_realm(),
639 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
645 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
646 """Setup the templates database.
648 :param path: Path to the database.
649 :param setup_path: Function for obtaining the path to setup files.
650 :param session_info: Session info
651 :param credentials: Credentials
652 :param lp: Loadparm context
654 templates_ldb = SamDB(path, session_info=session_info,
655 credentials=credentials, lp=lp)
658 templates_ldb.erase()
659 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
663 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
665 templates_ldb = SamDB(path, session_info=session_info,
666 credentials=credentials, lp=lp)
668 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
671 def setup_registry(path, setup_path, session_info, credentials, lp):
672 """Setup the registry.
674 :param path: Path to the registry database
675 :param setup_path: Function that returns the path to a setup.
676 :param session_info: Session information
677 :param credentials: Credentials
678 :param lp: Loadparm context
680 reg = registry.Registry()
681 hive = registry.open_ldb(path, session_info=session_info,
682 credentials=credentials, lp_ctx=lp)
683 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
684 provision_reg = setup_path("provision.reg")
685 assert os.path.exists(provision_reg)
686 reg.diff_apply(provision_reg)
689 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
690 """Setup the idmap database.
692 :param path: path to the idmap database
693 :param setup_path: Function that returns a path to a setup file
694 :param session_info: Session information
695 :param credentials: Credentials
696 :param lp: Loadparm context
698 if os.path.exists(path):
701 idmap_ldb = IDmapDB(path, session_info=session_info,
702 credentials=credentials, lp=lp)
705 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
709 def setup_samdb_rootdse(samdb, setup_path, names):
710 """Setup the SamDB rootdse.
712 :param samdb: Sam Database handle
713 :param setup_path: Obtain setup path
715 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
716 "SCHEMADN": names.schemadn,
717 "NETBIOSNAME": names.netbiosname,
718 "DNSDOMAIN": names.dnsdomain,
719 "REALM": names.realm,
720 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
721 "DOMAINDN": names.domaindn,
722 "ROOTDN": names.rootdn,
723 "CONFIGDN": names.configdn,
724 "SERVERDN": names.serverdn,
728 def setup_self_join(samdb, names,
729 machinepass, dnspass,
730 domainsid, invocationid, setup_path,
732 """Join a host to its own domain."""
733 assert isinstance(invocationid, str)
734 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
735 "CONFIGDN": names.configdn,
736 "SCHEMADN": names.schemadn,
737 "DOMAINDN": names.domaindn,
738 "SERVERDN": names.serverdn,
739 "INVOCATIONID": invocationid,
740 "NETBIOSNAME": names.netbiosname,
741 "DEFAULTSITE": names.sitename,
742 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
743 "MACHINEPASS_B64": b64encode(machinepass),
744 "DNSPASS_B64": b64encode(dnspass),
745 "REALM": names.realm,
746 "DOMAIN": names.domain,
747 "DNSDOMAIN": names.dnsdomain})
748 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
749 "POLICYGUID": policyguid,
750 "DNSDOMAIN": names.dnsdomain,
751 "DOMAINSID": str(domainsid),
752 "DOMAINDN": names.domaindn})
755 def setup_samdb(path, setup_path, session_info, credentials, lp,
757 domainsid, aci, domainguid, policyguid,
758 fill, adminpass, krbtgtpass,
759 machinepass, invocationid, dnspass,
760 serverrole, ldap_backend=None,
761 ldap_backend_type=None):
762 """Setup a complete SAM Database.
764 :note: This will wipe the main SAM database file!
767 erase = (fill != FILL_DRS)
769 # Also wipes the database
770 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
771 credentials=credentials, session_info=session_info,
773 ldap_backend=ldap_backend, serverrole=serverrole,
774 ldap_backend_type=ldap_backend_type, erase=erase)
776 samdb = SamDB(path, session_info=session_info,
777 credentials=credentials, lp=lp)
781 message("Pre-loading the Samba 4 and AD schema")
782 samdb.set_domain_sid(str(domainsid))
783 if serverrole == "domain controller":
784 samdb.set_invocation_id(invocationid)
786 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
787 names.configdn, names.sitename, names.serverdn,
790 samdb.transaction_start()
793 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
794 if serverrole == "domain controller":
795 domain_oc = "domainDNS"
797 domain_oc = "samba4LocalDomain"
799 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
800 "DOMAINDN": names.domaindn,
802 "DOMAIN_OC": domain_oc
805 message("Modifying DomainDN: " + names.domaindn + "")
806 if domainguid is not None:
807 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
811 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
812 "LDAPTIME": timestring(int(time.time())),
813 "DOMAINSID": str(domainsid),
814 "SCHEMADN": names.schemadn,
815 "NETBIOSNAME": names.netbiosname,
816 "DEFAULTSITE": names.sitename,
817 "CONFIGDN": names.configdn,
818 "SERVERDN": names.serverdn,
819 "POLICYGUID": policyguid,
820 "DOMAINDN": names.domaindn,
821 "DOMAINGUID_MOD": domainguid_mod,
824 message("Adding configuration container (permitted to fail)")
825 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
826 "CONFIGDN": names.configdn,
829 message("Modifying configuration container")
830 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
831 "CONFIGDN": names.configdn,
832 "SCHEMADN": names.schemadn,
835 message("Adding schema container (permitted to fail)")
836 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
837 "SCHEMADN": names.schemadn,
840 message("Modifying schema container")
842 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
844 setup_modify_ldif(samdb,
845 setup_path("provision_schema_basedn_modify.ldif"), {
846 "SCHEMADN": names.schemadn,
847 "NETBIOSNAME": names.netbiosname,
848 "DEFAULTSITE": names.sitename,
849 "CONFIGDN": names.configdn,
850 "SERVERDN": names.serverdn,
851 "PREFIXMAP_B64": b64encode(prefixmap)
854 message("Setting up sam.ldb Samba4 schema")
855 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
856 {"SCHEMADN": names.schemadn })
857 message("Setting up sam.ldb AD schema")
858 setup_add_ldif(samdb, setup_path("schema.ldif"),
859 {"SCHEMADN": names.schemadn})
860 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
861 {"SCHEMADN": names.schemadn})
863 message("Setting up sam.ldb configuration data")
864 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
865 "CONFIGDN": names.configdn,
866 "NETBIOSNAME": names.netbiosname,
867 "DEFAULTSITE": names.sitename,
868 "DNSDOMAIN": names.dnsdomain,
869 "DOMAIN": names.domain,
870 "SCHEMADN": names.schemadn,
871 "DOMAINDN": names.domaindn,
872 "SERVERDN": names.serverdn
875 message("Setting up display specifiers")
876 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
877 {"CONFIGDN": names.configdn})
879 message("Adding users container (permitted to fail)")
880 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
881 "DOMAINDN": names.domaindn})
882 message("Modifying users container")
883 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
884 "DOMAINDN": names.domaindn})
885 message("Adding computers container (permitted to fail)")
886 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
887 "DOMAINDN": names.domaindn})
888 message("Modifying computers container")
889 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
890 "DOMAINDN": names.domaindn})
891 message("Setting up sam.ldb data")
892 setup_add_ldif(samdb, setup_path("provision.ldif"), {
893 "DOMAINDN": names.domaindn,
894 "NETBIOSNAME": names.netbiosname,
895 "DEFAULTSITE": names.sitename,
896 "CONFIGDN": names.configdn,
897 "SERVERDN": names.serverdn
900 if fill == FILL_FULL:
901 message("Setting up sam.ldb users and groups")
902 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
903 "DOMAINDN": names.domaindn,
904 "DOMAINSID": str(domainsid),
905 "CONFIGDN": names.configdn,
906 "ADMINPASS_B64": b64encode(adminpass),
907 "KRBTGTPASS_B64": b64encode(krbtgtpass),
910 if serverrole == "domain controller":
911 message("Setting up self join")
912 setup_self_join(samdb, names=names, invocationid=invocationid,
914 machinepass=machinepass,
915 domainsid=domainsid, policyguid=policyguid,
916 setup_path=setup_path)
919 samdb.transaction_cancel()
922 samdb.transaction_commit()
927 FILL_NT4SYNC = "NT4SYNC"
930 def provision(setup_dir, message, session_info,
931 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
932 rootdn=None, domaindn=None, schemadn=None, configdn=None,
934 domain=None, hostname=None, hostip=None, hostip6=None,
935 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
936 policyguid=None, invocationid=None, machinepass=None,
937 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
938 wheel=None, backup=None, aci=None, serverrole=None,
939 ldap_backend=None, ldap_backend_type=None, sitename=None):
942 :note: caution, this wipes all existing data!
945 def setup_path(file):
946 return os.path.join(setup_dir, file)
948 if domainsid is None:
949 domainsid = security.random_sid()
951 if policyguid is None:
952 policyguid = str(uuid.uuid4())
953 if adminpass is None:
954 adminpass = glue.generate_random_str(12)
955 if krbtgtpass is None:
956 krbtgtpass = glue.generate_random_str(12)
957 if machinepass is None:
958 machinepass = glue.generate_random_str(12)
960 dnspass = glue.generate_random_str(12)
961 root_uid = findnss_uid([root or "root"])
962 nobody_uid = findnss_uid([nobody or "nobody"])
963 users_gid = findnss_gid([users or "users"])
965 wheel_gid = findnss_gid(["wheel", "adm"])
967 wheel_gid = findnss_gid([wheel])
969 aci = "# no aci for local ldb"
971 if targetdir is not None:
972 if (not os.path.exists(os.path.join(targetdir, "etc"))):
973 os.makedirs(os.path.join(targetdir, "etc"))
974 smbconf = os.path.join(targetdir, "etc", "smb.conf")
975 elif smbconf is None:
976 smbconf = param.default_path()
978 # only install a new smb.conf if there isn't one there already
979 if not os.path.exists(smbconf):
980 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
983 lp = param.LoadParm()
986 names = guess_names(lp=lp, hostname=hostname, domain=domain,
987 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
988 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
991 paths = provision_paths_from_lp(lp, names.dnsdomain)
995 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
996 except socket.gaierror, (socket.EAI_NODATA, msg):
1001 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1002 except socket.gaierror, (socket.EAI_NODATA, msg):
1005 if serverrole is None:
1006 serverrole = lp.get("server role")
1008 assert serverrole in ("domain controller", "member server", "standalone")
1009 if invocationid is None and serverrole == "domain controller":
1010 invocationid = str(uuid.uuid4())
1012 if not os.path.exists(paths.private_dir):
1013 os.mkdir(paths.private_dir)
1015 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1017 if ldap_backend is not None:
1018 if ldap_backend == "ldapi":
1019 # provision-backend will set this path suggested slapd command line / fedorads.inf
1020 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1022 # only install a new shares config db if there is none
1023 if not os.path.exists(paths.shareconf):
1024 message("Setting up share.ldb")
1025 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1026 credentials=credentials, lp=lp)
1027 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1030 message("Setting up secrets.ldb")
1031 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1032 session_info=session_info,
1033 credentials=credentials, lp=lp)
1035 message("Setting up the registry")
1036 setup_registry(paths.hklm, setup_path, session_info,
1037 credentials=credentials, lp=lp)
1039 message("Setting up templates db")
1040 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1041 credentials=credentials, lp=lp)
1043 message("Setting up idmap db")
1044 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1045 credentials=credentials, lp=lp)
1047 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1048 credentials=credentials, lp=lp, names=names,
1050 domainsid=domainsid,
1051 aci=aci, domainguid=domainguid, policyguid=policyguid,
1053 adminpass=adminpass, krbtgtpass=krbtgtpass,
1054 invocationid=invocationid,
1055 machinepass=machinepass, dnspass=dnspass,
1056 serverrole=serverrole, ldap_backend=ldap_backend,
1057 ldap_backend_type=ldap_backend_type)
1059 if serverrole == "domain controller":
1060 if paths.netlogon is None:
1061 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1062 message("Please either remove %s or see the template at %s" %
1063 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1064 assert(paths.netlogon is not None)
1066 if paths.sysvol is None:
1067 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1068 message("Please either remove %s or see the template at %s" %
1069 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1070 assert(paths.sysvol is not None)
1072 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1073 "{" + policyguid + "}")
1074 os.makedirs(policy_path, 0755)
1075 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1076 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1077 os.makedirs(os.path.join(policy_path, "User"), 0755)
1078 if not os.path.isdir(paths.netlogon):
1079 os.makedirs(paths.netlogon, 0755)
1081 if samdb_fill == FILL_FULL:
1082 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1083 root_uid=root_uid, nobody_uid=nobody_uid,
1084 users_gid=users_gid, wheel_gid=wheel_gid)
1086 message("Setting up sam.ldb rootDSE marking as synchronized")
1087 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1089 # Only make a zone file on the first DC, it should be replicated with DNS replication
1090 if serverrole == "domain controller":
1091 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1092 credentials=credentials, lp=lp)
1093 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1094 netbiosname=names.netbiosname, domainsid=domainsid,
1095 keytab_path=paths.keytab, samdb_url=paths.samdb,
1096 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1097 machinepass=machinepass, dnsdomain=names.dnsdomain)
1099 samdb = SamDB(paths.samdb, session_info=session_info,
1100 credentials=credentials, lp=lp)
1102 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1103 assert isinstance(domainguid, str)
1104 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1105 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1106 scope=SCOPE_SUBTREE)
1107 assert isinstance(hostguid, str)
1109 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1110 domaindn=names.domaindn, hostip=hostip,
1111 hostip6=hostip6, hostname=names.hostname,
1112 dnspass=dnspass, realm=names.realm,
1113 domainguid=domainguid, hostguid=hostguid)
1115 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1116 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1118 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1119 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1120 keytab_name=paths.dns_keytab)
1121 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1122 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1124 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1125 hostname=names.hostname, realm=names.realm)
1126 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1128 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1131 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1133 message("Once the above files are installed, your Samba4 server will be ready to use")
1134 message("Server Role: %s" % serverrole)
1135 message("Hostname: %s" % names.hostname)
1136 message("NetBIOS Domain: %s" % names.domain)
1137 message("DNS Domain: %s" % names.dnsdomain)
1138 message("DOMAIN SID: %s" % str(domainsid))
1139 if samdb_fill == FILL_FULL:
1140 message("Admin password: %s" % adminpass)
1142 result = ProvisionResult()
1143 result.domaindn = domaindn
1144 result.paths = paths
1146 result.samdb = samdb
1150 def provision_become_dc(setup_dir=None,
1151 smbconf=None, targetdir=None, realm=None,
1152 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1154 domain=None, hostname=None, domainsid=None,
1155 adminpass=None, krbtgtpass=None, domainguid=None,
1156 policyguid=None, invocationid=None, machinepass=None,
1157 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1158 wheel=None, backup=None, aci=None, serverrole=None,
1159 ldap_backend=None, ldap_backend_type=None, sitename=None):
1162 """print a message if quiet is not set."""
1165 return provision(setup_dir, message, system_session(), None,
1166 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1167 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1168 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1171 def setup_db_config(setup_path, dbdir):
1172 """Setup a Berkeley database.
1174 :param setup_path: Setup path function.
1175 :param dbdir: Database directory."""
1176 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1177 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1178 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1179 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1181 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1182 {"LDAPDBDIR": dbdir})
1186 def provision_backend(setup_dir=None, message=None,
1187 smbconf=None, targetdir=None, realm=None,
1188 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1189 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1190 ldap_backend_type=None, ldap_backend_port=None,
1191 ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
1193 def setup_path(file):
1194 return os.path.join(setup_dir, file)
1196 if hostname is None:
1197 hostname = socket.gethostname().split(".")[0].lower()
1200 root = findnss(pwd.getpwnam, ["root"])[0]
1202 if adminpass is None:
1203 adminpass = glue.generate_random_str(12)
1205 if targetdir is not None:
1206 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1207 os.makedirs(os.path.join(targetdir, "etc"))
1208 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1209 elif smbconf is None:
1210 smbconf = param.default_path()
1211 assert smbconf is not None
1213 # only install a new smb.conf if there isn't one there already
1214 if not os.path.exists(smbconf):
1215 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1218 # openldap-online-configuration: validation of olc and slaptest
1219 if ol_olc == "yes" and ol_slaptest is None:
1220 sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slaptest-Binary!")
1222 if ol_olc == "yes" and ol_slaptest is not None:
1223 ol_slaptest = ol_slaptest + "/slaptest"
1224 if not os.path.exists(ol_slaptest):
1225 message (ol_slaptest)
1226 sys.exit("Warning: Given Path to slaptest-Binary does not exist!")
1231 lp = param.LoadParm()
1234 if serverrole is None:
1235 serverrole = lp.get("server role")
1237 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1238 dnsdomain=realm, serverrole=serverrole,
1239 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1242 paths = provision_paths_from_lp(lp, names.dnsdomain)
1244 if not os.path.isdir(paths.ldapdir):
1245 os.makedirs(paths.ldapdir, 0700)
1246 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1248 os.unlink(schemadb_path)
1252 schemadb = Ldb(schemadb_path, lp=lp)
1254 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1256 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1257 {"SCHEMADN": names.schemadn,
1260 setup_modify_ldif(schemadb,
1261 setup_path("provision_schema_basedn_modify.ldif"), \
1262 {"SCHEMADN": names.schemadn,
1263 "NETBIOSNAME": names.netbiosname,
1264 "DEFAULTSITE": DEFAULTSITE,
1265 "CONFIGDN": names.configdn,
1266 "SERVERDN": names.serverdn,
1267 "PREFIXMAP_B64": b64encode(prefixmap)
1270 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1271 {"SCHEMADN": names.schemadn })
1272 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1273 {"SCHEMADN": names.schemadn})
1275 if ldap_backend_type == "fedora-ds":
1276 if ldap_backend_port is not None:
1277 serverport = "ServerPort=%d" % ldap_backend_port
1281 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1283 "HOSTNAME": hostname,
1284 "DNSDOMAIN": names.dnsdomain,
1285 "LDAPDIR": paths.ldapdir,
1286 "DOMAINDN": names.domaindn,
1287 "LDAPMANAGERDN": names.ldapmanagerdn,
1288 "LDAPMANAGERPASS": adminpass,
1289 "SERVERPORT": serverport})
1291 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1292 {"CONFIGDN": names.configdn,
1293 "SCHEMADN": names.schemadn,
1296 mapping = "schema-map-fedora-ds-1.0"
1297 backend_schema = "99_ad.ldif"
1299 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1301 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1303 elif ldap_backend_type == "openldap":
1304 attrs = ["linkID", "lDAPDisplayName"]
1305 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)
1307 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1308 refint_attributes = ""
1309 for i in range (0, len(res)):
1310 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1311 target = schemadb.searchone(basedn=names.schemadn,
1312 expression=expression,
1313 attribute="lDAPDisplayName",
1314 scope=SCOPE_SUBTREE)
1315 if target is not None:
1316 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1318 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1319 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1320 "MEMBEROF_ATTR" : str(target) })
1322 refint_config = read_and_sub_file(setup_path("refint.conf"),
1323 { "LINK_ATTRS" : refint_attributes})
1325 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1327 mmr_replicator_acl = ""
1328 mmr_serverids_config = ""
1329 mmr_syncrepl_schema_config = ""
1330 mmr_syncrepl_config_config = ""
1331 mmr_syncrepl_user_config = ""
1334 if ol_mmr_urls is not None:
1335 # For now, make these equal
1336 mmr_pass = adminpass
1338 url_list=filter(None,ol_mmr_urls.split(' '))
1339 if (len(url_list) == 1):
1340 url_list=filter(None,ol_mmr_urls.split(','))
1343 mmr_on_config = "MirrorMode On"
1344 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1346 for url in url_list:
1348 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1349 { "SERVERID" : str(serverid),
1350 "LDAPSERVER" : url })
1353 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1355 "MMRDN": names.schemadn,
1357 "MMR_PASSWORD": mmr_pass})
1360 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1362 "MMRDN": names.configdn,
1364 "MMR_PASSWORD": mmr_pass})
1367 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1369 "MMRDN": names.domaindn,
1371 "MMR_PASSWORD": mmr_pass })
1373 olc_config_pass = ""
1375 olc_syncrepl_config = ""
1378 olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
1379 { "OLC_PW": adminpass })
1380 olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
1382 # if olc = yes + mmr = yes, generate cn=config-replication directives
1383 # and olc_seed.lif for the other mmr-servers
1384 if ol_olc == "yes" and ol_mmr_urls is not None:
1386 olc_serverids_config = ""
1387 olc_syncrepl_config = ""
1388 olc_syncrepl_seed_config = ""
1390 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1392 for url in url_list:
1394 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1395 { "SERVERID" : str(serverid),
1396 "LDAPSERVER" : url })
1399 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1402 "MMR_PASSWORD": adminpass})
1404 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1406 "LDAPSERVER" : url})
1408 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1409 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1410 "OLC_PW": adminpass,
1411 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1416 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1417 {"DNSDOMAIN": names.dnsdomain,
1418 "LDAPDIR": paths.ldapdir,
1419 "DOMAINDN": names.domaindn,
1420 "CONFIGDN": names.configdn,
1421 "SCHEMADN": names.schemadn,
1422 "MEMBEROF_CONFIG": memberof_config,
1423 "MIRRORMODE": mmr_on_config,
1424 "REPLICATOR_ACL": mmr_replicator_acl,
1425 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1426 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1427 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1428 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1429 "OLC_CONFIG_PASS": olc_config_pass,
1430 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1431 "OLC_CONFIG_ACL": olc_config_acl,
1432 "OLC_MMR_CONFIG": olc_mmr_config,
1433 "REFINT_CONFIG": refint_config})
1434 setup_file(setup_path("modules.conf"), paths.modulesconf,
1435 {"REALM": names.realm})
1437 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1438 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1439 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1441 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1442 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1444 setup_file(setup_path("cn=samba.ldif"),
1445 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1446 { "UUID": str(uuid.uuid4()),
1447 "LDAPTIME": timestring(int(time.time()))} )
1448 setup_file(setup_path("cn=samba-admin.ldif"),
1449 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1450 {"LDAPADMINPASS_B64": b64encode(adminpass),
1451 "UUID": str(uuid.uuid4()),
1452 "LDAPTIME": timestring(int(time.time()))} )
1454 if ol_mmr_urls is not None:
1455 setup_file(setup_path("cn=replicator.ldif"),
1456 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1457 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1458 "UUID": str(uuid.uuid4()),
1459 "LDAPTIME": timestring(int(time.time()))} )
1462 mapping = "schema-map-openldap-2.3"
1463 backend_schema = "backend-schema.schema"
1465 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1466 if ldap_backend_port is not None:
1467 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1469 server_port_string = ""
1471 if ol_olc != "yes" and ol_mmr_urls is None:
1472 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1474 if ol_olc == "yes" and ol_mmr_urls is None:
1475 slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1477 if ol_olc != "yes" and ol_mmr_urls is not None:
1478 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1480 if ol_olc == "yes" and ol_mmr_urls is not None:
1481 slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1484 ldapuser = "--username=samba-admin"
1487 schema_command = "bin/ad2oLschema --option=convert:target=" + ldap_backend_type + " -I " + setup_path(mapping) + " -H tdb://" + schemadb_path + " -O " + os.path.join(paths.ldapdir, backend_schema)
1489 os.system(schema_command)
1491 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1492 message("Server Role: %s" % serverrole)
1493 message("Hostname: %s" % names.hostname)
1494 message("DNS Domain: %s" % names.dnsdomain)
1495 message("Base DN: %s" % names.domaindn)
1497 if ldap_backend_type == "openldap":
1498 message("LDAP admin user: samba-admin")
1500 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1502 message("LDAP admin password: %s" % adminpass)
1503 message(slapdcommand)
1504 if ol_olc == "yes" or ol_mmr_urls is not None:
1505 message("Attention to slapd-Port: <PORT> must be different than 389!")
1506 assert isinstance(ldap_backend_type, str)
1507 assert isinstance(ldapuser, str)
1508 assert isinstance(adminpass, str)
1509 assert isinstance(names.dnsdomain, str)
1510 assert isinstance(names.domain, str)
1511 assert isinstance(serverrole, str)
1512 args = ["--ldap-backend=ldapi",
1513 "--ldap-backend-type=" + ldap_backend_type,
1514 "--password=" + adminpass,
1516 "--realm=" + names.dnsdomain,
1517 "--domain=" + names.domain,
1518 "--server-role='" + serverrole + "'"]
1519 message("Run provision with: " + " ".join(args))
1522 # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d
1524 if not os.path.isdir(paths.olcdir):
1525 os.makedirs(paths.olcdir, 0770)
1526 paths.olslaptest = str(ol_slaptest)
1527 olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir + " >/dev/null 2>&1"
1528 os.system(olc_command)
1529 os.remove(paths.slapdconf)
1530 # use line below for debugging during olc-conversion with slaptest, instead of olc_command above
1531 #olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir"
1534 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1535 """Create a PHP LDAP admin configuration file.
1537 :param path: Path to write the configuration to.
1538 :param setup_path: Function to generate setup paths.
1540 setup_file(setup_path("phpldapadmin-config.php"), path,
1541 {"S4_LDAPI_URI": ldapi_uri})
1544 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1545 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1546 """Write out a DNS zone file, from the info in the current database.
1548 :param path: Path of the new zone file.
1549 :param setup_path: Setup path function.
1550 :param dnsdomain: DNS Domain name
1551 :param domaindn: DN of the Domain
1552 :param hostip: Local IPv4 IP
1553 :param hostip6: Local IPv6 IP
1554 :param hostname: Local hostname
1555 :param dnspass: Password for DNS
1556 :param realm: Realm name
1557 :param domainguid: GUID of the domain.
1558 :param hostguid: GUID of the host.
1560 assert isinstance(domainguid, str)
1562 if hostip6 is not None:
1563 hostip6_base_line = " IN AAAA " + hostip6
1564 hostip6_host_line = hostname + " IN AAAA " + hostip6
1566 hostip6_base_line = ""
1567 hostip6_host_line = ""
1569 if hostip is not None:
1570 hostip_base_line = " IN A " + hostip
1571 hostip_host_line = hostname + " IN A " + hostip
1573 hostip_base_line = ""
1574 hostip_host_line = ""
1576 setup_file(setup_path("provision.zone"), path, {
1577 "DNSPASS_B64": b64encode(dnspass),
1578 "HOSTNAME": hostname,
1579 "DNSDOMAIN": dnsdomain,
1581 "HOSTIP_BASE_LINE": hostip_base_line,
1582 "HOSTIP_HOST_LINE": hostip_host_line,
1583 "DOMAINGUID": domainguid,
1584 "DATESTRING": time.strftime("%Y%m%d%H"),
1585 "DEFAULTSITE": DEFAULTSITE,
1586 "HOSTGUID": hostguid,
1587 "HOSTIP6_BASE_LINE": hostip6_base_line,
1588 "HOSTIP6_HOST_LINE": hostip6_host_line,
1592 def create_named_conf(path, setup_path, realm, dnsdomain,
1594 """Write out a file containing zone statements suitable for inclusion in a
1595 named.conf file (including GSS-TSIG configuration).
1597 :param path: Path of the new named.conf file.
1598 :param setup_path: Setup path function.
1599 :param realm: Realm name
1600 :param dnsdomain: DNS Domain name
1601 :param private_dir: Path to private directory
1602 :param keytab_name: File name of DNS keytab file
1605 setup_file(setup_path("named.conf"), path, {
1606 "DNSDOMAIN": dnsdomain,
1608 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1609 "PRIVATE_DIR": private_dir
1612 def create_named_txt(path, setup_path, realm, dnsdomain,
1613 private_dir, keytab_name):
1614 """Write out a file containing zone statements suitable for inclusion in a
1615 named.conf file (including GSS-TSIG configuration).
1617 :param path: Path of the new named.conf file.
1618 :param setup_path: Setup path function.
1619 :param realm: Realm name
1620 :param dnsdomain: DNS Domain name
1621 :param private_dir: Path to private directory
1622 :param keytab_name: File name of DNS keytab file
1625 setup_file(setup_path("named.txt"), path, {
1626 "DNSDOMAIN": dnsdomain,
1628 "DNS_KEYTAB": keytab_name,
1629 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1630 "PRIVATE_DIR": private_dir
1633 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1634 """Write out a file containing zone statements suitable for inclusion in a
1635 named.conf file (including GSS-TSIG configuration).
1637 :param path: Path of the new named.conf file.
1638 :param setup_path: Setup path function.
1639 :param dnsdomain: DNS Domain name
1640 :param hostname: Local hostname
1641 :param realm: Realm name
1644 setup_file(setup_path("krb5.conf"), path, {
1645 "DNSDOMAIN": dnsdomain,
1646 "HOSTNAME": hostname,
1651 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1652 serverdn, servername):
1653 """Load schema for the SamDB.
1655 :param samdb: Load a schema into a SamDB.
1656 :param setup_path: Setup path function.
1657 :param schemadn: DN of the schema
1658 :param netbiosname: NetBIOS name of the host.
1659 :param configdn: DN of the configuration
1660 :param serverdn: DN of the server
1661 :param servername: Host name of the server
1663 schema_data = open(setup_path("schema.ldif"), 'r').read()
1664 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1665 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1666 check_all_substituted(schema_data)
1667 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1668 prefixmap = b64encode(prefixmap)
1670 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1671 head_data = substitute_var(head_data, {
1672 "SCHEMADN": schemadn,
1673 "NETBIOSNAME": netbiosname,
1674 "CONFIGDN": configdn,
1675 "DEFAULTSITE": sitename,
1676 "PREFIXMAP_B64": prefixmap,
1677 "SERVERDN": serverdn,
1678 "SERVERNAME": servername,
1680 check_all_substituted(head_data)
1681 samdb.attach_schema_from_ldif(head_data, schema_data)