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 from base64 import b64encode
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
38 from samba.idmap import IDmapDB
41 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
42 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
44 """Functions for setting up a Samba configuration."""
46 DEFAULTSITE = "Default-First-Site-Name"
48 class InvalidNetbiosName(Exception):
49 def __init__(self, name):
50 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
66 self.dns_keytab = None
69 self.private_dir = None
72 self.modulesconf = None
73 self.memberofconf = None
74 self.fedoradsinf = None
75 self.fedoradspartitions = None
83 self.ldapmanagerdn = None
86 self.netbiosname = None
92 class ProvisionResult:
99 def check_install(lp, session_info, credentials):
100 """Check whether the current install seems ok.
102 :param lp: Loadparm context
103 :param session_info: Session information
104 :param credentials: Credentials
106 if lp.get("realm") == "":
107 raise Error("Realm empty")
108 ldb = Ldb(lp.get("sam database"), session_info=session_info,
109 credentials=credentials, lp=lp)
110 if len(ldb.search("(cn=Administrator)")) != 1:
111 raise "No administrator account found"
114 def findnss(nssfn, names):
115 """Find a user or group from a list of possibilities.
117 :param nssfn: NSS Function to try (should raise KeyError if not found)
118 :param names: Names to check.
119 :return: Value return by first names list.
126 raise KeyError("Unable to find user/group %r" % names)
129 def open_ldb(session_info, credentials, lp, dbname):
130 """Open a LDB, thrashing it if it is corrupt.
132 :param session_info: auth session information
133 :param credentials: credentials
134 :param lp: Loadparm context
135 :param dbname: Path of the database to open.
136 :return: a Ldb object
138 assert session_info is not None
140 return Ldb(dbname, session_info=session_info, credentials=credentials,
145 return Ldb(dbname, session_info=session_info, credentials=credentials,
149 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
150 """Setup a ldb in the private dir.
152 :param ldb: LDB file to import data into
153 :param ldif_path: Path of the LDIF file to load
154 :param subst_vars: Optional variables to subsitute in LDIF.
156 assert isinstance(ldif_path, str)
158 data = open(ldif_path, 'r').read()
159 if subst_vars is not None:
160 data = substitute_var(data, subst_vars)
162 check_all_substituted(data)
167 def setup_modify_ldif(ldb, ldif_path, substvars=None):
168 """Modify a ldb in the private dir.
170 :param ldb: LDB object.
171 :param ldif_path: LDIF file path.
172 :param substvars: Optional dictionary with substitution variables.
174 data = open(ldif_path, 'r').read()
175 if substvars is not None:
176 data = substitute_var(data, substvars)
178 check_all_substituted(data)
180 ldb.modify_ldif(data)
183 def setup_ldb(ldb, ldif_path, subst_vars):
184 """Import a LDIF a file into a LDB handle, optionally substituting variables.
186 :note: Either all LDIF data will be added or none (using transactions).
188 :param ldb: LDB file to import into.
189 :param ldif_path: Path to the LDIF file.
190 :param subst_vars: Dictionary with substitution variables.
192 assert ldb is not None
193 ldb.transaction_start()
195 setup_add_ldif(ldb, ldif_path, subst_vars)
197 ldb.transaction_cancel()
199 ldb.transaction_commit()
202 def setup_file(template, fname, substvars):
203 """Setup a file in the private dir.
205 :param template: Path of the template file.
206 :param fname: Path of the file to create.
207 :param substvars: Substitution variables.
211 if os.path.exists(f):
214 data = open(template, 'r').read()
216 data = substitute_var(data, substvars)
217 check_all_substituted(data)
219 open(f, 'w').write(data)
222 def provision_paths_from_lp(lp, dnsdomain):
223 """Set the default paths for provisioning.
225 :param lp: Loadparm context.
226 :param dnsdomain: DNS Domain name
228 paths = ProvisionPaths()
229 paths.private_dir = lp.get("private dir")
230 paths.keytab = "secrets.keytab"
231 paths.dns_keytab = "dns.keytab"
233 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
234 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
235 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
236 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
237 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
238 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
239 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
240 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
241 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
242 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
243 paths.phpldapadminconfig = os.path.join(paths.private_dir,
244 "phpldapadmin-config.php")
245 paths.ldapdir = os.path.join(paths.private_dir,
247 paths.slapdconf = os.path.join(paths.ldapdir,
249 paths.modulesconf = os.path.join(paths.ldapdir,
251 paths.memberofconf = os.path.join(paths.ldapdir,
253 paths.fedoradsinf = os.path.join(paths.ldapdir,
255 paths.fedoradspartitions = os.path.join(paths.ldapdir,
256 "fedorads-partitions.ldif")
257 paths.hklm = "hklm.ldb"
258 paths.hkcr = "hkcr.ldb"
259 paths.hkcu = "hkcu.ldb"
260 paths.hku = "hku.ldb"
261 paths.hkpd = "hkpd.ldb"
262 paths.hkpt = "hkpt.ldb"
264 paths.sysvol = lp.get("path", "sysvol")
266 paths.netlogon = lp.get("path", "netlogon")
268 paths.smbconf = lp.configfile()
273 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
274 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
276 """Guess configuration settings to use."""
279 hostname = socket.gethostname().split(".")[0].lower()
281 netbiosname = hostname.upper()
282 if not valid_netbios_name(netbiosname):
283 raise InvalidNetbiosName(netbiosname)
285 hostname = hostname.lower()
287 if dnsdomain is None:
288 dnsdomain = lp.get("realm")
290 if serverrole is None:
291 serverrole = lp.get("server role")
293 assert dnsdomain is not None
294 realm = dnsdomain.upper()
296 if lp.get("realm").upper() != realm:
297 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
298 (lp.get("realm"), lp.configfile(), realm))
300 dnsdomain = dnsdomain.lower()
302 if (serverrole == "domain controller"):
304 domain = lp.get("workgroup")
306 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
307 if lp.get("workgroup").upper() != domain.upper():
308 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
309 lp.get("workgroup"), domain)
313 domaindn = "CN=" + netbiosname
315 assert domain is not None
316 domain = domain.upper()
317 if not valid_netbios_name(domain):
318 raise InvalidNetbiosName(domain)
324 configdn = "CN=Configuration," + rootdn
326 schemadn = "CN=Schema," + configdn
331 names = ProvisionNames()
332 names.rootdn = rootdn
333 names.domaindn = domaindn
334 names.configdn = configdn
335 names.schemadn = schemadn
336 names.ldapmanagerdn = "CN=Manager," + rootdn
337 names.dnsdomain = dnsdomain
338 names.domain = domain
340 names.netbiosname = netbiosname
341 names.hostname = hostname
342 names.sitename = sitename
343 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
348 def load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir):
349 if targetdir is not None:
350 if not os.path.exists(targetdir):
352 if not os.path.exists(os.path.join(targetdir, "etc")):
353 os.mkdir(os.path.join(targetdir, "etc"))
355 smbconf = os.path.join(targetdir, "etc", "smb.conf")
357 # only install a new smb.conf if there isn't one there already
359 if not os.path.exists(smbconf):
361 hostname = socket.gethostname().split(".")[0].lower()
363 if serverrole is None:
364 serverrole = "standalone"
366 assert serverrole in ("domain controller", "member server", "standalone")
367 if serverrole == "domain controller":
369 elif serverrole == "member server":
370 smbconfsuffix = "member"
371 elif serverrole == "standalone":
372 smbconfsuffix = "standalone"
374 assert domain is not None
375 assert realm is not None
377 default_lp = param.LoadParm()
378 #Load non-existant file
379 default_lp.load(smbconf)
381 if targetdir is not None:
382 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
383 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
385 default_lp.set("lock dir", os.path.abspath(targetdir))
390 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
391 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
393 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
395 "HOSTNAME": hostname,
398 "SERVERROLE": serverrole,
399 "NETLOGONPATH": netlogon,
400 "SYSVOLPATH": sysvol,
401 "PRIVATEDIR_LINE": privatedir_line,
402 "LOCKDIR_LINE": lockdir_line
405 lp = param.LoadParm()
411 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
412 users_gid, wheel_gid):
413 """setup reasonable name mappings for sam names to unix names.
415 :param samdb: SamDB object.
416 :param idmap: IDmap db object.
417 :param sid: The domain sid.
418 :param domaindn: The domain DN.
419 :param root_uid: uid of the UNIX root user.
420 :param nobody_uid: uid of the UNIX nobody user.
421 :param users_gid: gid of the UNIX users group.
422 :param wheel_gid: gid of the UNIX wheel group."""
423 # add some foreign sids if they are not present already
424 samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
425 samdb.add_foreign(domaindn, "S-1-1-0", "World")
426 samdb.add_foreign(domaindn, "S-1-5-2", "Network")
427 samdb.add_foreign(domaindn, "S-1-5-18", "System")
428 samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
430 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
431 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
433 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
434 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
437 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
439 serverrole, ldap_backend=None,
440 ldap_backend_type=None, erase=False):
441 """Setup the partitions for the SAM database.
443 Alternatively, provision() may call this, and then populate the database.
445 :note: This will wipe the Sam Database!
447 :note: This function always removes the local SAM LDB file. The erase
448 parameter controls whether to erase the existing data, which
449 may not be stored locally but in LDAP.
451 assert session_info is not None
453 samdb = SamDB(samdb_path, session_info=session_info,
454 credentials=credentials, lp=lp)
460 os.unlink(samdb_path)
462 samdb = SamDB(samdb_path, session_info=session_info,
463 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",
491 modules_list2 = ["show_deleted",
494 domaindn_ldb = "users.ldb"
495 if ldap_backend is not None:
496 domaindn_ldb = ldap_backend
497 configdn_ldb = "configuration.ldb"
498 if ldap_backend is not None:
499 configdn_ldb = ldap_backend
500 schemadn_ldb = "schema.ldb"
501 if ldap_backend is not None:
502 schema_ldb = ldap_backend
503 schemadn_ldb = ldap_backend
505 if ldap_backend_type == "fedora-ds":
506 backend_modules = ["nsuniqueid", "paged_searches"]
507 # We can handle linked attributes here, as we don't have directory-side subtree operations
508 tdb_modules_list = ["linked_attributes"]
509 elif ldap_backend_type == "openldap":
510 backend_modules = ["normalise", "entryuuid", "paged_searches"]
511 # OpenLDAP handles subtree renames, so we don't want to do any of these things
512 tdb_modules_list = None
513 elif serverrole == "domain controller":
514 backend_modules = ["repl_meta_data"]
516 backend_modules = ["objectguid"]
518 if tdb_modules_list is None:
519 tdb_modules_list_as_string = ""
521 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
523 samdb.transaction_start()
525 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
526 "SCHEMADN": names.schemadn,
527 "SCHEMADN_LDB": schemadn_ldb,
528 "SCHEMADN_MOD2": ",objectguid",
529 "CONFIGDN": names.configdn,
530 "CONFIGDN_LDB": configdn_ldb,
531 "DOMAINDN": names.domaindn,
532 "DOMAINDN_LDB": domaindn_ldb,
533 "SCHEMADN_MOD": "schema_fsmo,instancetype",
534 "CONFIGDN_MOD": "naming_fsmo,instancetype",
535 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
536 "MODULES_LIST": ",".join(modules_list),
537 "TDB_MODULES_LIST": tdb_modules_list_as_string,
538 "MODULES_LIST2": ",".join(modules_list2),
539 "BACKEND_MOD": ",".join(backend_modules),
543 samdb.transaction_cancel()
546 samdb.transaction_commit()
548 samdb = SamDB(samdb_path, session_info=session_info,
549 credentials=credentials, lp=lp)
551 samdb.transaction_start()
553 message("Setting up sam.ldb attributes")
554 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
556 message("Setting up sam.ldb rootDSE")
557 setup_samdb_rootdse(samdb, setup_path, names)
560 message("Erasing data from partitions")
561 samdb.erase_partitions()
564 samdb.transaction_cancel()
567 samdb.transaction_commit()
572 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
573 netbiosname, domainsid, keytab_path, samdb_url,
574 dns_keytab_path, dnspass, machinepass):
575 """Add DC-specific bits to a secrets database.
577 :param secretsdb: Ldb Handle to the secrets database
578 :param setup_path: Setup path function
579 :param machinepass: Machine password
581 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
582 "MACHINEPASS_B64": b64encode(machinepass),
585 "DNSDOMAIN": dnsdomain,
586 "DOMAINSID": str(domainsid),
587 "SECRETS_KEYTAB": keytab_path,
588 "NETBIOSNAME": netbiosname,
589 "SAM_LDB": samdb_url,
590 "DNS_KEYTAB": dns_keytab_path,
591 "DNSPASS_B64": b64encode(dnspass),
595 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
596 """Setup the secrets database.
598 :param path: Path to the secrets database.
599 :param setup_path: Get the path to a setup file.
600 :param session_info: Session info.
601 :param credentials: Credentials
602 :param lp: Loadparm context
603 :return: LDB handle for the created secrets database
605 if os.path.exists(path):
607 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
610 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
611 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
613 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
617 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
618 """Setup the templates database.
620 :param path: Path to the database.
621 :param setup_path: Function for obtaining the path to setup files.
622 :param session_info: Session info
623 :param credentials: Credentials
624 :param lp: Loadparm context
626 templates_ldb = SamDB(path, session_info=session_info,
627 credentials=credentials, lp=lp)
628 templates_ldb.erase()
629 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
632 def setup_registry(path, setup_path, session_info, credentials, lp):
633 """Setup the registry.
635 :param path: Path to the registry database
636 :param setup_path: Function that returns the path to a setup.
637 :param session_info: Session information
638 :param credentials: Credentials
639 :param lp: Loadparm context
641 reg = registry.Registry()
642 hive = registry.open_ldb(path, session_info=session_info,
643 credentials=credentials, lp_ctx=lp)
644 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
645 provision_reg = setup_path("provision.reg")
646 assert os.path.exists(provision_reg)
647 reg.diff_apply(provision_reg)
650 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
651 """Setup the idmap database.
653 :param path: path to the idmap database
654 :param setup_path: Function that returns a path to a setup file
655 :param session_info: Session information
656 :param credentials: Credentials
657 :param lp: Loadparm context
659 if os.path.exists(path):
662 idmap_ldb = IDmapDB(path, session_info=session_info,
663 credentials=credentials, lp=lp)
666 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
670 def setup_samdb_rootdse(samdb, setup_path, names):
671 """Setup the SamDB rootdse.
673 :param samdb: Sam Database handle
674 :param setup_path: Obtain setup path
676 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
677 "SCHEMADN": names.schemadn,
678 "NETBIOSNAME": names.netbiosname,
679 "DNSDOMAIN": names.dnsdomain,
680 "REALM": names.realm,
681 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
682 "DOMAINDN": names.domaindn,
683 "ROOTDN": names.rootdn,
684 "CONFIGDN": names.configdn,
685 "SERVERDN": names.serverdn,
689 def setup_self_join(samdb, names,
690 machinepass, dnspass,
691 domainsid, invocationid, setup_path,
693 """Join a host to its own domain."""
694 assert isinstance(invocationid, str)
695 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
696 "CONFIGDN": names.configdn,
697 "SCHEMADN": names.schemadn,
698 "DOMAINDN": names.domaindn,
699 "SERVERDN": names.serverdn,
700 "INVOCATIONID": invocationid,
701 "NETBIOSNAME": names.netbiosname,
702 "DEFAULTSITE": names.sitename,
703 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
704 "MACHINEPASS_B64": b64encode(machinepass),
705 "DNSPASS_B64": b64encode(dnspass),
706 "REALM": names.realm,
707 "DOMAIN": names.domain,
708 "DNSDOMAIN": names.dnsdomain})
709 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
710 "POLICYGUID": policyguid,
711 "DNSDOMAIN": names.dnsdomain,
712 "DOMAINSID": str(domainsid),
713 "DOMAINDN": names.domaindn})
716 def setup_samdb(path, setup_path, session_info, credentials, lp,
718 domainsid, aci, domainguid, policyguid,
719 fill, adminpass, krbtgtpass,
720 machinepass, invocationid, dnspass,
721 serverrole, ldap_backend=None,
722 ldap_backend_type=None):
723 """Setup a complete SAM Database.
725 :note: This will wipe the main SAM database file!
728 erase = (fill != FILL_DRS)
730 # Also wipes the database
731 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
732 credentials=credentials, session_info=session_info,
734 ldap_backend=ldap_backend, serverrole=serverrole,
735 ldap_backend_type=ldap_backend_type, erase=erase)
737 samdb = SamDB(path, session_info=session_info,
738 credentials=credentials, lp=lp)
741 # We want to finish here, but setup the index before we do so
742 message("Setting up sam.ldb index")
743 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
746 message("Pre-loading the Samba 4 and AD schema")
747 samdb.set_domain_sid(domainsid)
748 if serverrole == "domain controller":
749 samdb.set_invocation_id(invocationid)
751 load_schema(setup_path, samdb, names.schemadn, names.netbiosname, names.configdn, names.sitename)
753 samdb.transaction_start()
756 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
757 if serverrole == "domain controller":
758 domain_oc = "domainDNS"
760 domain_oc = "samba4LocalDomain"
762 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
763 "DOMAINDN": names.domaindn,
765 "DOMAIN_OC": domain_oc
768 message("Modifying DomainDN: " + names.domaindn + "")
769 if domainguid is not None:
770 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
774 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
775 "LDAPTIME": timestring(int(time.time())),
776 "DOMAINSID": str(domainsid),
777 "SCHEMADN": names.schemadn,
778 "NETBIOSNAME": names.netbiosname,
779 "DEFAULTSITE": names.sitename,
780 "CONFIGDN": names.configdn,
781 "SERVERDN": names.serverdn,
782 "POLICYGUID": policyguid,
783 "DOMAINDN": names.domaindn,
784 "DOMAINGUID_MOD": domainguid_mod,
787 message("Adding configuration container (permitted to fail)")
788 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
789 "CONFIGDN": names.configdn,
791 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
793 message("Modifying configuration container")
794 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
795 "CONFIGDN": names.configdn,
796 "SCHEMADN": names.schemadn,
799 message("Adding schema container (permitted to fail)")
800 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
801 "SCHEMADN": names.schemadn,
803 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
805 message("Modifying schema container")
806 setup_modify_ldif(samdb,
807 setup_path("provision_schema_basedn_modify.ldif"), {
808 "SCHEMADN": names.schemadn,
809 "NETBIOSNAME": names.netbiosname,
810 "DEFAULTSITE": names.sitename,
811 "CONFIGDN": names.configdn,
812 "SERVERDN": names.serverdn
815 message("Setting up sam.ldb Samba4 schema")
816 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
817 {"SCHEMADN": names.schemadn })
818 message("Setting up sam.ldb AD schema")
819 setup_add_ldif(samdb, setup_path("schema.ldif"),
820 {"SCHEMADN": names.schemadn})
822 message("Setting up sam.ldb configuration data")
823 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
824 "CONFIGDN": names.configdn,
825 "NETBIOSNAME": names.netbiosname,
826 "DEFAULTSITE": names.sitename,
827 "DNSDOMAIN": names.dnsdomain,
828 "DOMAIN": names.domain,
829 "SCHEMADN": names.schemadn,
830 "DOMAINDN": names.domaindn,
831 "SERVERDN": names.serverdn
834 message("Setting up display specifiers")
835 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
836 {"CONFIGDN": names.configdn})
838 message("Adding users container (permitted to fail)")
839 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
840 "DOMAINDN": names.domaindn})
841 message("Modifying users container")
842 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
843 "DOMAINDN": names.domaindn})
844 message("Adding computers container (permitted to fail)")
845 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
846 "DOMAINDN": names.domaindn})
847 message("Modifying computers container")
848 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
849 "DOMAINDN": names.domaindn})
850 message("Setting up sam.ldb data")
851 setup_add_ldif(samdb, setup_path("provision.ldif"), {
852 "DOMAINDN": names.domaindn,
853 "NETBIOSNAME": names.netbiosname,
854 "DEFAULTSITE": names.sitename,
855 "CONFIGDN": names.configdn,
856 "SERVERDN": names.serverdn
859 if fill == FILL_FULL:
860 message("Setting up sam.ldb users and groups")
861 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
862 "DOMAINDN": names.domaindn,
863 "DOMAINSID": str(domainsid),
864 "CONFIGDN": names.configdn,
865 "ADMINPASS_B64": b64encode(adminpass),
866 "KRBTGTPASS_B64": b64encode(krbtgtpass),
869 if serverrole == "domain controller":
870 message("Setting up self join")
871 setup_self_join(samdb, names=names, invocationid=invocationid,
873 machinepass=machinepass,
874 domainsid=domainsid, policyguid=policyguid,
875 setup_path=setup_path)
877 #We want to setup the index last, as adds are faster unindexed
878 message("Setting up sam.ldb index")
879 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
881 samdb.transaction_cancel()
884 samdb.transaction_commit()
889 FILL_NT4SYNC = "NT4SYNC"
892 def provision(setup_dir, message, session_info,
893 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
894 rootdn=None, domaindn=None, schemadn=None, configdn=None,
896 domain=None, hostname=None, hostip=None, hostip6=None,
897 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
898 policyguid=None, invocationid=None, machinepass=None,
899 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
900 wheel=None, backup=None, aci=None, serverrole=None,
901 ldap_backend=None, ldap_backend_type=None, sitename=None):
904 :note: caution, this wipes all existing data!
907 def setup_path(file):
908 return os.path.join(setup_dir, file)
910 if domainsid is None:
911 domainsid = security.random_sid()
913 domainsid = security.Sid(domainsid)
915 if policyguid is None:
916 policyguid = str(uuid.uuid4())
917 if adminpass is None:
918 adminpass = misc.random_password(12)
919 if krbtgtpass is None:
920 krbtgtpass = misc.random_password(12)
921 if machinepass is None:
922 machinepass = misc.random_password(12)
924 dnspass = misc.random_password(12)
926 root_uid = findnss(pwd.getpwnam, ["root"])[2]
928 root_uid = findnss(pwd.getpwnam, [root])[2]
930 nobody_uid = findnss(pwd.getpwnam, ["nobody"])[2]
932 nobody_uid = findnss(pwd.getpwnam, [nobody])[2]
934 users_gid = findnss(grp.getgrnam, ["users"])[2]
936 users_gid = findnss(grp.getgrnam, [users])[2]
938 wheel_gid = findnss(grp.getgrnam, ["wheel", "adm"])[2]
940 wheel_gid = findnss(grp.getgrnam, [wheel])[2]
942 aci = "# no aci for local ldb"
944 lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
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: pass
961 if serverrole is None:
962 serverrole = lp.get("server role")
964 assert serverrole in ("domain controller", "member server", "standalone")
965 if invocationid is None and serverrole == "domain controller":
966 invocationid = str(uuid.uuid4())
968 if not os.path.exists(paths.private_dir):
969 os.mkdir(paths.private_dir)
971 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
973 if ldap_backend is not None:
974 if ldap_backend == "ldapi":
975 # provision-backend will set this path suggested slapd command line / fedorads.inf
976 ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
978 # only install a new shares config db if there is none
979 if not os.path.exists(paths.shareconf):
980 message("Setting up share.ldb")
981 share_ldb = Ldb(paths.shareconf, session_info=session_info,
982 credentials=credentials, lp=lp)
983 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
986 message("Setting up secrets.ldb")
987 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
988 session_info=session_info,
989 credentials=credentials, lp=lp)
991 message("Setting up the registry")
992 setup_registry(paths.hklm, setup_path, session_info,
993 credentials=credentials, lp=lp)
995 message("Setting up templates db")
996 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
997 credentials=credentials, lp=lp)
999 message("Setting up idmap db")
1000 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1001 credentials=credentials, lp=lp)
1003 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1004 credentials=credentials, lp=lp, names=names,
1006 domainsid=domainsid,
1007 aci=aci, domainguid=domainguid, policyguid=policyguid,
1009 adminpass=adminpass, krbtgtpass=krbtgtpass,
1010 invocationid=invocationid,
1011 machinepass=machinepass, dnspass=dnspass,
1012 serverrole=serverrole, ldap_backend=ldap_backend,
1013 ldap_backend_type=ldap_backend_type)
1015 if lp.get("server role") == "domain controller":
1016 if paths.netlogon is None:
1017 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1018 message("Please either remove %s or see the template at %s" %
1019 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1020 assert(paths.netlogon is not None)
1022 if paths.sysvol is None:
1023 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1024 message("Please either remove %s or see the template at %s" %
1025 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1026 assert(paths.sysvol is not None)
1028 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1029 "{" + policyguid + "}")
1030 os.makedirs(policy_path, 0755)
1031 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1032 os.makedirs(os.path.join(policy_path, "User"), 0755)
1033 if not os.path.isdir(paths.netlogon):
1034 os.makedirs(paths.netlogon, 0755)
1036 if samdb_fill == FILL_FULL:
1037 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1038 root_uid=root_uid, nobody_uid=nobody_uid,
1039 users_gid=users_gid, wheel_gid=wheel_gid)
1041 message("Setting up sam.ldb rootDSE marking as synchronized")
1042 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1044 # Only make a zone file on the first DC, it should be replicated with DNS replication
1045 if serverrole == "domain controller":
1046 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1047 credentials=credentials, lp=lp)
1048 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1049 netbiosname=names.netbiosname, domainsid=domainsid,
1050 keytab_path=paths.keytab, samdb_url=paths.samdb,
1051 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1052 machinepass=machinepass, dnsdomain=names.dnsdomain)
1054 samdb = SamDB(paths.samdb, session_info=session_info,
1055 credentials=credentials, lp=lp)
1057 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1058 assert isinstance(domainguid, str)
1059 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1060 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1061 scope=SCOPE_SUBTREE)
1062 assert isinstance(hostguid, str)
1064 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1065 domaindn=names.domaindn, hostip=hostip,
1066 hostip6=hostip6, hostname=names.hostname,
1067 dnspass=dnspass, realm=names.realm,
1068 domainguid=domainguid, hostguid=hostguid)
1069 message("Please install the zone located in %s into your DNS server" % paths.dns)
1071 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1072 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1073 keytab_name=paths.dns_keytab)
1074 message("See %s for example configuration statements for secure GSS-TSIG updates" % paths.namedconf)
1076 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1077 hostname=names.hostname, realm=names.realm)
1078 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1080 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1083 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1085 message("Once the above files are installed, your Samba4 server will be ready to use")
1086 message("Server Role: %s" % serverrole)
1087 message("Hostname: %s" % names.hostname)
1088 message("NetBIOS Domain: %s" % names.domain)
1089 message("DNS Domain: %s" % names.dnsdomain)
1090 message("DOMAIN SID: %s" % str(domainsid))
1091 message("Admin password: %s" % adminpass)
1093 result = ProvisionResult()
1094 result.domaindn = domaindn
1095 result.paths = paths
1097 result.samdb = samdb
1101 def provision_become_dc(setup_dir=None,
1102 smbconf=None, targetdir=None, realm=None,
1103 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1105 domain=None, hostname=None, domainsid=None,
1106 adminpass=None, krbtgtpass=None, domainguid=None,
1107 policyguid=None, invocationid=None, machinepass=None,
1108 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1109 wheel=None, backup=None, aci=None, serverrole=None,
1110 ldap_backend=None, ldap_backend_type=None, sitename=None):
1113 """print a message if quiet is not set."""
1116 return provision(setup_dir, message, system_session(), None,
1117 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1118 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1119 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1122 def setup_db_config(setup_path, dbdir):
1123 """Setup a Berkeley database.
1125 :param setup_path: Setup path function.
1126 :param dbdir: Database directory."""
1127 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1128 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1129 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1130 os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1132 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1133 {"LDAPDBDIR": dbdir})
1137 def provision_backend(setup_dir=None, message=None,
1138 smbconf=None, targetdir=None, realm=None,
1139 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1140 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1141 ldap_backend_type=None, ldap_backend_port=None):
1143 def setup_path(file):
1144 return os.path.join(setup_dir, file)
1146 if hostname is None:
1147 hostname = socket.gethostname().split(".")[0].lower()
1150 root = findnss(pwd.getpwnam, ["root"])[0]
1152 lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1154 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1155 dnsdomain=realm, serverrole=serverrole,
1156 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1158 paths = provision_paths_from_lp(lp, names.dnsdomain)
1160 if not os.path.isdir(paths.ldapdir):
1161 os.makedirs(paths.ldapdir)
1162 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1164 os.unlink(schemadb_path)
1168 schemadb = Ldb(schemadb_path, lp=lp)
1170 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1171 {"SCHEMADN": names.schemadn,
1173 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1175 setup_modify_ldif(schemadb,
1176 setup_path("provision_schema_basedn_modify.ldif"), \
1177 {"SCHEMADN": names.schemadn,
1178 "NETBIOSNAME": names.netbiosname,
1179 "DEFAULTSITE": DEFAULTSITE,
1180 "CONFIGDN": names.configdn,
1181 "SERVERDN": names.serverdn
1184 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1185 {"SCHEMADN": names.schemadn })
1186 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1187 {"SCHEMADN": names.schemadn})
1189 if ldap_backend_type == "fedora-ds":
1190 if ldap_backend_port is not None:
1191 serverport = "ServerPort=%d" % ldap_backend_port
1195 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1197 "HOSTNAME": hostname,
1198 "DNSDOMAIN": names.dnsdomain,
1199 "LDAPDIR": paths.ldapdir,
1200 "DOMAINDN": names.domaindn,
1201 "LDAPMANAGERDN": names.ldapmanagerdn,
1202 "LDAPMANAGERPASS": adminpass,
1203 "SERVERPORT": serverport})
1205 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1206 {"CONFIGDN": names.configdn,
1207 "SCHEMADN": names.schemadn,
1210 mapping = "schema-map-fedora-ds-1.0"
1211 backend_schema = "99_ad.ldif"
1213 slapdcommand="Initailise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1215 elif ldap_backend_type == "openldap":
1216 attrs = ["linkID", "lDAPDisplayName"]
1217 res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1219 memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1220 refint_attributes = "";
1221 for i in range (0, len(res)):
1222 linkid = res[i]["linkID"][0]
1223 linkid = str(int(linkid) + 1)
1224 expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1225 target = schemadb.searchone(basedn=names.schemadn,
1226 expression=expression,
1227 attribute="lDAPDisplayName",
1228 scope=SCOPE_SUBTREE);
1229 if target is not None:
1230 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1231 memberof_config = memberof_config + """overlay memberof
1232 memberof-dangling error
1233 memberof-refint TRUE
1234 memberof-group-oc top
1235 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1236 memberof-memberof-ad """ + target + """
1237 memberof-dangling-error 32
1241 memberof_config = memberof_config + """
1243 refint_attributes""" + refint_attributes + "\n";
1245 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1246 {"DNSDOMAIN": names.dnsdomain,
1247 "LDAPDIR": paths.ldapdir,
1248 "DOMAINDN": names.domaindn,
1249 "CONFIGDN": names.configdn,
1250 "SCHEMADN": names.schemadn,
1251 "LDAPMANAGERDN": names.ldapmanagerdn,
1252 "LDAPMANAGERPASS": adminpass,
1253 "MEMBEROF_CONFIG": memberof_config})
1254 setup_file(setup_path("modules.conf"), paths.modulesconf,
1255 {"REALM": names.realm})
1257 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "user")))
1258 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "config")))
1259 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "schema")))
1260 mapping = "schema-map-openldap-2.3"
1261 backend_schema = "backend-schema.schema"
1263 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1264 if ldap_backend_port is not None:
1265 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1267 server_port_string = ""
1268 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1270 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);
1272 os.system(schema_command)
1275 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ( ldap_backend_type) )
1276 message("Server Role: %s" % serverrole)
1277 message("Hostname: %s" % names.hostname)
1278 message("DNS Domain: %s" % names.dnsdomain)
1279 message("Base DN: %s" % names.domaindn)
1280 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1281 message("LDAP admin password: %s" % adminpass)
1282 message(slapdcommand)
1285 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1286 """Create a PHP LDAP admin configuration file.
1288 :param path: Path to write the configuration to.
1289 :param setup_path: Function to generate setup paths.
1291 setup_file(setup_path("phpldapadmin-config.php"), path,
1292 {"S4_LDAPI_URI": ldapi_uri})
1295 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1296 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1297 """Write out a DNS zone file, from the info in the current database.
1299 :param path: Path of the new zone file.
1300 :param setup_path: Setup path function.
1301 :param dnsdomain: DNS Domain name
1302 :param domaindn: DN of the Domain
1303 :param hostip: Local IPv4 IP
1304 :param hostip6: Local IPv6 IP
1305 :param hostname: Local hostname
1306 :param dnspass: Password for DNS
1307 :param realm: Realm name
1308 :param domainguid: GUID of the domain.
1309 :param hostguid: GUID of the host.
1311 assert isinstance(domainguid, str)
1313 hostip6_base_line = ""
1314 hostip6_host_line = ""
1316 if hostip6 is not None:
1317 hostip6_base_line = " IN AAAA " + hostip6
1318 hostip6_host_line = hostname + " IN AAAA " + hostip6
1320 setup_file(setup_path("provision.zone"), path, {
1321 "DNSPASS_B64": b64encode(dnspass),
1322 "HOSTNAME": hostname,
1323 "DNSDOMAIN": dnsdomain,
1326 "DOMAINGUID": domainguid,
1327 "DATESTRING": time.strftime("%Y%m%d%H"),
1328 "DEFAULTSITE": DEFAULTSITE,
1329 "HOSTGUID": hostguid,
1330 "HOSTIP6_BASE_LINE": hostip6_base_line,
1331 "HOSTIP6_HOST_LINE": hostip6_host_line,
1334 def create_named_conf(path, setup_path, realm, dnsdomain,
1335 private_dir, keytab_name):
1336 """Write out a file containing zone statements suitable for inclusion in a
1337 named.conf file (including GSS-TSIG configuration).
1339 :param path: Path of the new named.conf file.
1340 :param setup_path: Setup path function.
1341 :param realm: Realm name
1342 :param dnsdomain: DNS Domain name
1343 :param private_dir: Path to private directory
1344 :param keytab_name: File name of DNS keytab file
1347 setup_file(setup_path("named.conf"), path, {
1348 "DNSDOMAIN": dnsdomain,
1350 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1351 "DNS_KEYTAB": keytab_name,
1352 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1355 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1356 """Write out a file containing zone statements suitable for inclusion in a
1357 named.conf file (including GSS-TSIG configuration).
1359 :param path: Path of the new named.conf file.
1360 :param setup_path: Setup path function.
1361 :param dnsdomain: DNS Domain name
1362 :param hostname: Local hostname
1363 :param realm: Realm name
1366 setup_file(setup_path("krb5.conf"), path, {
1367 "DNSDOMAIN": dnsdomain,
1368 "HOSTNAME": hostname,
1372 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1373 """Load schema for the SamDB.
1375 :param samdb: Load a schema into a SamDB.
1376 :param setup_path: Setup path function.
1377 :param schemadn: DN of the schema
1378 :param netbiosname: NetBIOS name of the host.
1379 :param configdn: DN of the configuration
1381 schema_data = open(setup_path("schema.ldif"), 'r').read()
1382 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1383 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1384 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1385 head_data = substitute_var(head_data, {
1386 "SCHEMADN": schemadn,
1387 "NETBIOSNAME": netbiosname,
1388 "CONFIGDN": configdn,
1389 "DEFAULTSITE":sitename
1391 samdb.attach_schema_from_ldif(head_data, schema_data)