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, \
41 DS_BEHAVIOR_WIN2000, DS_BEHAVIOR_WIN2003_INTERIM, DS_BEHAVIOR_WIN2003, DS_BEHAVIOR_WIN2008
42 from samba.samdb import SamDB
43 from samba.idmap import IDmapDB
44 from samba.dcerpc import security
46 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
47 timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
48 from ms_schema import read_ms_schema
50 __docformat__ = "restructuredText"
54 """Find the setup directory used by provision."""
55 dirname = os.path.dirname(__file__)
56 if "/site-packages/" in dirname:
57 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
58 for suffix in ["share/setup", "share/samba/setup", "setup"]:
59 ret = os.path.join(prefix, suffix)
60 if os.path.isdir(ret):
63 ret = os.path.join(dirname, "../../../setup")
64 if os.path.isdir(ret):
66 raise Exception("Unable to find setup directory.")
69 DEFAULTSITE = "Default-First-Site-Name"
71 class InvalidNetbiosName(Exception):
72 """A specified name was not a valid NetBIOS name."""
73 def __init__(self, name):
74 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
77 class ProvisionPaths(object):
90 self.dns_keytab = None
93 self.private_dir = None
96 self.modulesconf = None
97 self.memberofconf = None
98 self.fedoradsinf = None
99 self.fedoradspartitions = None
101 self.olmmrserveridsconf = None
102 self.olmmrsyncreplconf = None
104 self.olslaptest = None
105 self.olcseedldif = None
108 class ProvisionNames(object):
114 self.ldapmanagerdn = None
115 self.dnsdomain = None
117 self.netbiosname = None
124 class ProvisionResult(object):
131 def check_install(lp, session_info, credentials):
132 """Check whether the current install seems ok.
134 :param lp: Loadparm context
135 :param session_info: Session information
136 :param credentials: Credentials
138 if lp.get("realm") == "":
139 raise Exception("Realm empty")
140 ldb = Ldb(lp.get("sam database"), session_info=session_info,
141 credentials=credentials, lp=lp)
142 if len(ldb.search("(cn=Administrator)")) != 1:
143 raise "No administrator account found"
146 def findnss(nssfn, names):
147 """Find a user or group from a list of possibilities.
149 :param nssfn: NSS Function to try (should raise KeyError if not found)
150 :param names: Names to check.
151 :return: Value return by first names list.
158 raise KeyError("Unable to find user/group %r" % names)
161 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
162 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
165 def read_and_sub_file(file, subst_vars):
166 """Read a file and sub in variables found in it
168 :param file: File to be read (typically from setup directory)
169 param subst_vars: Optional variables to subsitute in the file.
171 data = open(file, 'r').read()
172 if subst_vars is not None:
173 data = substitute_var(data, subst_vars)
174 check_all_substituted(data)
178 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
179 """Setup a ldb in the private dir.
181 :param ldb: LDB file to import data into
182 :param ldif_path: Path of the LDIF file to load
183 :param subst_vars: Optional variables to subsitute in LDIF.
185 assert isinstance(ldif_path, str)
187 data = read_and_sub_file(ldif_path, subst_vars)
191 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
192 """Modify a ldb in the private dir.
194 :param ldb: LDB object.
195 :param ldif_path: LDIF file path.
196 :param subst_vars: Optional dictionary with substitution variables.
198 data = read_and_sub_file(ldif_path, subst_vars)
200 ldb.modify_ldif(data)
203 def setup_ldb(ldb, ldif_path, subst_vars):
204 """Import a LDIF a file into a LDB handle, optionally substituting variables.
206 :note: Either all LDIF data will be added or none (using transactions).
208 :param ldb: LDB file to import into.
209 :param ldif_path: Path to the LDIF file.
210 :param subst_vars: Dictionary with substitution variables.
212 assert ldb is not None
213 ldb.transaction_start()
215 setup_add_ldif(ldb, ldif_path, subst_vars)
217 ldb.transaction_cancel()
219 ldb.transaction_commit()
222 def setup_file(template, fname, subst_vars):
223 """Setup a file in the private dir.
225 :param template: Path of the template file.
226 :param fname: Path of the file to create.
227 :param subst_vars: Substitution variables.
231 if os.path.exists(f):
234 data = read_and_sub_file(template, subst_vars)
235 open(f, 'w').write(data)
238 def provision_paths_from_lp(lp, dnsdomain):
239 """Set the default paths for provisioning.
241 :param lp: Loadparm context.
242 :param dnsdomain: DNS Domain name
244 paths = ProvisionPaths()
245 paths.private_dir = lp.get("private dir")
246 paths.keytab = "secrets.keytab"
247 paths.dns_keytab = "dns.keytab"
249 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
250 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
251 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
252 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
253 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
254 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
255 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
256 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
257 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
258 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
259 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
260 paths.phpldapadminconfig = os.path.join(paths.private_dir,
261 "phpldapadmin-config.php")
262 paths.ldapdir = os.path.join(paths.private_dir,
264 paths.slapdconf = os.path.join(paths.ldapdir,
266 paths.modulesconf = os.path.join(paths.ldapdir,
268 paths.memberofconf = os.path.join(paths.ldapdir,
270 paths.fedoradsinf = os.path.join(paths.ldapdir,
272 paths.fedoradspartitions = os.path.join(paths.ldapdir,
273 "fedorads-partitions.ldif")
274 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
275 "mmr_serverids.conf")
276 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
278 paths.olcdir = os.path.join(paths.ldapdir,
280 paths.olcseedldif = os.path.join(paths.ldapdir,
282 paths.hklm = "hklm.ldb"
283 paths.hkcr = "hkcr.ldb"
284 paths.hkcu = "hkcu.ldb"
285 paths.hku = "hku.ldb"
286 paths.hkpd = "hkpd.ldb"
287 paths.hkpt = "hkpt.ldb"
289 paths.sysvol = lp.get("path", "sysvol")
291 paths.netlogon = lp.get("path", "netlogon")
293 paths.smbconf = lp.configfile
298 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
299 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
301 """Guess configuration settings to use."""
304 hostname = socket.gethostname().split(".")[0].lower()
306 netbiosname = hostname.upper()
307 if not valid_netbios_name(netbiosname):
308 raise InvalidNetbiosName(netbiosname)
310 hostname = hostname.lower()
312 if dnsdomain is None:
313 dnsdomain = lp.get("realm")
315 if serverrole is None:
316 serverrole = lp.get("server role")
318 assert dnsdomain is not None
319 realm = dnsdomain.upper()
321 if lp.get("realm").upper() != realm:
322 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
323 (lp.get("realm"), lp.configfile, realm))
325 dnsdomain = dnsdomain.lower()
327 if serverrole == "domain controller":
329 domain = lp.get("workgroup")
331 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
332 if lp.get("workgroup").upper() != domain.upper():
333 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
334 lp.get("workgroup"), domain)
338 domaindn = "CN=" + netbiosname
340 assert domain is not None
341 domain = domain.upper()
342 if not valid_netbios_name(domain):
343 raise InvalidNetbiosName(domain)
349 configdn = "CN=Configuration," + rootdn
351 schemadn = "CN=Schema," + configdn
356 names = ProvisionNames()
357 names.rootdn = rootdn
358 names.domaindn = domaindn
359 names.configdn = configdn
360 names.schemadn = schemadn
361 names.ldapmanagerdn = "CN=Manager," + rootdn
362 names.dnsdomain = dnsdomain
363 names.domain = domain
365 names.netbiosname = netbiosname
366 names.hostname = hostname
367 names.sitename = sitename
368 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
373 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
375 """Create a new smb.conf file based on a couple of basic settings.
377 assert smbconf is not None
379 hostname = socket.gethostname().split(".")[0].lower()
381 if serverrole is None:
382 serverrole = "standalone"
384 assert serverrole in ("domain controller", "member server", "standalone")
385 if serverrole == "domain controller":
387 elif serverrole == "member server":
388 smbconfsuffix = "member"
389 elif serverrole == "standalone":
390 smbconfsuffix = "standalone"
392 assert domain is not None
393 assert realm is not None
395 default_lp = param.LoadParm()
396 #Load non-existant file
397 if os.path.exists(smbconf):
398 default_lp.load(smbconf)
400 if targetdir is not None:
401 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
402 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
404 default_lp.set("lock dir", os.path.abspath(targetdir))
409 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
410 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
412 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
414 "HOSTNAME": hostname,
417 "SERVERROLE": serverrole,
418 "NETLOGONPATH": netlogon,
419 "SYSVOLPATH": sysvol,
420 "PRIVATEDIR_LINE": privatedir_line,
421 "LOCKDIR_LINE": lockdir_line
425 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
426 users_gid, wheel_gid):
427 """setup reasonable name mappings for sam names to unix names.
429 :param samdb: SamDB object.
430 :param idmap: IDmap db object.
431 :param sid: The domain sid.
432 :param domaindn: The domain DN.
433 :param root_uid: uid of the UNIX root user.
434 :param nobody_uid: uid of the UNIX nobody user.
435 :param users_gid: gid of the UNIX users group.
436 :param wheel_gid: gid of the UNIX wheel group."""
437 # add some foreign sids if they are not present already
438 samdb.add_stock_foreign_sids()
440 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
441 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
443 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
444 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
447 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
449 serverrole, ldap_backend=None,
450 ldap_backend_type=None, erase=False):
451 """Setup the partitions for the SAM database.
453 Alternatively, provision() may call this, and then populate the database.
455 :note: This will wipe the Sam Database!
457 :note: This function always removes the local SAM LDB file. The erase
458 parameter controls whether to erase the existing data, which
459 may not be stored locally but in LDAP.
461 assert session_info is not None
464 samdb = SamDB(samdb_path, session_info=session_info,
465 credentials=credentials, lp=lp)
469 os.unlink(samdb_path)
470 samdb = SamDB(samdb_path, session_info=session_info,
471 credentials=credentials, lp=lp)
476 #Add modules to the list to activate them by default
477 #beware often order is important
479 # Some Known ordering constraints:
480 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
481 # - objectclass must be before password_hash, because password_hash checks
482 # that the objectclass is of type person (filled in by objectclass
483 # module when expanding the objectclass list)
484 # - partition must be last
485 # - each partition has its own module list then
486 modules_list = ["rootdse",
504 "extended_dn_out_ldb"]
505 modules_list2 = ["show_deleted",
508 domaindn_ldb = "users.ldb"
509 if ldap_backend is not None:
510 domaindn_ldb = ldap_backend
511 configdn_ldb = "configuration.ldb"
512 if ldap_backend is not None:
513 configdn_ldb = ldap_backend
514 schemadn_ldb = "schema.ldb"
515 if ldap_backend is not None:
516 schema_ldb = ldap_backend
517 schemadn_ldb = ldap_backend
519 if ldap_backend_type == "fedora-ds":
520 backend_modules = ["nsuniqueid", "paged_searches"]
521 # We can handle linked attributes here, as we don't have directory-side subtree operations
522 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
523 elif ldap_backend_type == "openldap":
524 backend_modules = ["entryuuid", "paged_searches"]
525 # OpenLDAP handles subtree renames, so we don't want to do any of these things
526 tdb_modules_list = ["extended_dn_out_dereference"]
527 elif ldap_backend is not None:
528 raise "LDAP Backend specified, but LDAP Backend Type not specified"
529 elif serverrole == "domain controller":
530 backend_modules = ["repl_meta_data"]
532 backend_modules = ["objectguid"]
534 if tdb_modules_list is None:
535 tdb_modules_list_as_string = ""
537 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
539 samdb.transaction_start()
541 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
542 "SCHEMADN": names.schemadn,
543 "SCHEMADN_LDB": schemadn_ldb,
544 "SCHEMADN_MOD2": ",objectguid",
545 "CONFIGDN": names.configdn,
546 "CONFIGDN_LDB": configdn_ldb,
547 "DOMAINDN": names.domaindn,
548 "DOMAINDN_LDB": domaindn_ldb,
549 "SCHEMADN_MOD": "schema_fsmo,instancetype",
550 "CONFIGDN_MOD": "naming_fsmo,instancetype",
551 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
552 "MODULES_LIST": ",".join(modules_list),
553 "TDB_MODULES_LIST": tdb_modules_list_as_string,
554 "MODULES_LIST2": ",".join(modules_list2),
555 "BACKEND_MOD": ",".join(backend_modules),
559 samdb.transaction_cancel()
562 samdb.transaction_commit()
564 samdb = SamDB(samdb_path, session_info=session_info,
565 credentials=credentials, lp=lp)
567 samdb.transaction_start()
569 message("Setting up sam.ldb attributes")
570 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
572 message("Setting up sam.ldb rootDSE")
573 setup_samdb_rootdse(samdb, setup_path, names)
576 message("Erasing data from partitions")
577 samdb.erase_partitions()
580 samdb.transaction_cancel()
583 samdb.transaction_commit()
588 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
589 netbiosname, domainsid, keytab_path, samdb_url,
590 dns_keytab_path, dnspass, machinepass):
591 """Add DC-specific bits to a secrets database.
593 :param secretsdb: Ldb Handle to the secrets database
594 :param setup_path: Setup path function
595 :param machinepass: Machine password
597 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
598 "MACHINEPASS_B64": b64encode(machinepass),
601 "DNSDOMAIN": dnsdomain,
602 "DOMAINSID": str(domainsid),
603 "SECRETS_KEYTAB": keytab_path,
604 "NETBIOSNAME": netbiosname,
605 "SAM_LDB": samdb_url,
606 "DNS_KEYTAB": dns_keytab_path,
607 "DNSPASS_B64": b64encode(dnspass),
611 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
612 """Setup the secrets database.
614 :param path: Path to the secrets database.
615 :param setup_path: Get the path to a setup file.
616 :param session_info: Session info.
617 :param credentials: Credentials
618 :param lp: Loadparm context
619 :return: LDB handle for the created secrets database
621 if os.path.exists(path):
623 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
626 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
627 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
629 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
631 if credentials is not None and credentials.authentication_requested():
632 if credentials.get_bind_dn() is not None:
633 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
634 "LDAPMANAGERDN": credentials.get_bind_dn(),
635 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
638 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
639 "LDAPADMINUSER": credentials.get_username(),
640 "LDAPADMINREALM": credentials.get_realm(),
641 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
647 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
648 """Setup the templates database.
650 :param path: Path to the database.
651 :param setup_path: Function for obtaining the path to setup files.
652 :param session_info: Session info
653 :param credentials: Credentials
654 :param lp: Loadparm context
656 templates_ldb = SamDB(path, session_info=session_info,
657 credentials=credentials, lp=lp)
660 templates_ldb.erase()
661 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
665 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
667 templates_ldb = SamDB(path, session_info=session_info,
668 credentials=credentials, lp=lp)
670 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
673 def setup_registry(path, setup_path, session_info, credentials, lp):
674 """Setup the registry.
676 :param path: Path to the registry database
677 :param setup_path: Function that returns the path to a setup.
678 :param session_info: Session information
679 :param credentials: Credentials
680 :param lp: Loadparm context
682 reg = registry.Registry()
683 hive = registry.open_ldb(path, session_info=session_info,
684 credentials=credentials, lp_ctx=lp)
685 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
686 provision_reg = setup_path("provision.reg")
687 assert os.path.exists(provision_reg)
688 reg.diff_apply(provision_reg)
691 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
692 """Setup the idmap database.
694 :param path: path to the idmap database
695 :param setup_path: Function that returns a path to a setup file
696 :param session_info: Session information
697 :param credentials: Credentials
698 :param lp: Loadparm context
700 if os.path.exists(path):
703 idmap_ldb = IDmapDB(path, session_info=session_info,
704 credentials=credentials, lp=lp)
707 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
711 def setup_samdb_rootdse(samdb, setup_path, names):
712 """Setup the SamDB rootdse.
714 :param samdb: Sam Database handle
715 :param setup_path: Obtain setup path
717 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
718 "SCHEMADN": names.schemadn,
719 "NETBIOSNAME": names.netbiosname,
720 "DNSDOMAIN": names.dnsdomain,
721 "REALM": names.realm,
722 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
723 "DOMAINDN": names.domaindn,
724 "ROOTDN": names.rootdn,
725 "CONFIGDN": names.configdn,
726 "SERVERDN": names.serverdn,
730 def setup_self_join(samdb, names,
731 machinepass, dnspass,
732 domainsid, invocationid, setup_path,
733 policyguid, domainControllerFunctionality):
734 """Join a host to its own domain."""
735 assert isinstance(invocationid, str)
736 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
737 "CONFIGDN": names.configdn,
738 "SCHEMADN": names.schemadn,
739 "DOMAINDN": names.domaindn,
740 "SERVERDN": names.serverdn,
741 "INVOCATIONID": invocationid,
742 "NETBIOSNAME": names.netbiosname,
743 "DEFAULTSITE": names.sitename,
744 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
745 "MACHINEPASS_B64": b64encode(machinepass),
746 "DNSPASS_B64": b64encode(dnspass),
747 "REALM": names.realm,
748 "DOMAIN": names.domain,
749 "DNSDOMAIN": names.dnsdomain,
750 "SAMBA_VERSION_STRING": version,
751 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
752 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
753 "POLICYGUID": policyguid,
754 "DNSDOMAIN": names.dnsdomain,
755 "DOMAINSID": str(domainsid),
756 "DOMAINDN": names.domaindn})
759 def setup_samdb(path, setup_path, session_info, credentials, lp,
761 domainsid, aci, domainguid, policyguid,
762 fill, adminpass, krbtgtpass,
763 machinepass, invocationid, dnspass,
764 serverrole, ldap_backend=None,
765 ldap_backend_type=None):
766 """Setup a complete SAM Database.
768 :note: This will wipe the main SAM database file!
771 domainFunctionality = DS_BEHAVIOR_WIN2008
772 forestFunctionality = DS_BEHAVIOR_WIN2008
773 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
775 erase = (fill != FILL_DRS)
777 # Also wipes the database
778 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
779 credentials=credentials, session_info=session_info,
781 ldap_backend=ldap_backend, serverrole=serverrole,
782 ldap_backend_type=ldap_backend_type, erase=erase)
784 samdb = SamDB(path, session_info=session_info,
785 credentials=credentials, lp=lp)
789 message("Pre-loading the Samba 4 and AD schema")
791 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
792 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
793 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
795 samdb.set_domain_sid(str(domainsid))
796 if serverrole == "domain controller":
797 samdb.set_invocation_id(invocationid)
799 schema_data = load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
800 names.configdn, names.sitename, names.serverdn)
801 samdb.transaction_start()
804 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
805 if serverrole == "domain controller":
806 domain_oc = "domainDNS"
808 domain_oc = "samba4LocalDomain"
810 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
811 "DOMAINDN": names.domaindn,
813 "DOMAIN_OC": domain_oc
816 message("Modifying DomainDN: " + names.domaindn + "")
817 if domainguid is not None:
818 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
822 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
823 "LDAPTIME": timestring(int(time.time())),
824 "DOMAINSID": str(domainsid),
825 "SCHEMADN": names.schemadn,
826 "NETBIOSNAME": names.netbiosname,
827 "DEFAULTSITE": names.sitename,
828 "CONFIGDN": names.configdn,
829 "SERVERDN": names.serverdn,
830 "POLICYGUID": policyguid,
831 "DOMAINDN": names.domaindn,
832 "DOMAINGUID_MOD": domainguid_mod,
833 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
836 message("Adding configuration container (permitted to fail)")
837 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
838 "CONFIGDN": names.configdn,
841 message("Modifying configuration container")
842 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
843 "CONFIGDN": names.configdn,
844 "SCHEMADN": names.schemadn,
847 message("Adding schema container (permitted to fail)")
848 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
849 "SCHEMADN": names.schemadn,
852 message("Modifying schema container")
854 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
856 setup_modify_ldif(samdb,
857 setup_path("provision_schema_basedn_modify.ldif"), {
858 "SCHEMADN": names.schemadn,
859 "NETBIOSNAME": names.netbiosname,
860 "DEFAULTSITE": names.sitename,
861 "CONFIGDN": names.configdn,
862 "SERVERDN": names.serverdn,
863 "PREFIXMAP_B64": b64encode(prefixmap)
866 message("Setting up sam.ldb schema")
867 samdb.add_ldif(schema_data)
868 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
869 {"SCHEMADN": names.schemadn})
871 message("Setting up sam.ldb configuration data")
872 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
873 "CONFIGDN": names.configdn,
874 "NETBIOSNAME": names.netbiosname,
875 "DEFAULTSITE": names.sitename,
876 "DNSDOMAIN": names.dnsdomain,
877 "DOMAIN": names.domain,
878 "SCHEMADN": names.schemadn,
879 "DOMAINDN": names.domaindn,
880 "SERVERDN": names.serverdn,
881 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
884 message("Setting up display specifiers")
885 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
886 {"CONFIGDN": names.configdn})
888 message("Adding users container (permitted to fail)")
889 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
890 "DOMAINDN": names.domaindn})
891 message("Modifying users container")
892 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
893 "DOMAINDN": names.domaindn})
894 message("Adding computers container (permitted to fail)")
895 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
896 "DOMAINDN": names.domaindn})
897 message("Modifying computers container")
898 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
899 "DOMAINDN": names.domaindn})
900 message("Setting up sam.ldb data")
901 setup_add_ldif(samdb, setup_path("provision.ldif"), {
902 "DOMAINDN": names.domaindn,
903 "NETBIOSNAME": names.netbiosname,
904 "DEFAULTSITE": names.sitename,
905 "CONFIGDN": names.configdn,
906 "SERVERDN": names.serverdn
909 if fill == FILL_FULL:
910 message("Setting up sam.ldb users and groups")
911 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
912 "DOMAINDN": names.domaindn,
913 "DOMAINSID": str(domainsid),
914 "CONFIGDN": names.configdn,
915 "ADMINPASS_B64": b64encode(adminpass),
916 "KRBTGTPASS_B64": b64encode(krbtgtpass),
919 if serverrole == "domain controller":
920 message("Setting up self join")
921 setup_self_join(samdb, names=names, invocationid=invocationid,
923 machinepass=machinepass,
924 domainsid=domainsid, policyguid=policyguid,
925 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
928 samdb.transaction_cancel()
931 samdb.transaction_commit()
936 FILL_NT4SYNC = "NT4SYNC"
939 def provision(setup_dir, message, session_info,
940 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
941 rootdn=None, domaindn=None, schemadn=None, configdn=None,
943 domain=None, hostname=None, hostip=None, hostip6=None,
944 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
945 policyguid=None, invocationid=None, machinepass=None,
946 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
947 wheel=None, backup=None, aci=None, serverrole=None,
948 ldap_backend=None, ldap_backend_type=None, sitename=None):
951 :note: caution, this wipes all existing data!
954 def setup_path(file):
955 return os.path.join(setup_dir, file)
957 if domainsid is None:
958 domainsid = security.random_sid()
960 if policyguid is None:
961 policyguid = str(uuid.uuid4())
962 if adminpass is None:
963 adminpass = glue.generate_random_str(12)
964 if krbtgtpass is None:
965 krbtgtpass = glue.generate_random_str(12)
966 if machinepass is None:
967 machinepass = glue.generate_random_str(12)
969 dnspass = glue.generate_random_str(12)
970 root_uid = findnss_uid([root or "root"])
971 nobody_uid = findnss_uid([nobody or "nobody"])
972 users_gid = findnss_gid([users or "users"])
974 wheel_gid = findnss_gid(["wheel", "adm"])
976 wheel_gid = findnss_gid([wheel])
978 aci = "# no aci for local ldb"
980 if targetdir is not None:
981 if (not os.path.exists(os.path.join(targetdir, "etc"))):
982 os.makedirs(os.path.join(targetdir, "etc"))
983 smbconf = os.path.join(targetdir, "etc", "smb.conf")
984 elif smbconf is None:
985 smbconf = param.default_path()
987 # only install a new smb.conf if there isn't one there already
988 if not os.path.exists(smbconf):
989 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
992 lp = param.LoadParm()
995 names = guess_names(lp=lp, hostname=hostname, domain=domain,
996 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
997 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1000 paths = provision_paths_from_lp(lp, names.dnsdomain)
1004 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1005 except socket.gaierror, (socket.EAI_NODATA, msg):
1010 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1011 except socket.gaierror, (socket.EAI_NODATA, msg):
1014 if serverrole is None:
1015 serverrole = lp.get("server role")
1017 assert serverrole in ("domain controller", "member server", "standalone")
1018 if invocationid is None and serverrole == "domain controller":
1019 invocationid = str(uuid.uuid4())
1021 if not os.path.exists(paths.private_dir):
1022 os.mkdir(paths.private_dir)
1024 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1026 if ldap_backend is not None:
1027 if ldap_backend == "ldapi":
1028 # provision-backend will set this path suggested slapd command line / fedorads.inf
1029 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1031 # only install a new shares config db if there is none
1032 if not os.path.exists(paths.shareconf):
1033 message("Setting up share.ldb")
1034 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1035 credentials=credentials, lp=lp)
1036 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1039 message("Setting up secrets.ldb")
1040 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1041 session_info=session_info,
1042 credentials=credentials, lp=lp)
1044 message("Setting up the registry")
1045 setup_registry(paths.hklm, setup_path, session_info,
1046 credentials=credentials, lp=lp)
1048 message("Setting up templates db")
1049 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1050 credentials=credentials, lp=lp)
1052 message("Setting up idmap db")
1053 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1054 credentials=credentials, lp=lp)
1056 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1057 credentials=credentials, lp=lp, names=names,
1059 domainsid=domainsid,
1060 aci=aci, domainguid=domainguid, policyguid=policyguid,
1062 adminpass=adminpass, krbtgtpass=krbtgtpass,
1063 invocationid=invocationid,
1064 machinepass=machinepass, dnspass=dnspass,
1065 serverrole=serverrole, ldap_backend=ldap_backend,
1066 ldap_backend_type=ldap_backend_type)
1068 if serverrole == "domain controller":
1069 if paths.netlogon is None:
1070 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1071 message("Please either remove %s or see the template at %s" %
1072 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1073 assert(paths.netlogon is not None)
1075 if paths.sysvol is None:
1076 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1077 message("Please either remove %s or see the template at %s" %
1078 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1079 assert(paths.sysvol is not None)
1081 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1082 "{" + policyguid + "}")
1083 os.makedirs(policy_path, 0755)
1084 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1085 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1086 os.makedirs(os.path.join(policy_path, "User"), 0755)
1087 if not os.path.isdir(paths.netlogon):
1088 os.makedirs(paths.netlogon, 0755)
1090 if samdb_fill == FILL_FULL:
1091 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1092 root_uid=root_uid, nobody_uid=nobody_uid,
1093 users_gid=users_gid, wheel_gid=wheel_gid)
1095 message("Setting up sam.ldb rootDSE marking as synchronized")
1096 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1098 # Only make a zone file on the first DC, it should be replicated with DNS replication
1099 if serverrole == "domain controller":
1100 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1101 credentials=credentials, lp=lp)
1102 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1103 netbiosname=names.netbiosname, domainsid=domainsid,
1104 keytab_path=paths.keytab, samdb_url=paths.samdb,
1105 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1106 machinepass=machinepass, dnsdomain=names.dnsdomain)
1108 samdb = SamDB(paths.samdb, session_info=session_info,
1109 credentials=credentials, lp=lp)
1111 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1112 assert isinstance(domainguid, str)
1113 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1114 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1115 scope=SCOPE_SUBTREE)
1116 assert isinstance(hostguid, str)
1118 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1119 domaindn=names.domaindn, hostip=hostip,
1120 hostip6=hostip6, hostname=names.hostname,
1121 dnspass=dnspass, realm=names.realm,
1122 domainguid=domainguid, hostguid=hostguid)
1124 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1125 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1127 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1128 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1129 keytab_name=paths.dns_keytab)
1130 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1131 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1133 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1134 hostname=names.hostname, realm=names.realm)
1135 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1137 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1140 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1142 message("Once the above files are installed, your Samba4 server will be ready to use")
1143 message("Server Role: %s" % serverrole)
1144 message("Hostname: %s" % names.hostname)
1145 message("NetBIOS Domain: %s" % names.domain)
1146 message("DNS Domain: %s" % names.dnsdomain)
1147 message("DOMAIN SID: %s" % str(domainsid))
1148 if samdb_fill == FILL_FULL:
1149 message("Admin password: %s" % adminpass)
1151 result = ProvisionResult()
1152 result.domaindn = domaindn
1153 result.paths = paths
1155 result.samdb = samdb
1159 def provision_become_dc(setup_dir=None,
1160 smbconf=None, targetdir=None, realm=None,
1161 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1163 domain=None, hostname=None, domainsid=None,
1164 adminpass=None, krbtgtpass=None, domainguid=None,
1165 policyguid=None, invocationid=None, machinepass=None,
1166 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1167 wheel=None, backup=None, aci=None, serverrole=None,
1168 ldap_backend=None, ldap_backend_type=None, sitename=None):
1171 """print a message if quiet is not set."""
1174 return provision(setup_dir, message, system_session(), None,
1175 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1176 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1177 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1180 def setup_db_config(setup_path, dbdir):
1181 """Setup a Berkeley database.
1183 :param setup_path: Setup path function.
1184 :param dbdir: Database directory."""
1185 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1186 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1187 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1188 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1190 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1191 {"LDAPDBDIR": dbdir})
1195 def provision_backend(setup_dir=None, message=None,
1196 smbconf=None, targetdir=None, realm=None,
1197 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1198 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1199 ldap_backend_type=None, ldap_backend_port=None,
1200 ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
1202 def setup_path(file):
1203 return os.path.join(setup_dir, file)
1205 if hostname is None:
1206 hostname = socket.gethostname().split(".")[0].lower()
1209 root = findnss(pwd.getpwnam, ["root"])[0]
1211 if adminpass is None:
1212 adminpass = glue.generate_random_str(12)
1214 if targetdir is not None:
1215 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1216 os.makedirs(os.path.join(targetdir, "etc"))
1217 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1218 elif smbconf is None:
1219 smbconf = param.default_path()
1220 assert smbconf is not None
1222 # only install a new smb.conf if there isn't one there already
1223 if not os.path.exists(smbconf):
1224 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1227 # openldap-online-configuration: validation of olc and slaptest
1228 if ol_olc == "yes" and ol_slaptest is None:
1229 sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slaptest-Binary!")
1231 if ol_olc == "yes" and ol_slaptest is not None:
1232 ol_slaptest = ol_slaptest + "/slaptest"
1233 if not os.path.exists(ol_slaptest):
1234 message (ol_slaptest)
1235 sys.exit("Warning: Given Path to slaptest-Binary does not exist!")
1240 lp = param.LoadParm()
1243 if serverrole is None:
1244 serverrole = lp.get("server role")
1246 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1247 dnsdomain=realm, serverrole=serverrole,
1248 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1251 paths = provision_paths_from_lp(lp, names.dnsdomain)
1253 if not os.path.isdir(paths.ldapdir):
1254 os.makedirs(paths.ldapdir, 0700)
1255 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1257 os.unlink(schemadb_path)
1261 schemadb = SamDB(schemadb_path, lp=lp)
1262 schemadb.transaction_start()
1265 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1267 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1268 {"SCHEMADN": names.schemadn,
1271 setup_modify_ldif(schemadb,
1272 setup_path("provision_schema_basedn_modify.ldif"), \
1273 {"SCHEMADN": names.schemadn,
1274 "NETBIOSNAME": names.netbiosname,
1275 "DEFAULTSITE": DEFAULTSITE,
1276 "CONFIGDN": names.configdn,
1277 "SERVERDN": names.serverdn,
1278 "PREFIXMAP_B64": b64encode(prefixmap)
1281 data = load_schema(setup_path, schemadb, names.schemadn, names.netbiosname,
1282 names.configdn, DEFAULTSITE, names.serverdn)
1283 schemadb.add_ldif(data)
1285 schemadb.transaction_cancel()
1287 schemadb.transaction_commit()
1289 if ldap_backend_type == "fedora-ds":
1290 if ldap_backend_port is not None:
1291 serverport = "ServerPort=%d" % ldap_backend_port
1295 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1297 "HOSTNAME": hostname,
1298 "DNSDOMAIN": names.dnsdomain,
1299 "LDAPDIR": paths.ldapdir,
1300 "DOMAINDN": names.domaindn,
1301 "LDAPMANAGERDN": names.ldapmanagerdn,
1302 "LDAPMANAGERPASS": adminpass,
1303 "SERVERPORT": serverport})
1305 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1306 {"CONFIGDN": names.configdn,
1307 "SCHEMADN": names.schemadn,
1310 mapping = "schema-map-fedora-ds-1.0"
1311 backend_schema = "99_ad.ldif"
1313 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1315 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1317 elif ldap_backend_type == "openldap":
1318 attrs = ["linkID", "lDAPDisplayName"]
1319 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)
1321 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1322 refint_attributes = ""
1323 for i in range (0, len(res)):
1324 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1325 target = schemadb.searchone(basedn=names.schemadn,
1326 expression=expression,
1327 attribute="lDAPDisplayName",
1328 scope=SCOPE_SUBTREE)
1329 if target is not None:
1330 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1332 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1333 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1334 "MEMBEROF_ATTR" : str(target) })
1336 refint_config = read_and_sub_file(setup_path("refint.conf"),
1337 { "LINK_ATTRS" : refint_attributes})
1339 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1341 mmr_replicator_acl = ""
1342 mmr_serverids_config = ""
1343 mmr_syncrepl_schema_config = ""
1344 mmr_syncrepl_config_config = ""
1345 mmr_syncrepl_user_config = ""
1348 if ol_mmr_urls is not None:
1349 # For now, make these equal
1350 mmr_pass = adminpass
1352 url_list=filter(None,ol_mmr_urls.split(' '))
1353 if (len(url_list) == 1):
1354 url_list=filter(None,ol_mmr_urls.split(','))
1357 mmr_on_config = "MirrorMode On"
1358 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1360 for url in url_list:
1362 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1363 { "SERVERID" : str(serverid),
1364 "LDAPSERVER" : url })
1367 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1369 "MMRDN": names.schemadn,
1371 "MMR_PASSWORD": mmr_pass})
1374 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1376 "MMRDN": names.configdn,
1378 "MMR_PASSWORD": mmr_pass})
1381 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1383 "MMRDN": names.domaindn,
1385 "MMR_PASSWORD": mmr_pass })
1387 olc_config_pass = ""
1389 olc_syncrepl_config = ""
1392 olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
1393 { "OLC_PW": adminpass })
1394 olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
1396 # if olc = yes + mmr = yes, generate cn=config-replication directives
1397 # and olc_seed.lif for the other mmr-servers
1398 if ol_olc == "yes" and ol_mmr_urls is not None:
1400 olc_serverids_config = ""
1401 olc_syncrepl_config = ""
1402 olc_syncrepl_seed_config = ""
1404 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1406 for url in url_list:
1408 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1409 { "SERVERID" : str(serverid),
1410 "LDAPSERVER" : url })
1413 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1416 "MMR_PASSWORD": adminpass})
1418 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1420 "LDAPSERVER" : url})
1422 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1423 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1424 "OLC_PW": adminpass,
1425 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1430 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1431 {"DNSDOMAIN": names.dnsdomain,
1432 "LDAPDIR": paths.ldapdir,
1433 "DOMAINDN": names.domaindn,
1434 "CONFIGDN": names.configdn,
1435 "SCHEMADN": names.schemadn,
1436 "MEMBEROF_CONFIG": memberof_config,
1437 "MIRRORMODE": mmr_on_config,
1438 "REPLICATOR_ACL": mmr_replicator_acl,
1439 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1440 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1441 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1442 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1443 "OLC_CONFIG_PASS": olc_config_pass,
1444 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1445 "OLC_CONFIG_ACL": olc_config_acl,
1446 "OLC_MMR_CONFIG": olc_mmr_config,
1447 "REFINT_CONFIG": refint_config})
1448 setup_file(setup_path("modules.conf"), paths.modulesconf,
1449 {"REALM": names.realm})
1451 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1452 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1453 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1455 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1456 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1458 setup_file(setup_path("cn=samba.ldif"),
1459 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1460 { "UUID": str(uuid.uuid4()),
1461 "LDAPTIME": timestring(int(time.time()))} )
1462 setup_file(setup_path("cn=samba-admin.ldif"),
1463 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1464 {"LDAPADMINPASS_B64": b64encode(adminpass),
1465 "UUID": str(uuid.uuid4()),
1466 "LDAPTIME": timestring(int(time.time()))} )
1468 if ol_mmr_urls is not None:
1469 setup_file(setup_path("cn=replicator.ldif"),
1470 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1471 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1472 "UUID": str(uuid.uuid4()),
1473 "LDAPTIME": timestring(int(time.time()))} )
1476 mapping = "schema-map-openldap-2.3"
1477 backend_schema = "backend-schema.schema"
1479 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1480 if ldap_backend_port is not None:
1481 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1483 server_port_string = ""
1485 if ol_olc != "yes" and ol_mmr_urls is None:
1486 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1488 if ol_olc == "yes" and ol_mmr_urls is None:
1489 slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1491 if ol_olc != "yes" and ol_mmr_urls is not None:
1492 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1494 if ol_olc == "yes" and ol_mmr_urls is not None:
1495 slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1498 ldapuser = "--username=samba-admin"
1501 backend_schema_data = schemadb.convert_schema_to_openldap(ldap_backend_type, open(setup_path(mapping), 'r').read())
1502 assert backend_schema_data is not None
1503 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1505 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1506 message("Server Role: %s" % serverrole)
1507 message("Hostname: %s" % names.hostname)
1508 message("DNS Domain: %s" % names.dnsdomain)
1509 message("Base DN: %s" % names.domaindn)
1511 if ldap_backend_type == "openldap":
1512 message("LDAP admin user: samba-admin")
1514 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1516 message("LDAP admin password: %s" % adminpass)
1517 message(slapdcommand)
1518 if ol_olc == "yes" or ol_mmr_urls is not None:
1519 message("Attention to slapd-Port: <PORT> must be different than 389!")
1520 assert isinstance(ldap_backend_type, str)
1521 assert isinstance(ldapuser, str)
1522 assert isinstance(adminpass, str)
1523 assert isinstance(names.dnsdomain, str)
1524 assert isinstance(names.domain, str)
1525 assert isinstance(serverrole, str)
1526 args = ["--ldap-backend=ldapi",
1527 "--ldap-backend-type=" + ldap_backend_type,
1528 "--password=" + adminpass,
1530 "--realm=" + names.dnsdomain,
1531 "--domain=" + names.domain,
1532 "--server-role='" + serverrole + "'"]
1533 message("Run provision with: " + " ".join(args))
1536 # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d
1538 if not os.path.isdir(paths.olcdir):
1539 os.makedirs(paths.olcdir, 0770)
1540 paths.olslaptest = str(ol_slaptest)
1541 olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir + " >/dev/null 2>&1"
1542 os.system(olc_command)
1543 os.remove(paths.slapdconf)
1544 # use line below for debugging during olc-conversion with slaptest, instead of olc_command above
1545 #olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir"
1548 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1549 """Create a PHP LDAP admin configuration file.
1551 :param path: Path to write the configuration to.
1552 :param setup_path: Function to generate setup paths.
1554 setup_file(setup_path("phpldapadmin-config.php"), path,
1555 {"S4_LDAPI_URI": ldapi_uri})
1558 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1559 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1560 """Write out a DNS zone file, from the info in the current database.
1562 :param path: Path of the new zone file.
1563 :param setup_path: Setup path function.
1564 :param dnsdomain: DNS Domain name
1565 :param domaindn: DN of the Domain
1566 :param hostip: Local IPv4 IP
1567 :param hostip6: Local IPv6 IP
1568 :param hostname: Local hostname
1569 :param dnspass: Password for DNS
1570 :param realm: Realm name
1571 :param domainguid: GUID of the domain.
1572 :param hostguid: GUID of the host.
1574 assert isinstance(domainguid, str)
1576 if hostip6 is not None:
1577 hostip6_base_line = " IN AAAA " + hostip6
1578 hostip6_host_line = hostname + " IN AAAA " + hostip6
1580 hostip6_base_line = ""
1581 hostip6_host_line = ""
1583 if hostip is not None:
1584 hostip_base_line = " IN A " + hostip
1585 hostip_host_line = hostname + " IN A " + hostip
1587 hostip_base_line = ""
1588 hostip_host_line = ""
1590 setup_file(setup_path("provision.zone"), path, {
1591 "DNSPASS_B64": b64encode(dnspass),
1592 "HOSTNAME": hostname,
1593 "DNSDOMAIN": dnsdomain,
1595 "HOSTIP_BASE_LINE": hostip_base_line,
1596 "HOSTIP_HOST_LINE": hostip_host_line,
1597 "DOMAINGUID": domainguid,
1598 "DATESTRING": time.strftime("%Y%m%d%H"),
1599 "DEFAULTSITE": DEFAULTSITE,
1600 "HOSTGUID": hostguid,
1601 "HOSTIP6_BASE_LINE": hostip6_base_line,
1602 "HOSTIP6_HOST_LINE": hostip6_host_line,
1606 def create_named_conf(path, setup_path, realm, dnsdomain,
1608 """Write out a file containing zone statements suitable for inclusion in a
1609 named.conf file (including GSS-TSIG configuration).
1611 :param path: Path of the new named.conf file.
1612 :param setup_path: Setup path function.
1613 :param realm: Realm name
1614 :param dnsdomain: DNS Domain name
1615 :param private_dir: Path to private directory
1616 :param keytab_name: File name of DNS keytab file
1619 setup_file(setup_path("named.conf"), path, {
1620 "DNSDOMAIN": dnsdomain,
1622 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1623 "PRIVATE_DIR": private_dir
1626 def create_named_txt(path, setup_path, realm, dnsdomain,
1627 private_dir, keytab_name):
1628 """Write out a file containing zone statements suitable for inclusion in a
1629 named.conf file (including GSS-TSIG configuration).
1631 :param path: Path of the new named.conf file.
1632 :param setup_path: Setup path function.
1633 :param realm: Realm name
1634 :param dnsdomain: DNS Domain name
1635 :param private_dir: Path to private directory
1636 :param keytab_name: File name of DNS keytab file
1639 setup_file(setup_path("named.txt"), path, {
1640 "DNSDOMAIN": dnsdomain,
1642 "DNS_KEYTAB": keytab_name,
1643 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1644 "PRIVATE_DIR": private_dir
1647 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1648 """Write out a file containing zone statements suitable for inclusion in a
1649 named.conf file (including GSS-TSIG configuration).
1651 :param path: Path of the new named.conf file.
1652 :param setup_path: Setup path function.
1653 :param dnsdomain: DNS Domain name
1654 :param hostname: Local hostname
1655 :param realm: Realm name
1658 setup_file(setup_path("krb5.conf"), path, {
1659 "DNSDOMAIN": dnsdomain,
1660 "HOSTNAME": hostname,
1665 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1667 """Load schema for the SamDB.
1669 :param samdb: Load a schema into a SamDB.
1670 :param setup_path: Setup path function.
1671 :param schemadn: DN of the schema
1672 :param netbiosname: NetBIOS name of the host.
1673 :param configdn: DN of the configuration
1674 :param serverdn: DN of the server
1676 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
1678 schema_data = get_schema_data(setup_path, {"SCHEMADN": schemadn})
1679 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1680 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1681 check_all_substituted(schema_data)
1682 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1683 prefixmap = b64encode(prefixmap)
1685 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1686 head_data = substitute_var(head_data, {
1687 "SCHEMADN": schemadn,
1688 "NETBIOSNAME": netbiosname,
1689 "CONFIGDN": configdn,
1690 "DEFAULTSITE": sitename,
1691 "PREFIXMAP_B64": prefixmap,
1692 "SERVERDN": serverdn,
1694 check_all_substituted(head_data)
1695 samdb.attach_schema_from_ldif(head_data, schema_data)
1698 def get_schema_data(setup_path, subst_vars = None):
1699 """Get schema data from the AD schema files instead of schema.ldif.
1701 :param setup_path: Setup path function.
1702 :param subst_vars: Optional variables to substitute in the file.
1704 Returns the schema data after substitution
1707 # this data used to be read from schema.ldif
1709 data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
1710 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
1712 if subst_vars is not None:
1713 data = substitute_var(data, subst_vars)
1714 check_all_substituted(data)