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
43 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
44 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
46 __docformat__ = "restructuredText"
48 DEFAULTSITE = "Default-First-Site-Name"
50 class InvalidNetbiosName(Exception):
51 """A specified name was not a valid NetBIOS name."""
52 def __init__(self, name):
53 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
69 self.dns_keytab = None
72 self.private_dir = None
75 self.modulesconf = None
76 self.memberofconf = None
77 self.fedoradsinf = None
78 self.fedoradspartitions = None
86 self.ldapmanagerdn = None
89 self.netbiosname = None
95 class ProvisionResult:
102 def check_install(lp, session_info, credentials):
103 """Check whether the current install seems ok.
105 :param lp: Loadparm context
106 :param session_info: Session information
107 :param credentials: Credentials
109 if lp.get("realm") == "":
110 raise Exception("Realm empty")
111 ldb = Ldb(lp.get("sam database"), session_info=session_info,
112 credentials=credentials, lp=lp)
113 if len(ldb.search("(cn=Administrator)")) != 1:
114 raise "No administrator account found"
117 def findnss(nssfn, names):
118 """Find a user or group from a list of possibilities.
120 :param nssfn: NSS Function to try (should raise KeyError if not found)
121 :param names: Names to check.
122 :return: Value return by first names list.
129 raise KeyError("Unable to find user/group %r" % names)
132 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
133 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
136 def open_ldb(session_info, credentials, lp, dbname):
137 """Open a LDB, thrashing it if it is corrupt.
139 :param session_info: auth session information
140 :param credentials: credentials
141 :param lp: Loadparm context
142 :param dbname: Path of the database to open.
143 :return: a Ldb object
145 assert session_info is not None
147 return Ldb(dbname, session_info=session_info, credentials=credentials,
152 return Ldb(dbname, session_info=session_info, credentials=credentials,
156 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
157 """Setup a ldb in the private dir.
159 :param ldb: LDB file to import data into
160 :param ldif_path: Path of the LDIF file to load
161 :param subst_vars: Optional variables to subsitute in LDIF.
163 assert isinstance(ldif_path, str)
165 data = open(ldif_path, 'r').read()
166 if subst_vars is not None:
167 data = substitute_var(data, subst_vars)
169 check_all_substituted(data)
174 def setup_modify_ldif(ldb, ldif_path, substvars=None):
175 """Modify a ldb in the private dir.
177 :param ldb: LDB object.
178 :param ldif_path: LDIF file path.
179 :param substvars: Optional dictionary with substitution variables.
181 data = open(ldif_path, 'r').read()
182 if substvars is not None:
183 data = substitute_var(data, substvars)
185 check_all_substituted(data)
187 ldb.modify_ldif(data)
190 def setup_ldb(ldb, ldif_path, subst_vars):
191 """Import a LDIF a file into a LDB handle, optionally substituting variables.
193 :note: Either all LDIF data will be added or none (using transactions).
195 :param ldb: LDB file to import into.
196 :param ldif_path: Path to the LDIF file.
197 :param subst_vars: Dictionary with substitution variables.
199 assert ldb is not None
200 ldb.transaction_start()
202 setup_add_ldif(ldb, ldif_path, subst_vars)
204 ldb.transaction_cancel()
206 ldb.transaction_commit()
209 def setup_file(template, fname, substvars):
210 """Setup a file in the private dir.
212 :param template: Path of the template file.
213 :param fname: Path of the file to create.
214 :param substvars: Substitution variables.
218 if os.path.exists(f):
221 data = open(template, 'r').read()
223 data = substitute_var(data, substvars)
224 check_all_substituted(data)
226 open(f, 'w').write(data)
229 def provision_paths_from_lp(lp, dnsdomain):
230 """Set the default paths for provisioning.
232 :param lp: Loadparm context.
233 :param dnsdomain: DNS Domain name
235 paths = ProvisionPaths()
236 paths.private_dir = lp.get("private dir")
237 paths.keytab = "secrets.keytab"
238 paths.dns_keytab = "dns.keytab"
240 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
241 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
242 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
243 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
244 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
245 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
246 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
247 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
248 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
249 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
250 paths.phpldapadminconfig = os.path.join(paths.private_dir,
251 "phpldapadmin-config.php")
252 paths.ldapdir = os.path.join(paths.private_dir,
254 paths.slapdconf = os.path.join(paths.ldapdir,
256 paths.modulesconf = os.path.join(paths.ldapdir,
258 paths.memberofconf = os.path.join(paths.ldapdir,
260 paths.fedoradsinf = os.path.join(paths.ldapdir,
262 paths.fedoradspartitions = os.path.join(paths.ldapdir,
263 "fedorads-partitions.ldif")
264 paths.hklm = "hklm.ldb"
265 paths.hkcr = "hkcr.ldb"
266 paths.hkcu = "hkcu.ldb"
267 paths.hku = "hku.ldb"
268 paths.hkpd = "hkpd.ldb"
269 paths.hkpt = "hkpt.ldb"
271 paths.sysvol = lp.get("path", "sysvol")
273 paths.netlogon = lp.get("path", "netlogon")
275 paths.smbconf = lp.configfile()
280 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
281 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
283 """Guess configuration settings to use."""
286 hostname = socket.gethostname().split(".")[0].lower()
288 netbiosname = hostname.upper()
289 if not valid_netbios_name(netbiosname):
290 raise InvalidNetbiosName(netbiosname)
292 hostname = hostname.lower()
294 if dnsdomain is None:
295 dnsdomain = lp.get("realm")
297 if serverrole is None:
298 serverrole = lp.get("server role")
300 assert dnsdomain is not None
301 realm = dnsdomain.upper()
303 if lp.get("realm").upper() != realm:
304 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
305 (lp.get("realm"), lp.configfile(), realm))
307 dnsdomain = dnsdomain.lower()
309 if serverrole == "domain controller":
311 domain = lp.get("workgroup")
313 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
314 if lp.get("workgroup").upper() != domain.upper():
315 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
316 lp.get("workgroup"), domain)
320 domaindn = "CN=" + netbiosname
322 assert domain is not None
323 domain = domain.upper()
324 if not valid_netbios_name(domain):
325 raise InvalidNetbiosName(domain)
331 configdn = "CN=Configuration," + rootdn
333 schemadn = "CN=Schema," + configdn
338 names = ProvisionNames()
339 names.rootdn = rootdn
340 names.domaindn = domaindn
341 names.configdn = configdn
342 names.schemadn = schemadn
343 names.ldapmanagerdn = "CN=Manager," + rootdn
344 names.dnsdomain = dnsdomain
345 names.domain = domain
347 names.netbiosname = netbiosname
348 names.hostname = hostname
349 names.sitename = sitename
350 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
355 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
358 hostname = socket.gethostname().split(".")[0].lower()
360 if serverrole is None:
361 serverrole = "standalone"
363 assert serverrole in ("domain controller", "member server", "standalone")
364 if serverrole == "domain controller":
366 elif serverrole == "member server":
367 smbconfsuffix = "member"
368 elif serverrole == "standalone":
369 smbconfsuffix = "standalone"
371 assert domain is not None
372 assert realm is not None
374 default_lp = param.LoadParm()
375 #Load non-existant file
376 default_lp.load(smbconf)
378 if targetdir is not None:
379 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
380 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
382 default_lp.set("lock dir", os.path.abspath(targetdir))
387 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
388 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
390 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
392 "HOSTNAME": hostname,
395 "SERVERROLE": serverrole,
396 "NETLOGONPATH": netlogon,
397 "SYSVOLPATH": sysvol,
398 "PRIVATEDIR_LINE": privatedir_line,
399 "LOCKDIR_LINE": lockdir_line
404 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
405 users_gid, wheel_gid):
406 """setup reasonable name mappings for sam names to unix names.
408 :param samdb: SamDB object.
409 :param idmap: IDmap db object.
410 :param sid: The domain sid.
411 :param domaindn: The domain DN.
412 :param root_uid: uid of the UNIX root user.
413 :param nobody_uid: uid of the UNIX nobody user.
414 :param users_gid: gid of the UNIX users group.
415 :param wheel_gid: gid of the UNIX wheel group."""
416 # add some foreign sids if they are not present already
417 samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
418 samdb.add_foreign(domaindn, "S-1-1-0", "World")
419 samdb.add_foreign(domaindn, "S-1-5-2", "Network")
420 samdb.add_foreign(domaindn, "S-1-5-18", "System")
421 samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
423 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
424 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
426 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
427 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
430 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
432 serverrole, ldap_backend=None,
433 ldap_backend_type=None, erase=False):
434 """Setup the partitions for the SAM database.
436 Alternatively, provision() may call this, and then populate the database.
438 :note: This will wipe the Sam Database!
440 :note: This function always removes the local SAM LDB file. The erase
441 parameter controls whether to erase the existing data, which
442 may not be stored locally but in LDAP.
444 assert session_info is not None
446 samdb = SamDB(samdb_path, session_info=session_info,
447 credentials=credentials, lp=lp)
453 os.unlink(samdb_path)
455 samdb = SamDB(samdb_path, session_info=session_info,
456 credentials=credentials, lp=lp)
458 #Add modules to the list to activate them by default
459 #beware often order is important
461 # Some Known ordering constraints:
462 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
463 # - objectclass must be before password_hash, because password_hash checks
464 # that the objectclass is of type person (filled in by objectclass
465 # module when expanding the objectclass list)
466 # - partition must be last
467 # - each partition has its own module list then
468 modules_list = ["rootdse",
484 modules_list2 = ["show_deleted",
487 domaindn_ldb = "users.ldb"
488 if ldap_backend is not None:
489 domaindn_ldb = ldap_backend
490 configdn_ldb = "configuration.ldb"
491 if ldap_backend is not None:
492 configdn_ldb = ldap_backend
493 schemadn_ldb = "schema.ldb"
494 if ldap_backend is not None:
495 schema_ldb = ldap_backend
496 schemadn_ldb = ldap_backend
498 if ldap_backend_type == "fedora-ds":
499 backend_modules = ["nsuniqueid", "paged_searches"]
500 # We can handle linked attributes here, as we don't have directory-side subtree operations
501 tdb_modules_list = ["linked_attributes"]
502 elif ldap_backend_type == "openldap":
503 backend_modules = ["normalise", "entryuuid", "paged_searches"]
504 # OpenLDAP handles subtree renames, so we don't want to do any of these things
505 tdb_modules_list = None
506 elif serverrole == "domain controller":
507 backend_modules = ["repl_meta_data"]
509 backend_modules = ["objectguid"]
511 if tdb_modules_list is None:
512 tdb_modules_list_as_string = ""
514 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
516 samdb.transaction_start()
518 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
519 "SCHEMADN": names.schemadn,
520 "SCHEMADN_LDB": schemadn_ldb,
521 "SCHEMADN_MOD2": ",objectguid",
522 "CONFIGDN": names.configdn,
523 "CONFIGDN_LDB": configdn_ldb,
524 "DOMAINDN": names.domaindn,
525 "DOMAINDN_LDB": domaindn_ldb,
526 "SCHEMADN_MOD": "schema_fsmo,instancetype",
527 "CONFIGDN_MOD": "naming_fsmo,instancetype",
528 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
529 "MODULES_LIST": ",".join(modules_list),
530 "TDB_MODULES_LIST": tdb_modules_list_as_string,
531 "MODULES_LIST2": ",".join(modules_list2),
532 "BACKEND_MOD": ",".join(backend_modules),
536 samdb.transaction_cancel()
539 samdb.transaction_commit()
541 samdb = SamDB(samdb_path, session_info=session_info,
542 credentials=credentials, lp=lp)
544 samdb.transaction_start()
546 message("Setting up sam.ldb attributes")
547 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
549 message("Setting up sam.ldb rootDSE")
550 setup_samdb_rootdse(samdb, setup_path, names)
553 message("Erasing data from partitions")
554 samdb.erase_partitions()
557 samdb.transaction_cancel()
560 samdb.transaction_commit()
565 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
566 netbiosname, domainsid, keytab_path, samdb_url,
567 dns_keytab_path, dnspass, machinepass):
568 """Add DC-specific bits to a secrets database.
570 :param secretsdb: Ldb Handle to the secrets database
571 :param setup_path: Setup path function
572 :param machinepass: Machine password
574 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
575 "MACHINEPASS_B64": b64encode(machinepass),
578 "DNSDOMAIN": dnsdomain,
579 "DOMAINSID": str(domainsid),
580 "SECRETS_KEYTAB": keytab_path,
581 "NETBIOSNAME": netbiosname,
582 "SAM_LDB": samdb_url,
583 "DNS_KEYTAB": dns_keytab_path,
584 "DNSPASS_B64": b64encode(dnspass),
588 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
589 """Setup the secrets database.
591 :param path: Path to the secrets database.
592 :param setup_path: Get the path to a setup file.
593 :param session_info: Session info.
594 :param credentials: Credentials
595 :param lp: Loadparm context
596 :return: LDB handle for the created secrets database
598 if os.path.exists(path):
600 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
603 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
604 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
606 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
610 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
611 """Setup the templates database.
613 :param path: Path to the database.
614 :param setup_path: Function for obtaining the path to setup files.
615 :param session_info: Session info
616 :param credentials: Credentials
617 :param lp: Loadparm context
619 templates_ldb = SamDB(path, session_info=session_info,
620 credentials=credentials, lp=lp)
621 templates_ldb.erase()
622 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
625 def setup_registry(path, setup_path, session_info, credentials, lp):
626 """Setup the registry.
628 :param path: Path to the registry database
629 :param setup_path: Function that returns the path to a setup.
630 :param session_info: Session information
631 :param credentials: Credentials
632 :param lp: Loadparm context
634 reg = registry.Registry()
635 hive = registry.open_ldb(path, session_info=session_info,
636 credentials=credentials, lp_ctx=lp)
637 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
638 provision_reg = setup_path("provision.reg")
639 assert os.path.exists(provision_reg)
640 reg.diff_apply(provision_reg)
643 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
644 """Setup the idmap database.
646 :param path: path to the idmap database
647 :param setup_path: Function that returns a path to a setup file
648 :param session_info: Session information
649 :param credentials: Credentials
650 :param lp: Loadparm context
652 if os.path.exists(path):
655 idmap_ldb = IDmapDB(path, session_info=session_info,
656 credentials=credentials, lp=lp)
659 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
663 def setup_samdb_rootdse(samdb, setup_path, names):
664 """Setup the SamDB rootdse.
666 :param samdb: Sam Database handle
667 :param setup_path: Obtain setup path
669 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
670 "SCHEMADN": names.schemadn,
671 "NETBIOSNAME": names.netbiosname,
672 "DNSDOMAIN": names.dnsdomain,
673 "REALM": names.realm,
674 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
675 "DOMAINDN": names.domaindn,
676 "ROOTDN": names.rootdn,
677 "CONFIGDN": names.configdn,
678 "SERVERDN": names.serverdn,
682 def setup_self_join(samdb, names,
683 machinepass, dnspass,
684 domainsid, invocationid, setup_path,
686 """Join a host to its own domain."""
687 assert isinstance(invocationid, str)
688 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
689 "CONFIGDN": names.configdn,
690 "SCHEMADN": names.schemadn,
691 "DOMAINDN": names.domaindn,
692 "SERVERDN": names.serverdn,
693 "INVOCATIONID": invocationid,
694 "NETBIOSNAME": names.netbiosname,
695 "DEFAULTSITE": names.sitename,
696 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
697 "MACHINEPASS_B64": b64encode(machinepass),
698 "DNSPASS_B64": b64encode(dnspass),
699 "REALM": names.realm,
700 "DOMAIN": names.domain,
701 "DNSDOMAIN": names.dnsdomain})
702 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
703 "POLICYGUID": policyguid,
704 "DNSDOMAIN": names.dnsdomain,
705 "DOMAINSID": str(domainsid),
706 "DOMAINDN": names.domaindn})
709 def setup_samdb(path, setup_path, session_info, credentials, lp,
711 domainsid, aci, domainguid, policyguid,
712 fill, adminpass, krbtgtpass,
713 machinepass, invocationid, dnspass,
714 serverrole, ldap_backend=None,
715 ldap_backend_type=None):
716 """Setup a complete SAM Database.
718 :note: This will wipe the main SAM database file!
721 erase = (fill != FILL_DRS)
723 # Also wipes the database
724 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
725 credentials=credentials, session_info=session_info,
727 ldap_backend=ldap_backend, serverrole=serverrole,
728 ldap_backend_type=ldap_backend_type, erase=erase)
730 samdb = SamDB(path, session_info=session_info,
731 credentials=credentials, lp=lp)
734 # We want to finish here, but setup the index before we do so
735 message("Setting up sam.ldb index")
736 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
739 message("Pre-loading the Samba 4 and AD schema")
740 samdb.set_domain_sid(domainsid)
741 if serverrole == "domain controller":
742 samdb.set_invocation_id(invocationid)
744 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
745 names.configdn, names.sitename)
747 samdb.transaction_start()
750 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
751 if serverrole == "domain controller":
752 domain_oc = "domainDNS"
754 domain_oc = "samba4LocalDomain"
756 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
757 "DOMAINDN": names.domaindn,
759 "DOMAIN_OC": domain_oc
762 message("Modifying DomainDN: " + names.domaindn + "")
763 if domainguid is not None:
764 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
768 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
769 "LDAPTIME": timestring(int(time.time())),
770 "DOMAINSID": str(domainsid),
771 "SCHEMADN": names.schemadn,
772 "NETBIOSNAME": names.netbiosname,
773 "DEFAULTSITE": names.sitename,
774 "CONFIGDN": names.configdn,
775 "SERVERDN": names.serverdn,
776 "POLICYGUID": policyguid,
777 "DOMAINDN": names.domaindn,
778 "DOMAINGUID_MOD": domainguid_mod,
781 message("Adding configuration container (permitted to fail)")
782 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
783 "CONFIGDN": names.configdn,
785 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
787 message("Modifying configuration container")
788 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
789 "CONFIGDN": names.configdn,
790 "SCHEMADN": names.schemadn,
793 message("Adding schema container (permitted to fail)")
794 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
795 "SCHEMADN": names.schemadn,
797 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
799 message("Modifying schema container")
801 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
803 setup_modify_ldif(samdb,
804 setup_path("provision_schema_basedn_modify.ldif"), {
805 "SCHEMADN": names.schemadn,
806 "NETBIOSNAME": names.netbiosname,
807 "DEFAULTSITE": names.sitename,
808 "CONFIGDN": names.configdn,
809 "SERVERDN": names.serverdn,
810 "PREFIXMAP_B64": b64encode(prefixmap)
813 message("Setting up sam.ldb Samba4 schema")
814 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
815 {"SCHEMADN": names.schemadn })
816 message("Setting up sam.ldb AD schema")
817 setup_add_ldif(samdb, setup_path("schema.ldif"),
818 {"SCHEMADN": names.schemadn})
820 message("Setting up sam.ldb configuration data")
821 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
822 "CONFIGDN": names.configdn,
823 "NETBIOSNAME": names.netbiosname,
824 "DEFAULTSITE": names.sitename,
825 "DNSDOMAIN": names.dnsdomain,
826 "DOMAIN": names.domain,
827 "SCHEMADN": names.schemadn,
828 "DOMAINDN": names.domaindn,
829 "SERVERDN": names.serverdn
832 message("Setting up display specifiers")
833 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
834 {"CONFIGDN": names.configdn})
836 message("Adding users container (permitted to fail)")
837 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
838 "DOMAINDN": names.domaindn})
839 message("Modifying users container")
840 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
841 "DOMAINDN": names.domaindn})
842 message("Adding computers container (permitted to fail)")
843 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
844 "DOMAINDN": names.domaindn})
845 message("Modifying computers container")
846 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
847 "DOMAINDN": names.domaindn})
848 message("Setting up sam.ldb data")
849 setup_add_ldif(samdb, setup_path("provision.ldif"), {
850 "DOMAINDN": names.domaindn,
851 "NETBIOSNAME": names.netbiosname,
852 "DEFAULTSITE": names.sitename,
853 "CONFIGDN": names.configdn,
854 "SERVERDN": names.serverdn
857 if fill == FILL_FULL:
858 message("Setting up sam.ldb users and groups")
859 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
860 "DOMAINDN": names.domaindn,
861 "DOMAINSID": str(domainsid),
862 "CONFIGDN": names.configdn,
863 "ADMINPASS_B64": b64encode(adminpass),
864 "KRBTGTPASS_B64": b64encode(krbtgtpass),
867 if serverrole == "domain controller":
868 message("Setting up self join")
869 setup_self_join(samdb, names=names, invocationid=invocationid,
871 machinepass=machinepass,
872 domainsid=domainsid, policyguid=policyguid,
873 setup_path=setup_path)
875 #We want to setup the index last, as adds are faster unindexed
876 message("Setting up sam.ldb index")
877 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
879 samdb.transaction_cancel()
882 samdb.transaction_commit()
887 FILL_NT4SYNC = "NT4SYNC"
890 def provision(setup_dir, message, session_info,
891 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
892 rootdn=None, domaindn=None, schemadn=None, configdn=None,
894 domain=None, hostname=None, hostip=None, hostip6=None,
895 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
896 policyguid=None, invocationid=None, machinepass=None,
897 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
898 wheel=None, backup=None, aci=None, serverrole=None,
899 ldap_backend=None, ldap_backend_type=None, sitename=None):
902 :note: caution, this wipes all existing data!
905 def setup_path(file):
906 return os.path.join(setup_dir, file)
908 if domainsid is None:
909 domainsid = security.random_sid()
911 domainsid = security.Sid(domainsid)
913 if policyguid is None:
914 policyguid = str(uuid.uuid4())
915 if adminpass is None:
916 adminpass = misc.random_password(12)
917 if krbtgtpass is None:
918 krbtgtpass = misc.random_password(12)
919 if machinepass is None:
920 machinepass = misc.random_password(12)
922 dnspass = misc.random_password(12)
923 root_uid = findnss_uid([root or "root"])
924 nobody_uid = findnss_uid([nobody or "nobody"])
925 users_gid = findnss_gid([users or "users"])
927 wheel_gid = findnss_gid(["wheel", "adm"])
929 wheel_gid = findnss_gid([wheel])
931 aci = "# no aci for local ldb"
933 if targetdir is not None:
934 if (not os.path.exists(os.path.join(targetdir, "etc"))):
935 os.makedirs(os.path.join(targetdir, "etc"))
936 smbconf = os.path.join(targetdir, "etc", "smb.conf")
938 # only install a new smb.conf if there isn't one there already
939 if not os.path.exists(smbconf):
940 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
943 lp = param.LoadParm()
946 names = guess_names(lp=lp, hostname=hostname, domain=domain,
947 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
948 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
951 paths = provision_paths_from_lp(lp, names.dnsdomain)
954 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
958 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
959 except socket.gaierror:
962 if serverrole is None:
963 serverrole = lp.get("server role")
965 assert serverrole in ("domain controller", "member server", "standalone")
966 if invocationid is None and serverrole == "domain controller":
967 invocationid = str(uuid.uuid4())
969 if not os.path.exists(paths.private_dir):
970 os.mkdir(paths.private_dir)
972 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
974 if ldap_backend is not None:
975 if ldap_backend == "ldapi":
976 # provision-backend will set this path suggested slapd command line / fedorads.inf
977 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
979 # only install a new shares config db if there is none
980 if not os.path.exists(paths.shareconf):
981 message("Setting up share.ldb")
982 share_ldb = Ldb(paths.shareconf, session_info=session_info,
983 credentials=credentials, lp=lp)
984 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
987 message("Setting up secrets.ldb")
988 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
989 session_info=session_info,
990 credentials=credentials, lp=lp)
992 message("Setting up the registry")
993 setup_registry(paths.hklm, setup_path, session_info,
994 credentials=credentials, lp=lp)
996 message("Setting up templates db")
997 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
998 credentials=credentials, lp=lp)
1000 message("Setting up idmap db")
1001 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1002 credentials=credentials, lp=lp)
1004 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1005 credentials=credentials, lp=lp, names=names,
1007 domainsid=domainsid,
1008 aci=aci, domainguid=domainguid, policyguid=policyguid,
1010 adminpass=adminpass, krbtgtpass=krbtgtpass,
1011 invocationid=invocationid,
1012 machinepass=machinepass, dnspass=dnspass,
1013 serverrole=serverrole, ldap_backend=ldap_backend,
1014 ldap_backend_type=ldap_backend_type)
1016 if lp.get("server role") == "domain controller":
1017 if paths.netlogon is None:
1018 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1019 message("Please either remove %s or see the template at %s" %
1020 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1021 assert(paths.netlogon is not None)
1023 if paths.sysvol is None:
1024 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1025 message("Please either remove %s or see the template at %s" %
1026 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1027 assert(paths.sysvol is not None)
1029 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1030 "{" + policyguid + "}")
1031 os.makedirs(policy_path, 0755)
1032 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1033 os.makedirs(os.path.join(policy_path, "User"), 0755)
1034 if not os.path.isdir(paths.netlogon):
1035 os.makedirs(paths.netlogon, 0755)
1037 if samdb_fill == FILL_FULL:
1038 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1039 root_uid=root_uid, nobody_uid=nobody_uid,
1040 users_gid=users_gid, wheel_gid=wheel_gid)
1042 message("Setting up sam.ldb rootDSE marking as synchronized")
1043 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1045 # Only make a zone file on the first DC, it should be replicated with DNS replication
1046 if serverrole == "domain controller":
1047 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1048 credentials=credentials, lp=lp)
1049 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1050 netbiosname=names.netbiosname, domainsid=domainsid,
1051 keytab_path=paths.keytab, samdb_url=paths.samdb,
1052 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1053 machinepass=machinepass, dnsdomain=names.dnsdomain)
1055 samdb = SamDB(paths.samdb, session_info=session_info,
1056 credentials=credentials, lp=lp)
1058 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1059 assert isinstance(domainguid, str)
1060 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1061 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1062 scope=SCOPE_SUBTREE)
1063 assert isinstance(hostguid, str)
1065 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1066 domaindn=names.domaindn, hostip=hostip,
1067 hostip6=hostip6, hostname=names.hostname,
1068 dnspass=dnspass, realm=names.realm,
1069 domainguid=domainguid, hostguid=hostguid)
1070 message("Please install the zone located in %s into your DNS server" % paths.dns)
1072 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1073 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1074 keytab_name=paths.dns_keytab)
1075 message("See %s for example configuration statements for secure GSS-TSIG updates" % paths.namedconf)
1077 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1078 hostname=names.hostname, realm=names.realm)
1079 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1081 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1084 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1086 message("Once the above files are installed, your Samba4 server will be ready to use")
1087 message("Server Role: %s" % serverrole)
1088 message("Hostname: %s" % names.hostname)
1089 message("NetBIOS Domain: %s" % names.domain)
1090 message("DNS Domain: %s" % names.dnsdomain)
1091 message("DOMAIN SID: %s" % str(domainsid))
1092 message("Admin password: %s" % adminpass)
1094 result = ProvisionResult()
1095 result.domaindn = domaindn
1096 result.paths = paths
1098 result.samdb = samdb
1102 def provision_become_dc(setup_dir=None,
1103 smbconf=None, targetdir=None, realm=None,
1104 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1106 domain=None, hostname=None, domainsid=None,
1107 adminpass=None, krbtgtpass=None, domainguid=None,
1108 policyguid=None, invocationid=None, machinepass=None,
1109 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1110 wheel=None, backup=None, aci=None, serverrole=None,
1111 ldap_backend=None, ldap_backend_type=None, sitename=None):
1114 """print a message if quiet is not set."""
1117 return provision(setup_dir, message, system_session(), None,
1118 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1119 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1120 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1123 def setup_db_config(setup_path, dbdir):
1124 """Setup a Berkeley database.
1126 :param setup_path: Setup path function.
1127 :param dbdir: Database directory."""
1128 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1129 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1130 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1131 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1133 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1134 {"LDAPDBDIR": dbdir})
1138 def provision_backend(setup_dir=None, message=None,
1139 smbconf=None, targetdir=None, realm=None,
1140 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1141 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1142 ldap_backend_type=None, ldap_backend_port=None):
1144 def setup_path(file):
1145 return os.path.join(setup_dir, file)
1147 if hostname is None:
1148 hostname = socket.gethostname().split(".")[0].lower()
1151 root = findnss(pwd.getpwnam, ["root"])[0]
1154 etcdir = os.path.join(targetdir, "etc")
1156 smbconf = os.path.join(etcdir, "smb.conf")
1158 # only install a new smb.conf if there isn't one there already
1159 if not os.path.exists(smbconf):
1160 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1161 serverrole, targetdir)
1163 lp = param.LoadParm()
1166 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1167 dnsdomain=realm, serverrole=serverrole,
1168 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1171 paths = provision_paths_from_lp(lp, names.dnsdomain)
1173 if not os.path.isdir(paths.ldapdir):
1174 os.makedirs(paths.ldapdir)
1175 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1177 os.unlink(schemadb_path)
1181 schemadb = Ldb(schemadb_path, lp=lp)
1183 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1185 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1186 {"SCHEMADN": names.schemadn,
1188 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1190 setup_modify_ldif(schemadb,
1191 setup_path("provision_schema_basedn_modify.ldif"), \
1192 {"SCHEMADN": names.schemadn,
1193 "NETBIOSNAME": names.netbiosname,
1194 "DEFAULTSITE": DEFAULTSITE,
1195 "CONFIGDN": names.configdn,
1196 "SERVERDN": names.serverdn,
1197 "PREFIXMAP_B64": b64encode(prefixmap)
1200 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1201 {"SCHEMADN": names.schemadn })
1202 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1203 {"SCHEMADN": names.schemadn})
1205 if ldap_backend_type == "fedora-ds":
1206 if ldap_backend_port is not None:
1207 serverport = "ServerPort=%d" % ldap_backend_port
1211 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1213 "HOSTNAME": hostname,
1214 "DNSDOMAIN": names.dnsdomain,
1215 "LDAPDIR": paths.ldapdir,
1216 "DOMAINDN": names.domaindn,
1217 "LDAPMANAGERDN": names.ldapmanagerdn,
1218 "LDAPMANAGERPASS": adminpass,
1219 "SERVERPORT": serverport})
1221 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1222 {"CONFIGDN": names.configdn,
1223 "SCHEMADN": names.schemadn,
1226 mapping = "schema-map-fedora-ds-1.0"
1227 backend_schema = "99_ad.ldif"
1229 slapdcommand="Initailise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1231 elif ldap_backend_type == "openldap":
1232 attrs = ["linkID", "lDAPDisplayName"]
1233 res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
1235 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1236 refint_attributes = ""
1237 for i in range (0, len(res)):
1238 expression = "(&(objectclass=attributeSchema)(linkID=%d))" % (int(res[i]["linkID"][0])+1)
1239 target = schemadb.searchone(basedn=names.schemadn,
1240 expression=expression,
1241 attribute="lDAPDisplayName",
1242 scope=SCOPE_SUBTREE)
1243 if target is not None:
1244 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1245 memberof_config += """overlay memberof
1246 memberof-dangling error
1247 memberof-refint TRUE
1248 memberof-group-oc top
1249 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1250 memberof-memberof-ad """ + target + """
1251 memberof-dangling-error 32
1255 memberof_config += """
1257 refint_attributes""" + refint_attributes + "\n"
1259 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1260 {"DNSDOMAIN": names.dnsdomain,
1261 "LDAPDIR": paths.ldapdir,
1262 "DOMAINDN": names.domaindn,
1263 "CONFIGDN": names.configdn,
1264 "SCHEMADN": names.schemadn,
1265 "LDAPMANAGERDN": names.ldapmanagerdn,
1266 "LDAPMANAGERPASS": adminpass,
1267 "MEMBEROF_CONFIG": memberof_config})
1268 setup_file(setup_path("modules.conf"), paths.modulesconf,
1269 {"REALM": names.realm})
1271 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "user")))
1272 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "config")))
1273 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "schema")))
1274 mapping = "schema-map-openldap-2.3"
1275 backend_schema = "backend-schema.schema"
1277 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1278 if ldap_backend_port is not None:
1279 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1281 server_port_string = ""
1282 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1284 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)
1286 os.system(schema_command)
1289 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1290 message("Server Role: %s" % serverrole)
1291 message("Hostname: %s" % names.hostname)
1292 message("DNS Domain: %s" % names.dnsdomain)
1293 message("Base DN: %s" % names.domaindn)
1294 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1295 message("LDAP admin password: %s" % adminpass)
1296 message(slapdcommand)
1299 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1300 """Create a PHP LDAP admin configuration file.
1302 :param path: Path to write the configuration to.
1303 :param setup_path: Function to generate setup paths.
1305 setup_file(setup_path("phpldapadmin-config.php"), path,
1306 {"S4_LDAPI_URI": ldapi_uri})
1309 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1310 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1311 """Write out a DNS zone file, from the info in the current database.
1313 :param path: Path of the new zone file.
1314 :param setup_path: Setup path function.
1315 :param dnsdomain: DNS Domain name
1316 :param domaindn: DN of the Domain
1317 :param hostip: Local IPv4 IP
1318 :param hostip6: Local IPv6 IP
1319 :param hostname: Local hostname
1320 :param dnspass: Password for DNS
1321 :param realm: Realm name
1322 :param domainguid: GUID of the domain.
1323 :param hostguid: GUID of the host.
1325 assert isinstance(domainguid, str)
1327 if hostip6 is not None:
1328 hostip6_base_line = " IN AAAA " + hostip6
1329 hostip6_host_line = hostname + " IN AAAA " + hostip6
1331 hostip6_base_line = ""
1332 hostip6_host_line = ""
1334 setup_file(setup_path("provision.zone"), path, {
1335 "DNSPASS_B64": b64encode(dnspass),
1336 "HOSTNAME": hostname,
1337 "DNSDOMAIN": dnsdomain,
1340 "DOMAINGUID": domainguid,
1341 "DATESTRING": time.strftime("%Y%m%d%H"),
1342 "DEFAULTSITE": DEFAULTSITE,
1343 "HOSTGUID": hostguid,
1344 "HOSTIP6_BASE_LINE": hostip6_base_line,
1345 "HOSTIP6_HOST_LINE": hostip6_host_line,
1349 def create_named_conf(path, setup_path, realm, dnsdomain,
1350 private_dir, keytab_name):
1351 """Write out a file containing zone statements suitable for inclusion in a
1352 named.conf file (including GSS-TSIG configuration).
1354 :param path: Path of the new named.conf file.
1355 :param setup_path: Setup path function.
1356 :param realm: Realm name
1357 :param dnsdomain: DNS Domain name
1358 :param private_dir: Path to private directory
1359 :param keytab_name: File name of DNS keytab file
1362 setup_file(setup_path("named.conf"), path, {
1363 "DNSDOMAIN": dnsdomain,
1365 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1366 "DNS_KEYTAB": keytab_name,
1367 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1370 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1371 """Write out a file containing zone statements suitable for inclusion in a
1372 named.conf file (including GSS-TSIG configuration).
1374 :param path: Path of the new named.conf file.
1375 :param setup_path: Setup path function.
1376 :param dnsdomain: DNS Domain name
1377 :param hostname: Local hostname
1378 :param realm: Realm name
1381 setup_file(setup_path("krb5.conf"), path, {
1382 "DNSDOMAIN": dnsdomain,
1383 "HOSTNAME": hostname,
1388 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1389 """Load schema for the SamDB.
1391 :param samdb: Load a schema into a SamDB.
1392 :param setup_path: Setup path function.
1393 :param schemadn: DN of the schema
1394 :param netbiosname: NetBIOS name of the host.
1395 :param configdn: DN of the configuration
1397 schema_data = open(setup_path("schema.ldif"), 'r').read()
1398 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1399 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1400 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1401 prefixmap = b64encode(prefixmap)
1403 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1404 head_data = substitute_var(head_data, {
1405 "SCHEMADN": schemadn,
1406 "NETBIOSNAME": netbiosname,
1407 "CONFIGDN": configdn,
1408 "DEFAULTSITE":sitename,
1409 "PREFIXMAP_B64":prefixmap
1411 samdb.attach_schema_from_ldif(head_data, schema_data)