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
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 from base64 import b64encode
37 from auth import system_session
38 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
39 from samba.samdb import SamDB
40 from samba.idmap import IDmapDB
41 from samba.dcerpc import security
43 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
44 timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
46 __docformat__ = "restructuredText"
50 """Find the setup directory used by provision."""
51 dirname = os.path.dirname(__file__)
52 if "/site-packages/" in dirname:
53 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
54 for suffix in ["share/setup", "share/samba/setup", "setup"]:
55 ret = os.path.join(prefix, suffix)
56 if os.path.isdir(ret):
59 ret = os.path.join(dirname, "../../../setup")
60 if os.path.isdir(ret):
62 raise Exception("Unable to find setup directory.")
65 DEFAULTSITE = "Default-First-Site-Name"
67 class InvalidNetbiosName(Exception):
68 """A specified name was not a valid NetBIOS name."""
69 def __init__(self, name):
70 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
73 class ProvisionPaths(object):
86 self.dns_keytab = None
89 self.private_dir = None
92 self.modulesconf = None
93 self.memberofconf = None
94 self.fedoradsinf = None
95 self.fedoradspartitions = None
97 self.olmmrserveridsconf = None
98 self.olmmrsyncreplconf = None
101 class ProvisionNames(object):
107 self.ldapmanagerdn = None
108 self.dnsdomain = None
110 self.netbiosname = None
117 class ProvisionResult(object):
124 def check_install(lp, session_info, credentials):
125 """Check whether the current install seems ok.
127 :param lp: Loadparm context
128 :param session_info: Session information
129 :param credentials: Credentials
131 if lp.get("realm") == "":
132 raise Exception("Realm empty")
133 ldb = Ldb(lp.get("sam database"), session_info=session_info,
134 credentials=credentials, lp=lp)
135 if len(ldb.search("(cn=Administrator)")) != 1:
136 raise "No administrator account found"
139 def findnss(nssfn, names):
140 """Find a user or group from a list of possibilities.
142 :param nssfn: NSS Function to try (should raise KeyError if not found)
143 :param names: Names to check.
144 :return: Value return by first names list.
151 raise KeyError("Unable to find user/group %r" % names)
154 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
155 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
158 def read_and_sub_file(file, subst_vars):
159 """Read a file and sub in variables found in it
161 :param file: File to be read (typically from setup directory)
162 param subst_vars: Optional variables to subsitute in the file.
164 data = open(file, 'r').read()
165 if subst_vars is not None:
166 data = substitute_var(data, subst_vars)
167 check_all_substituted(data)
171 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
172 """Setup a ldb in the private dir.
174 :param ldb: LDB file to import data into
175 :param ldif_path: Path of the LDIF file to load
176 :param subst_vars: Optional variables to subsitute in LDIF.
178 assert isinstance(ldif_path, str)
180 data = read_and_sub_file(ldif_path, subst_vars)
184 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
185 """Modify a ldb in the private dir.
187 :param ldb: LDB object.
188 :param ldif_path: LDIF file path.
189 :param subst_vars: Optional dictionary with substitution variables.
191 data = read_and_sub_file(ldif_path, subst_vars)
193 ldb.modify_ldif(data)
196 def setup_ldb(ldb, ldif_path, subst_vars):
197 """Import a LDIF a file into a LDB handle, optionally substituting variables.
199 :note: Either all LDIF data will be added or none (using transactions).
201 :param ldb: LDB file to import into.
202 :param ldif_path: Path to the LDIF file.
203 :param subst_vars: Dictionary with substitution variables.
205 assert ldb is not None
206 ldb.transaction_start()
208 setup_add_ldif(ldb, ldif_path, subst_vars)
210 ldb.transaction_cancel()
212 ldb.transaction_commit()
215 def setup_file(template, fname, subst_vars):
216 """Setup a file in the private dir.
218 :param template: Path of the template file.
219 :param fname: Path of the file to create.
220 :param subst_vars: Substitution variables.
224 if os.path.exists(f):
227 data = read_and_sub_file(template, subst_vars)
228 open(f, 'w').write(data)
231 def provision_paths_from_lp(lp, dnsdomain):
232 """Set the default paths for provisioning.
234 :param lp: Loadparm context.
235 :param dnsdomain: DNS Domain name
237 paths = ProvisionPaths()
238 paths.private_dir = lp.get("private dir")
239 paths.keytab = "secrets.keytab"
240 paths.dns_keytab = "dns.keytab"
242 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
243 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
244 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
245 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
246 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
247 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
248 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
249 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
250 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
251 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
252 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
253 paths.phpldapadminconfig = os.path.join(paths.private_dir,
254 "phpldapadmin-config.php")
255 paths.ldapdir = os.path.join(paths.private_dir,
257 paths.slapdconf = os.path.join(paths.ldapdir,
259 paths.modulesconf = os.path.join(paths.ldapdir,
261 paths.memberofconf = os.path.join(paths.ldapdir,
263 paths.fedoradsinf = os.path.join(paths.ldapdir,
265 paths.fedoradspartitions = os.path.join(paths.ldapdir,
266 "fedorads-partitions.ldif")
267 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
268 "mmr_serverids.conf")
269 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
271 paths.hklm = "hklm.ldb"
272 paths.hkcr = "hkcr.ldb"
273 paths.hkcu = "hkcu.ldb"
274 paths.hku = "hku.ldb"
275 paths.hkpd = "hkpd.ldb"
276 paths.hkpt = "hkpt.ldb"
278 paths.sysvol = lp.get("path", "sysvol")
280 paths.netlogon = lp.get("path", "netlogon")
282 paths.smbconf = lp.configfile
287 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
288 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
290 """Guess configuration settings to use."""
293 hostname = socket.gethostname().split(".")[0].lower()
295 netbiosname = hostname.upper()
296 if not valid_netbios_name(netbiosname):
297 raise InvalidNetbiosName(netbiosname)
299 hostname = hostname.lower()
301 if dnsdomain is None:
302 dnsdomain = lp.get("realm")
304 if serverrole is None:
305 serverrole = lp.get("server role")
307 assert dnsdomain is not None
308 realm = dnsdomain.upper()
310 if lp.get("realm").upper() != realm:
311 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
312 (lp.get("realm"), lp.configfile, realm))
314 dnsdomain = dnsdomain.lower()
316 if serverrole == "domain controller":
318 domain = lp.get("workgroup")
320 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
321 if lp.get("workgroup").upper() != domain.upper():
322 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
323 lp.get("workgroup"), domain)
327 domaindn = "CN=" + netbiosname
329 assert domain is not None
330 domain = domain.upper()
331 if not valid_netbios_name(domain):
332 raise InvalidNetbiosName(domain)
338 configdn = "CN=Configuration," + rootdn
340 schemadn = "CN=Schema," + configdn
345 names = ProvisionNames()
346 names.rootdn = rootdn
347 names.domaindn = domaindn
348 names.configdn = configdn
349 names.schemadn = schemadn
350 names.ldapmanagerdn = "CN=Manager," + rootdn
351 names.dnsdomain = dnsdomain
352 names.domain = domain
354 names.netbiosname = netbiosname
355 names.hostname = hostname
356 names.sitename = sitename
357 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
362 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
364 """Create a new smb.conf file based on a couple of basic settings.
366 assert smbconf is not None
368 hostname = socket.gethostname().split(".")[0].lower()
370 if serverrole is None:
371 serverrole = "standalone"
373 assert serverrole in ("domain controller", "member server", "standalone")
374 if serverrole == "domain controller":
376 elif serverrole == "member server":
377 smbconfsuffix = "member"
378 elif serverrole == "standalone":
379 smbconfsuffix = "standalone"
381 assert domain is not None
382 assert realm is not None
384 default_lp = param.LoadParm()
385 #Load non-existant file
386 if os.path.exists(smbconf):
387 default_lp.load(smbconf)
389 if targetdir is not None:
390 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
391 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
393 default_lp.set("lock dir", os.path.abspath(targetdir))
398 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
399 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
401 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
403 "HOSTNAME": hostname,
406 "SERVERROLE": serverrole,
407 "NETLOGONPATH": netlogon,
408 "SYSVOLPATH": sysvol,
409 "PRIVATEDIR_LINE": privatedir_line,
410 "LOCKDIR_LINE": lockdir_line
414 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
415 users_gid, wheel_gid):
416 """setup reasonable name mappings for sam names to unix names.
418 :param samdb: SamDB object.
419 :param idmap: IDmap db object.
420 :param sid: The domain sid.
421 :param domaindn: The domain DN.
422 :param root_uid: uid of the UNIX root user.
423 :param nobody_uid: uid of the UNIX nobody user.
424 :param users_gid: gid of the UNIX users group.
425 :param wheel_gid: gid of the UNIX wheel group."""
426 # add some foreign sids if they are not present already
427 samdb.add_stock_foreign_sids()
429 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
430 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
432 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
433 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
436 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
438 serverrole, ldap_backend=None,
439 ldap_backend_type=None, erase=False):
440 """Setup the partitions for the SAM database.
442 Alternatively, provision() may call this, and then populate the database.
444 :note: This will wipe the Sam Database!
446 :note: This function always removes the local SAM LDB file. The erase
447 parameter controls whether to erase the existing data, which
448 may not be stored locally but in LDAP.
450 assert session_info is not None
453 samdb = SamDB(samdb_path, session_info=session_info,
454 credentials=credentials, lp=lp)
458 os.unlink(samdb_path)
459 samdb = SamDB(samdb_path, session_info=session_info,
460 credentials=credentials, lp=lp)
465 #Add modules to the list to activate them by default
466 #beware often order is important
468 # Some Known ordering constraints:
469 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
470 # - objectclass must be before password_hash, because password_hash checks
471 # that the objectclass is of type person (filled in by objectclass
472 # module when expanding the objectclass list)
473 # - partition must be last
474 # - each partition has its own module list then
475 modules_list = ["rootdse",
493 "extended_dn_out_ldb"]
494 modules_list2 = ["show_deleted",
497 domaindn_ldb = "users.ldb"
498 if ldap_backend is not None:
499 domaindn_ldb = ldap_backend
500 configdn_ldb = "configuration.ldb"
501 if ldap_backend is not None:
502 configdn_ldb = ldap_backend
503 schemadn_ldb = "schema.ldb"
504 if ldap_backend is not None:
505 schema_ldb = ldap_backend
506 schemadn_ldb = ldap_backend
508 if ldap_backend_type == "fedora-ds":
509 backend_modules = ["nsuniqueid", "paged_searches"]
510 # We can handle linked attributes here, as we don't have directory-side subtree operations
511 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
512 elif ldap_backend_type == "openldap":
513 backend_modules = ["entryuuid", "paged_searches"]
514 # OpenLDAP handles subtree renames, so we don't want to do any of these things
515 tdb_modules_list = ["extended_dn_out_dereference"]
516 elif ldap_backend is not None:
517 raise "LDAP Backend specified, but LDAP Backend Type not specified"
518 elif serverrole == "domain controller":
519 backend_modules = ["repl_meta_data"]
521 backend_modules = ["objectguid"]
523 if tdb_modules_list is None:
524 tdb_modules_list_as_string = ""
526 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
528 samdb.transaction_start()
530 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
531 "SCHEMADN": names.schemadn,
532 "SCHEMADN_LDB": schemadn_ldb,
533 "SCHEMADN_MOD2": ",objectguid",
534 "CONFIGDN": names.configdn,
535 "CONFIGDN_LDB": configdn_ldb,
536 "DOMAINDN": names.domaindn,
537 "DOMAINDN_LDB": domaindn_ldb,
538 "SCHEMADN_MOD": "schema_fsmo,instancetype",
539 "CONFIGDN_MOD": "naming_fsmo,instancetype",
540 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
541 "MODULES_LIST": ",".join(modules_list),
542 "TDB_MODULES_LIST": tdb_modules_list_as_string,
543 "MODULES_LIST2": ",".join(modules_list2),
544 "BACKEND_MOD": ",".join(backend_modules),
548 samdb.transaction_cancel()
551 samdb.transaction_commit()
553 samdb = SamDB(samdb_path, session_info=session_info,
554 credentials=credentials, lp=lp)
556 samdb.transaction_start()
558 message("Setting up sam.ldb attributes")
559 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
561 message("Setting up sam.ldb rootDSE")
562 setup_samdb_rootdse(samdb, setup_path, names)
565 message("Erasing data from partitions")
566 samdb.erase_partitions()
569 samdb.transaction_cancel()
572 samdb.transaction_commit()
577 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
578 netbiosname, domainsid, keytab_path, samdb_url,
579 dns_keytab_path, dnspass, machinepass):
580 """Add DC-specific bits to a secrets database.
582 :param secretsdb: Ldb Handle to the secrets database
583 :param setup_path: Setup path function
584 :param machinepass: Machine password
586 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
587 "MACHINEPASS_B64": b64encode(machinepass),
590 "DNSDOMAIN": dnsdomain,
591 "DOMAINSID": str(domainsid),
592 "SECRETS_KEYTAB": keytab_path,
593 "NETBIOSNAME": netbiosname,
594 "SAM_LDB": samdb_url,
595 "DNS_KEYTAB": dns_keytab_path,
596 "DNSPASS_B64": b64encode(dnspass),
600 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
601 """Setup the secrets database.
603 :param path: Path to the secrets database.
604 :param setup_path: Get the path to a setup file.
605 :param session_info: Session info.
606 :param credentials: Credentials
607 :param lp: Loadparm context
608 :return: LDB handle for the created secrets database
610 if os.path.exists(path):
612 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
615 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
616 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
618 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
620 if credentials is not None and credentials.authentication_requested():
621 if credentials.get_bind_dn() is not None:
622 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
623 "LDAPMANAGERDN": credentials.get_bind_dn(),
624 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
627 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
628 "LDAPADMINUSER": credentials.get_username(),
629 "LDAPADMINREALM": credentials.get_realm(),
630 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
636 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
637 """Setup the templates database.
639 :param path: Path to the database.
640 :param setup_path: Function for obtaining the path to setup files.
641 :param session_info: Session info
642 :param credentials: Credentials
643 :param lp: Loadparm context
645 templates_ldb = SamDB(path, session_info=session_info,
646 credentials=credentials, lp=lp)
649 templates_ldb.erase()
650 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
654 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
656 templates_ldb = SamDB(path, session_info=session_info,
657 credentials=credentials, lp=lp)
659 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
662 def setup_registry(path, setup_path, session_info, credentials, lp):
663 """Setup the registry.
665 :param path: Path to the registry database
666 :param setup_path: Function that returns the path to a setup.
667 :param session_info: Session information
668 :param credentials: Credentials
669 :param lp: Loadparm context
671 reg = registry.Registry()
672 hive = registry.open_ldb(path, session_info=session_info,
673 credentials=credentials, lp_ctx=lp)
674 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
675 provision_reg = setup_path("provision.reg")
676 assert os.path.exists(provision_reg)
677 reg.diff_apply(provision_reg)
680 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
681 """Setup the idmap database.
683 :param path: path to the idmap database
684 :param setup_path: Function that returns a path to a setup file
685 :param session_info: Session information
686 :param credentials: Credentials
687 :param lp: Loadparm context
689 if os.path.exists(path):
692 idmap_ldb = IDmapDB(path, session_info=session_info,
693 credentials=credentials, lp=lp)
696 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
700 def setup_samdb_rootdse(samdb, setup_path, names):
701 """Setup the SamDB rootdse.
703 :param samdb: Sam Database handle
704 :param setup_path: Obtain setup path
706 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
707 "SCHEMADN": names.schemadn,
708 "NETBIOSNAME": names.netbiosname,
709 "DNSDOMAIN": names.dnsdomain,
710 "REALM": names.realm,
711 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
712 "DOMAINDN": names.domaindn,
713 "ROOTDN": names.rootdn,
714 "CONFIGDN": names.configdn,
715 "SERVERDN": names.serverdn,
719 def setup_self_join(samdb, names,
720 machinepass, dnspass,
721 domainsid, invocationid, setup_path,
723 """Join a host to its own domain."""
724 assert isinstance(invocationid, str)
725 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
726 "CONFIGDN": names.configdn,
727 "SCHEMADN": names.schemadn,
728 "DOMAINDN": names.domaindn,
729 "SERVERDN": names.serverdn,
730 "INVOCATIONID": invocationid,
731 "NETBIOSNAME": names.netbiosname,
732 "DEFAULTSITE": names.sitename,
733 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
734 "MACHINEPASS_B64": b64encode(machinepass),
735 "DNSPASS_B64": b64encode(dnspass),
736 "REALM": names.realm,
737 "DOMAIN": names.domain,
738 "DNSDOMAIN": names.dnsdomain})
739 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
740 "POLICYGUID": policyguid,
741 "DNSDOMAIN": names.dnsdomain,
742 "DOMAINSID": str(domainsid),
743 "DOMAINDN": names.domaindn})
746 def setup_samdb(path, setup_path, session_info, credentials, lp,
748 domainsid, aci, domainguid, policyguid,
749 fill, adminpass, krbtgtpass,
750 machinepass, invocationid, dnspass,
751 serverrole, ldap_backend=None,
752 ldap_backend_type=None):
753 """Setup a complete SAM Database.
755 :note: This will wipe the main SAM database file!
758 erase = (fill != FILL_DRS)
760 # Also wipes the database
761 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
762 credentials=credentials, session_info=session_info,
764 ldap_backend=ldap_backend, serverrole=serverrole,
765 ldap_backend_type=ldap_backend_type, erase=erase)
767 samdb = SamDB(path, session_info=session_info,
768 credentials=credentials, lp=lp)
772 message("Pre-loading the Samba 4 and AD schema")
773 samdb.set_domain_sid(str(domainsid))
774 if serverrole == "domain controller":
775 samdb.set_invocation_id(invocationid)
777 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
778 names.configdn, names.sitename, names.serverdn,
781 samdb.transaction_start()
784 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
785 if serverrole == "domain controller":
786 domain_oc = "domainDNS"
788 domain_oc = "samba4LocalDomain"
790 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
791 "DOMAINDN": names.domaindn,
793 "DOMAIN_OC": domain_oc
796 message("Modifying DomainDN: " + names.domaindn + "")
797 if domainguid is not None:
798 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
802 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
803 "LDAPTIME": timestring(int(time.time())),
804 "DOMAINSID": str(domainsid),
805 "SCHEMADN": names.schemadn,
806 "NETBIOSNAME": names.netbiosname,
807 "DEFAULTSITE": names.sitename,
808 "CONFIGDN": names.configdn,
809 "SERVERDN": names.serverdn,
810 "POLICYGUID": policyguid,
811 "DOMAINDN": names.domaindn,
812 "DOMAINGUID_MOD": domainguid_mod,
815 message("Adding configuration container (permitted to fail)")
816 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
817 "CONFIGDN": names.configdn,
820 message("Modifying configuration container")
821 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
822 "CONFIGDN": names.configdn,
823 "SCHEMADN": names.schemadn,
826 message("Adding schema container (permitted to fail)")
827 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
828 "SCHEMADN": names.schemadn,
831 message("Modifying schema container")
833 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
835 setup_modify_ldif(samdb,
836 setup_path("provision_schema_basedn_modify.ldif"), {
837 "SCHEMADN": names.schemadn,
838 "NETBIOSNAME": names.netbiosname,
839 "DEFAULTSITE": names.sitename,
840 "CONFIGDN": names.configdn,
841 "SERVERDN": names.serverdn,
842 "PREFIXMAP_B64": b64encode(prefixmap)
845 message("Setting up sam.ldb Samba4 schema")
846 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
847 {"SCHEMADN": names.schemadn })
848 message("Setting up sam.ldb AD schema")
849 setup_add_ldif(samdb, setup_path("schema.ldif"),
850 {"SCHEMADN": names.schemadn})
851 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
852 {"SCHEMADN": names.schemadn})
854 message("Setting up sam.ldb configuration data")
855 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
856 "CONFIGDN": names.configdn,
857 "NETBIOSNAME": names.netbiosname,
858 "DEFAULTSITE": names.sitename,
859 "DNSDOMAIN": names.dnsdomain,
860 "DOMAIN": names.domain,
861 "SCHEMADN": names.schemadn,
862 "DOMAINDN": names.domaindn,
863 "SERVERDN": names.serverdn
866 message("Setting up display specifiers")
867 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
868 {"CONFIGDN": names.configdn})
870 message("Adding users container (permitted to fail)")
871 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
872 "DOMAINDN": names.domaindn})
873 message("Modifying users container")
874 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
875 "DOMAINDN": names.domaindn})
876 message("Adding computers container (permitted to fail)")
877 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
878 "DOMAINDN": names.domaindn})
879 message("Modifying computers container")
880 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
881 "DOMAINDN": names.domaindn})
882 message("Setting up sam.ldb data")
883 setup_add_ldif(samdb, setup_path("provision.ldif"), {
884 "DOMAINDN": names.domaindn,
885 "NETBIOSNAME": names.netbiosname,
886 "DEFAULTSITE": names.sitename,
887 "CONFIGDN": names.configdn,
888 "SERVERDN": names.serverdn
891 if fill == FILL_FULL:
892 message("Setting up sam.ldb users and groups")
893 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
894 "DOMAINDN": names.domaindn,
895 "DOMAINSID": str(domainsid),
896 "CONFIGDN": names.configdn,
897 "ADMINPASS_B64": b64encode(adminpass),
898 "KRBTGTPASS_B64": b64encode(krbtgtpass),
901 if serverrole == "domain controller":
902 message("Setting up self join")
903 setup_self_join(samdb, names=names, invocationid=invocationid,
905 machinepass=machinepass,
906 domainsid=domainsid, policyguid=policyguid,
907 setup_path=setup_path)
910 samdb.transaction_cancel()
913 samdb.transaction_commit()
918 FILL_NT4SYNC = "NT4SYNC"
921 def provision(setup_dir, message, session_info,
922 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
923 rootdn=None, domaindn=None, schemadn=None, configdn=None,
925 domain=None, hostname=None, hostip=None, hostip6=None,
926 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
927 policyguid=None, invocationid=None, machinepass=None,
928 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
929 wheel=None, backup=None, aci=None, serverrole=None,
930 ldap_backend=None, ldap_backend_type=None, sitename=None):
933 :note: caution, this wipes all existing data!
936 def setup_path(file):
937 return os.path.join(setup_dir, file)
939 if domainsid is None:
940 domainsid = security.random_sid()
942 if policyguid is None:
943 policyguid = str(uuid.uuid4())
944 if adminpass is None:
945 adminpass = glue.generate_random_str(12)
946 if krbtgtpass is None:
947 krbtgtpass = glue.generate_random_str(12)
948 if machinepass is None:
949 machinepass = glue.generate_random_str(12)
951 dnspass = glue.generate_random_str(12)
952 root_uid = findnss_uid([root or "root"])
953 nobody_uid = findnss_uid([nobody or "nobody"])
954 users_gid = findnss_gid([users or "users"])
956 wheel_gid = findnss_gid(["wheel", "adm"])
958 wheel_gid = findnss_gid([wheel])
960 aci = "# no aci for local ldb"
962 if targetdir is not None:
963 if (not os.path.exists(os.path.join(targetdir, "etc"))):
964 os.makedirs(os.path.join(targetdir, "etc"))
965 smbconf = os.path.join(targetdir, "etc", "smb.conf")
966 elif smbconf is None:
967 smbconf = param.default_path()
969 # only install a new smb.conf if there isn't one there already
970 if not os.path.exists(smbconf):
971 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
974 lp = param.LoadParm()
977 names = guess_names(lp=lp, hostname=hostname, domain=domain,
978 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
979 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
982 paths = provision_paths_from_lp(lp, names.dnsdomain)
986 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
987 except socket.gaierror, (socket.EAI_NODATA, msg):
992 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
993 except socket.gaierror, (socket.EAI_NODATA, msg):
996 if serverrole is None:
997 serverrole = lp.get("server role")
999 assert serverrole in ("domain controller", "member server", "standalone")
1000 if invocationid is None and serverrole == "domain controller":
1001 invocationid = str(uuid.uuid4())
1003 if not os.path.exists(paths.private_dir):
1004 os.mkdir(paths.private_dir)
1006 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1008 if ldap_backend is not None:
1009 if ldap_backend == "ldapi":
1010 # provision-backend will set this path suggested slapd command line / fedorads.inf
1011 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1013 # only install a new shares config db if there is none
1014 if not os.path.exists(paths.shareconf):
1015 message("Setting up share.ldb")
1016 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1017 credentials=credentials, lp=lp)
1018 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1021 message("Setting up secrets.ldb")
1022 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1023 session_info=session_info,
1024 credentials=credentials, lp=lp)
1026 message("Setting up the registry")
1027 setup_registry(paths.hklm, setup_path, session_info,
1028 credentials=credentials, lp=lp)
1030 message("Setting up templates db")
1031 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1032 credentials=credentials, lp=lp)
1034 message("Setting up idmap db")
1035 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1036 credentials=credentials, lp=lp)
1038 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1039 credentials=credentials, lp=lp, names=names,
1041 domainsid=domainsid,
1042 aci=aci, domainguid=domainguid, policyguid=policyguid,
1044 adminpass=adminpass, krbtgtpass=krbtgtpass,
1045 invocationid=invocationid,
1046 machinepass=machinepass, dnspass=dnspass,
1047 serverrole=serverrole, ldap_backend=ldap_backend,
1048 ldap_backend_type=ldap_backend_type)
1050 if lp.get("server role") == "domain controller":
1051 if paths.netlogon is None:
1052 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1053 message("Please either remove %s or see the template at %s" %
1054 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1055 assert(paths.netlogon is not None)
1057 if paths.sysvol is None:
1058 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1059 message("Please either remove %s or see the template at %s" %
1060 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1061 assert(paths.sysvol is not None)
1063 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1064 "{" + policyguid + "}")
1065 os.makedirs(policy_path, 0755)
1066 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1067 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1068 os.makedirs(os.path.join(policy_path, "User"), 0755)
1069 if not os.path.isdir(paths.netlogon):
1070 os.makedirs(paths.netlogon, 0755)
1072 if samdb_fill == FILL_FULL:
1073 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1074 root_uid=root_uid, nobody_uid=nobody_uid,
1075 users_gid=users_gid, wheel_gid=wheel_gid)
1077 message("Setting up sam.ldb rootDSE marking as synchronized")
1078 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1080 # Only make a zone file on the first DC, it should be replicated with DNS replication
1081 if serverrole == "domain controller":
1082 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1083 credentials=credentials, lp=lp)
1084 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1085 netbiosname=names.netbiosname, domainsid=domainsid,
1086 keytab_path=paths.keytab, samdb_url=paths.samdb,
1087 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1088 machinepass=machinepass, dnsdomain=names.dnsdomain)
1090 samdb = SamDB(paths.samdb, session_info=session_info,
1091 credentials=credentials, lp=lp)
1093 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1094 assert isinstance(domainguid, str)
1095 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1096 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1097 scope=SCOPE_SUBTREE)
1098 assert isinstance(hostguid, str)
1100 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1101 domaindn=names.domaindn, hostip=hostip,
1102 hostip6=hostip6, hostname=names.hostname,
1103 dnspass=dnspass, realm=names.realm,
1104 domainguid=domainguid, hostguid=hostguid)
1106 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1107 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1109 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1110 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1111 keytab_name=paths.dns_keytab)
1112 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1113 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1115 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1116 hostname=names.hostname, realm=names.realm)
1117 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1119 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1122 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1124 message("Once the above files are installed, your Samba4 server will be ready to use")
1125 message("Server Role: %s" % serverrole)
1126 message("Hostname: %s" % names.hostname)
1127 message("NetBIOS Domain: %s" % names.domain)
1128 message("DNS Domain: %s" % names.dnsdomain)
1129 message("DOMAIN SID: %s" % str(domainsid))
1130 message("Admin password: %s" % adminpass)
1132 result = ProvisionResult()
1133 result.domaindn = domaindn
1134 result.paths = paths
1136 result.samdb = samdb
1140 def provision_become_dc(setup_dir=None,
1141 smbconf=None, targetdir=None, realm=None,
1142 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1144 domain=None, hostname=None, domainsid=None,
1145 adminpass=None, krbtgtpass=None, domainguid=None,
1146 policyguid=None, invocationid=None, machinepass=None,
1147 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1148 wheel=None, backup=None, aci=None, serverrole=None,
1149 ldap_backend=None, ldap_backend_type=None, sitename=None):
1152 """print a message if quiet is not set."""
1155 return provision(setup_dir, message, system_session(), None,
1156 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1157 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1158 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1161 def setup_db_config(setup_path, dbdir):
1162 """Setup a Berkeley database.
1164 :param setup_path: Setup path function.
1165 :param dbdir: Database directory."""
1166 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1167 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1168 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1169 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1171 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1172 {"LDAPDBDIR": dbdir})
1176 def provision_backend(setup_dir=None, message=None,
1177 smbconf=None, targetdir=None, realm=None,
1178 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1179 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1180 ldap_backend_type=None, ldap_backend_port=None,
1183 def setup_path(file):
1184 return os.path.join(setup_dir, file)
1186 if hostname is None:
1187 hostname = socket.gethostname().split(".")[0].lower()
1190 root = findnss(pwd.getpwnam, ["root"])[0]
1192 if adminpass is None:
1193 adminpass = glue.generate_random_str(12)
1195 if targetdir is not None:
1196 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1197 os.makedirs(os.path.join(targetdir, "etc"))
1198 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1199 elif smbconf is None:
1200 smbconf = param.default_path()
1201 assert smbconf is not None
1203 # only install a new smb.conf if there isn't one there already
1204 if not os.path.exists(smbconf):
1205 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1208 lp = param.LoadParm()
1211 if serverrole is None:
1212 serverrole = lp.get("server role")
1214 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1215 dnsdomain=realm, serverrole=serverrole,
1216 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1219 paths = provision_paths_from_lp(lp, names.dnsdomain)
1221 if not os.path.isdir(paths.ldapdir):
1222 os.makedirs(paths.ldapdir, 0700)
1223 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1225 os.unlink(schemadb_path)
1229 schemadb = Ldb(schemadb_path, lp=lp)
1231 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1233 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1234 {"SCHEMADN": names.schemadn,
1237 setup_modify_ldif(schemadb,
1238 setup_path("provision_schema_basedn_modify.ldif"), \
1239 {"SCHEMADN": names.schemadn,
1240 "NETBIOSNAME": names.netbiosname,
1241 "DEFAULTSITE": DEFAULTSITE,
1242 "CONFIGDN": names.configdn,
1243 "SERVERDN": names.serverdn,
1244 "PREFIXMAP_B64": b64encode(prefixmap)
1247 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1248 {"SCHEMADN": names.schemadn })
1249 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1250 {"SCHEMADN": names.schemadn})
1252 if ldap_backend_type == "fedora-ds":
1253 if ldap_backend_port is not None:
1254 serverport = "ServerPort=%d" % ldap_backend_port
1258 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1260 "HOSTNAME": hostname,
1261 "DNSDOMAIN": names.dnsdomain,
1262 "LDAPDIR": paths.ldapdir,
1263 "DOMAINDN": names.domaindn,
1264 "LDAPMANAGERDN": names.ldapmanagerdn,
1265 "LDAPMANAGERPASS": adminpass,
1266 "SERVERPORT": serverport})
1268 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1269 {"CONFIGDN": names.configdn,
1270 "SCHEMADN": names.schemadn,
1273 mapping = "schema-map-fedora-ds-1.0"
1274 backend_schema = "99_ad.ldif"
1276 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1278 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1280 elif ldap_backend_type == "openldap":
1281 attrs = ["linkID", "lDAPDisplayName"]
1282 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)
1284 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1285 refint_attributes = ""
1286 for i in range (0, len(res)):
1287 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1288 target = schemadb.searchone(basedn=names.schemadn,
1289 expression=expression,
1290 attribute="lDAPDisplayName",
1291 scope=SCOPE_SUBTREE)
1292 if target is not None:
1293 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1295 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1296 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1297 "MEMBEROF_ATTR" : str(target) })
1299 refint_config = read_and_sub_file(setup_path("refint.conf"),
1300 { "LINK_ATTRS" : refint_attributes})
1302 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1304 mmr_replicator_acl = ""
1305 mmr_serverids_config = ""
1306 mmr_syncrepl_schema_config = ""
1307 mmr_syncrepl_config_config = ""
1308 mmr_syncrepl_user_config = ""
1310 if ol_mmr_urls is not None:
1311 # For now, make these equal
1312 mmr_pass = adminpass
1314 url_list=filter(None,ol_mmr_urls.split(' '))
1315 if (len(url_list) == 1):
1316 url_list=filter(None,ol_mmr_urls.split(','))
1319 mmr_on_config = "MirrorMode On"
1320 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1322 for url in url_list:
1324 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1325 { "SERVERID" : str(serverid),
1326 "LDAPSERVER" : url })
1329 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1331 "MMRDN": names.schemadn,
1333 "MMR_PASSWORD": mmr_pass})
1336 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1338 "MMRDN": names.configdn,
1340 "MMR_PASSWORD": mmr_pass})
1343 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1345 "MMRDN": names.domaindn,
1347 "MMR_PASSWORD": mmr_pass })
1350 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1351 {"DNSDOMAIN": names.dnsdomain,
1352 "LDAPDIR": paths.ldapdir,
1353 "DOMAINDN": names.domaindn,
1354 "CONFIGDN": names.configdn,
1355 "SCHEMADN": names.schemadn,
1356 "MEMBEROF_CONFIG": memberof_config,
1357 "MIRRORMODE": mmr_on_config,
1358 "REPLICATOR_ACL": mmr_replicator_acl,
1359 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1360 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1361 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1362 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1363 "REFINT_CONFIG": refint_config})
1364 setup_file(setup_path("modules.conf"), paths.modulesconf,
1365 {"REALM": names.realm})
1367 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1368 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1369 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1371 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1372 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1374 setup_file(setup_path("cn=samba.ldif"),
1375 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1376 { "UUID": str(uuid.uuid4()),
1377 "LDAPTIME": timestring(int(time.time()))} )
1378 setup_file(setup_path("cn=samba-admin.ldif"),
1379 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1380 {"LDAPADMINPASS_B64": b64encode(adminpass),
1381 "UUID": str(uuid.uuid4()),
1382 "LDAPTIME": timestring(int(time.time()))} )
1384 if ol_mmr_urls is not None:
1385 setup_file(setup_path("cn=replicator.ldif"),
1386 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1387 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1388 "UUID": str(uuid.uuid4()),
1389 "LDAPTIME": timestring(int(time.time()))} )
1393 mapping = "schema-map-openldap-2.3"
1394 backend_schema = "backend-schema.schema"
1396 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1397 if ldap_backend_port is not None:
1398 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1400 server_port_string = ""
1402 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1404 ldapuser = "--username=samba-admin"
1407 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)
1409 os.system(schema_command)
1411 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1412 message("Server Role: %s" % serverrole)
1413 message("Hostname: %s" % names.hostname)
1414 message("DNS Domain: %s" % names.dnsdomain)
1415 message("Base DN: %s" % names.domaindn)
1417 if ldap_backend_type == "openldap":
1418 message("LDAP admin user: samba-admin")
1420 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1422 message("LDAP admin password: %s" % adminpass)
1423 message(slapdcommand)
1424 assert isinstance(ldap_backend_type, str)
1425 assert isinstance(ldapuser, str)
1426 assert isinstance(adminpass, str)
1427 assert isinstance(names.dnsdomain, str)
1428 assert isinstance(names.domain, str)
1429 assert isinstance(serverrole, str)
1430 args = ["--ldap-backend=ldapi",
1431 "--ldap-backend-type=" + ldap_backend_type,
1432 "--password=" + adminpass,
1434 "--realm=" + names.dnsdomain,
1435 "--domain=" + names.domain,
1436 "--server-role='" + serverrole + "'"]
1437 message("Run provision with: " + " ".join(args))
1440 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1441 """Create a PHP LDAP admin configuration file.
1443 :param path: Path to write the configuration to.
1444 :param setup_path: Function to generate setup paths.
1446 setup_file(setup_path("phpldapadmin-config.php"), path,
1447 {"S4_LDAPI_URI": ldapi_uri})
1450 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1451 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1452 """Write out a DNS zone file, from the info in the current database.
1454 :param path: Path of the new zone file.
1455 :param setup_path: Setup path function.
1456 :param dnsdomain: DNS Domain name
1457 :param domaindn: DN of the Domain
1458 :param hostip: Local IPv4 IP
1459 :param hostip6: Local IPv6 IP
1460 :param hostname: Local hostname
1461 :param dnspass: Password for DNS
1462 :param realm: Realm name
1463 :param domainguid: GUID of the domain.
1464 :param hostguid: GUID of the host.
1466 assert isinstance(domainguid, str)
1468 if hostip6 is not None:
1469 hostip6_base_line = " IN AAAA " + hostip6
1470 hostip6_host_line = hostname + " IN AAAA " + hostip6
1472 hostip6_base_line = ""
1473 hostip6_host_line = ""
1475 if hostip is not None:
1476 hostip_base_line = " IN A " + hostip
1477 hostip_host_line = hostname + " IN A " + hostip
1479 hostip_base_line = ""
1480 hostip_host_line = ""
1482 setup_file(setup_path("provision.zone"), path, {
1483 "DNSPASS_B64": b64encode(dnspass),
1484 "HOSTNAME": hostname,
1485 "DNSDOMAIN": dnsdomain,
1487 "HOSTIP_BASE_LINE": hostip_base_line,
1488 "HOSTIP_HOST_LINE": hostip_host_line,
1489 "DOMAINGUID": domainguid,
1490 "DATESTRING": time.strftime("%Y%m%d%H"),
1491 "DEFAULTSITE": DEFAULTSITE,
1492 "HOSTGUID": hostguid,
1493 "HOSTIP6_BASE_LINE": hostip6_base_line,
1494 "HOSTIP6_HOST_LINE": hostip6_host_line,
1498 def create_named_conf(path, setup_path, realm, dnsdomain,
1500 """Write out a file containing zone statements suitable for inclusion in a
1501 named.conf file (including GSS-TSIG configuration).
1503 :param path: Path of the new named.conf file.
1504 :param setup_path: Setup path function.
1505 :param realm: Realm name
1506 :param dnsdomain: DNS Domain name
1507 :param private_dir: Path to private directory
1508 :param keytab_name: File name of DNS keytab file
1511 setup_file(setup_path("named.conf"), path, {
1512 "DNSDOMAIN": dnsdomain,
1514 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1515 "PRIVATE_DIR": private_dir
1518 def create_named_txt(path, setup_path, realm, dnsdomain,
1519 private_dir, keytab_name):
1520 """Write out a file containing zone statements suitable for inclusion in a
1521 named.conf file (including GSS-TSIG configuration).
1523 :param path: Path of the new named.conf file.
1524 :param setup_path: Setup path function.
1525 :param realm: Realm name
1526 :param dnsdomain: DNS Domain name
1527 :param private_dir: Path to private directory
1528 :param keytab_name: File name of DNS keytab file
1531 setup_file(setup_path("named.txt"), path, {
1532 "DNSDOMAIN": dnsdomain,
1534 "DNS_KEYTAB": keytab_name,
1535 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1536 "PRIVATE_DIR": private_dir
1539 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1540 """Write out a file containing zone statements suitable for inclusion in a
1541 named.conf file (including GSS-TSIG configuration).
1543 :param path: Path of the new named.conf file.
1544 :param setup_path: Setup path function.
1545 :param dnsdomain: DNS Domain name
1546 :param hostname: Local hostname
1547 :param realm: Realm name
1550 setup_file(setup_path("krb5.conf"), path, {
1551 "DNSDOMAIN": dnsdomain,
1552 "HOSTNAME": hostname,
1557 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1558 serverdn, servername):
1559 """Load schema for the SamDB.
1561 :param samdb: Load a schema into a SamDB.
1562 :param setup_path: Setup path function.
1563 :param schemadn: DN of the schema
1564 :param netbiosname: NetBIOS name of the host.
1565 :param configdn: DN of the configuration
1566 :param serverdn: DN of the server
1567 :param servername: Host name of the server
1569 schema_data = open(setup_path("schema.ldif"), 'r').read()
1570 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1571 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1572 check_all_substituted(schema_data)
1573 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1574 prefixmap = b64encode(prefixmap)
1576 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1577 head_data = substitute_var(head_data, {
1578 "SCHEMADN": schemadn,
1579 "NETBIOSNAME": netbiosname,
1580 "CONFIGDN": configdn,
1581 "DEFAULTSITE": sitename,
1582 "PREFIXMAP_B64": prefixmap,
1583 "SERVERDN": serverdn,
1584 "SERVERNAME": servername,
1586 check_all_substituted(head_data)
1587 samdb.attach_schema_from_ldif(head_data, schema_data)