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 __docformat__ = "restructuredText"
48 DEFAULTSITE = "Default-First-Site-Name"
50 class InvalidNetbiosName(Exception):
51 def __init__(self, name):
52 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
68 self.dns_keytab = None
71 self.private_dir = None
74 self.modulesconf = None
75 self.memberofconf = None
76 self.fedoradsinf = None
77 self.fedoradspartitions = None
85 self.ldapmanagerdn = None
88 self.netbiosname = None
94 class ProvisionResult:
101 def check_install(lp, session_info, credentials):
102 """Check whether the current install seems ok.
104 :param lp: Loadparm context
105 :param session_info: Session information
106 :param credentials: Credentials
108 if lp.get("realm") == "":
109 raise Exception("Realm empty")
110 ldb = Ldb(lp.get("sam database"), session_info=session_info,
111 credentials=credentials, lp=lp)
112 if len(ldb.search("(cn=Administrator)")) != 1:
113 raise "No administrator account found"
116 def findnss(nssfn, names):
117 """Find a user or group from a list of possibilities.
119 :param nssfn: NSS Function to try (should raise KeyError if not found)
120 :param names: Names to check.
121 :return: Value return by first names list.
128 raise KeyError("Unable to find user/group %r" % names)
131 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
132 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
135 def open_ldb(session_info, credentials, lp, dbname):
136 """Open a LDB, thrashing it if it is corrupt.
138 :param session_info: auth session information
139 :param credentials: credentials
140 :param lp: Loadparm context
141 :param dbname: Path of the database to open.
142 :return: a Ldb object
144 assert session_info is not None
146 return Ldb(dbname, session_info=session_info, credentials=credentials,
151 return Ldb(dbname, session_info=session_info, credentials=credentials,
155 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
156 """Setup a ldb in the private dir.
158 :param ldb: LDB file to import data into
159 :param ldif_path: Path of the LDIF file to load
160 :param subst_vars: Optional variables to subsitute in LDIF.
162 assert isinstance(ldif_path, str)
164 data = open(ldif_path, 'r').read()
165 if subst_vars is not None:
166 data = substitute_var(data, subst_vars)
168 check_all_substituted(data)
173 def setup_modify_ldif(ldb, ldif_path, substvars=None):
174 """Modify a ldb in the private dir.
176 :param ldb: LDB object.
177 :param ldif_path: LDIF file path.
178 :param substvars: Optional dictionary with substitution variables.
180 data = open(ldif_path, 'r').read()
181 if substvars is not None:
182 data = substitute_var(data, substvars)
184 check_all_substituted(data)
186 ldb.modify_ldif(data)
189 def setup_ldb(ldb, ldif_path, subst_vars):
190 """Import a LDIF a file into a LDB handle, optionally substituting variables.
192 :note: Either all LDIF data will be added or none (using transactions).
194 :param ldb: LDB file to import into.
195 :param ldif_path: Path to the LDIF file.
196 :param subst_vars: Dictionary with substitution variables.
198 assert ldb is not None
199 ldb.transaction_start()
201 setup_add_ldif(ldb, ldif_path, subst_vars)
203 ldb.transaction_cancel()
205 ldb.transaction_commit()
208 def setup_file(template, fname, substvars):
209 """Setup a file in the private dir.
211 :param template: Path of the template file.
212 :param fname: Path of the file to create.
213 :param substvars: Substitution variables.
217 if os.path.exists(f):
220 data = open(template, 'r').read()
222 data = substitute_var(data, substvars)
223 check_all_substituted(data)
225 open(f, 'w').write(data)
228 def provision_paths_from_lp(lp, dnsdomain):
229 """Set the default paths for provisioning.
231 :param lp: Loadparm context.
232 :param dnsdomain: DNS Domain name
234 paths = ProvisionPaths()
235 paths.private_dir = lp.get("private dir")
236 paths.keytab = "secrets.keytab"
237 paths.dns_keytab = "dns.keytab"
239 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
240 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
241 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
242 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
243 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
244 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
245 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
246 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
247 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
248 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
249 paths.phpldapadminconfig = os.path.join(paths.private_dir,
250 "phpldapadmin-config.php")
251 paths.ldapdir = os.path.join(paths.private_dir,
253 paths.slapdconf = os.path.join(paths.ldapdir,
255 paths.modulesconf = os.path.join(paths.ldapdir,
257 paths.memberofconf = os.path.join(paths.ldapdir,
259 paths.fedoradsinf = os.path.join(paths.ldapdir,
261 paths.fedoradspartitions = os.path.join(paths.ldapdir,
262 "fedorads-partitions.ldif")
263 paths.hklm = "hklm.ldb"
264 paths.hkcr = "hkcr.ldb"
265 paths.hkcu = "hkcu.ldb"
266 paths.hku = "hku.ldb"
267 paths.hkpd = "hkpd.ldb"
268 paths.hkpt = "hkpt.ldb"
270 paths.sysvol = lp.get("path", "sysvol")
272 paths.netlogon = lp.get("path", "netlogon")
274 paths.smbconf = lp.configfile()
279 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
280 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
282 """Guess configuration settings to use."""
285 hostname = socket.gethostname().split(".")[0].lower()
287 netbiosname = hostname.upper()
288 if not valid_netbios_name(netbiosname):
289 raise InvalidNetbiosName(netbiosname)
291 hostname = hostname.lower()
293 if dnsdomain is None:
294 dnsdomain = lp.get("realm")
296 if serverrole is None:
297 serverrole = lp.get("server role")
299 assert dnsdomain is not None
300 realm = dnsdomain.upper()
302 if lp.get("realm").upper() != realm:
303 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
304 (lp.get("realm"), lp.configfile(), realm))
306 dnsdomain = dnsdomain.lower()
308 if serverrole == "domain controller":
310 domain = lp.get("workgroup")
312 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
313 if lp.get("workgroup").upper() != domain.upper():
314 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
315 lp.get("workgroup"), domain)
319 domaindn = "CN=" + netbiosname
321 assert domain is not None
322 domain = domain.upper()
323 if not valid_netbios_name(domain):
324 raise InvalidNetbiosName(domain)
330 configdn = "CN=Configuration," + rootdn
332 schemadn = "CN=Schema," + configdn
337 names = ProvisionNames()
338 names.rootdn = rootdn
339 names.domaindn = domaindn
340 names.configdn = configdn
341 names.schemadn = schemadn
342 names.ldapmanagerdn = "CN=Manager," + rootdn
343 names.dnsdomain = dnsdomain
344 names.domain = domain
346 names.netbiosname = netbiosname
347 names.hostname = hostname
348 names.sitename = sitename
349 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
354 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
357 hostname = socket.gethostname().split(".")[0].lower()
359 if serverrole is None:
360 serverrole = "standalone"
362 assert serverrole in ("domain controller", "member server", "standalone")
363 if serverrole == "domain controller":
365 elif serverrole == "member server":
366 smbconfsuffix = "member"
367 elif serverrole == "standalone":
368 smbconfsuffix = "standalone"
370 assert domain is not None
371 assert realm is not None
373 default_lp = param.LoadParm()
374 #Load non-existant file
375 default_lp.load(smbconf)
377 if targetdir is not None:
378 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
379 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
381 default_lp.set("lock dir", os.path.abspath(targetdir))
386 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
387 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
389 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
391 "HOSTNAME": hostname,
394 "SERVERROLE": serverrole,
395 "NETLOGONPATH": netlogon,
396 "SYSVOLPATH": sysvol,
397 "PRIVATEDIR_LINE": privatedir_line,
398 "LOCKDIR_LINE": lockdir_line
403 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
404 users_gid, wheel_gid):
405 """setup reasonable name mappings for sam names to unix names.
407 :param samdb: SamDB object.
408 :param idmap: IDmap db object.
409 :param sid: The domain sid.
410 :param domaindn: The domain DN.
411 :param root_uid: uid of the UNIX root user.
412 :param nobody_uid: uid of the UNIX nobody user.
413 :param users_gid: gid of the UNIX users group.
414 :param wheel_gid: gid of the UNIX wheel group."""
415 # add some foreign sids if they are not present already
416 samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
417 samdb.add_foreign(domaindn, "S-1-1-0", "World")
418 samdb.add_foreign(domaindn, "S-1-5-2", "Network")
419 samdb.add_foreign(domaindn, "S-1-5-18", "System")
420 samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
422 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
423 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
425 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
426 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
429 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
431 serverrole, ldap_backend=None,
432 ldap_backend_type=None, erase=False):
433 """Setup the partitions for the SAM database.
435 Alternatively, provision() may call this, and then populate the database.
437 :note: This will wipe the Sam Database!
439 :note: This function always removes the local SAM LDB file. The erase
440 parameter controls whether to erase the existing data, which
441 may not be stored locally but in LDAP.
443 assert session_info is not None
445 samdb = SamDB(samdb_path, session_info=session_info,
446 credentials=credentials, lp=lp)
452 os.unlink(samdb_path)
454 samdb = SamDB(samdb_path, session_info=session_info,
455 credentials=credentials, lp=lp)
457 #Add modules to the list to activate them by default
458 #beware often order is important
460 # Some Known ordering constraints:
461 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
462 # - objectclass must be before password_hash, because password_hash checks
463 # that the objectclass is of type person (filled in by objectclass
464 # module when expanding the objectclass list)
465 # - partition must be last
466 # - each partition has its own module list then
467 modules_list = ["rootdse",
483 modules_list2 = ["show_deleted",
486 domaindn_ldb = "users.ldb"
487 if ldap_backend is not None:
488 domaindn_ldb = ldap_backend
489 configdn_ldb = "configuration.ldb"
490 if ldap_backend is not None:
491 configdn_ldb = ldap_backend
492 schemadn_ldb = "schema.ldb"
493 if ldap_backend is not None:
494 schema_ldb = ldap_backend
495 schemadn_ldb = ldap_backend
497 if ldap_backend_type == "fedora-ds":
498 backend_modules = ["nsuniqueid", "paged_searches"]
499 # We can handle linked attributes here, as we don't have directory-side subtree operations
500 tdb_modules_list = ["linked_attributes"]
501 elif ldap_backend_type == "openldap":
502 backend_modules = ["normalise", "entryuuid", "paged_searches"]
503 # OpenLDAP handles subtree renames, so we don't want to do any of these things
504 tdb_modules_list = None
505 elif serverrole == "domain controller":
506 backend_modules = ["repl_meta_data"]
508 backend_modules = ["objectguid"]
510 if tdb_modules_list is None:
511 tdb_modules_list_as_string = ""
513 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
515 samdb.transaction_start()
517 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
518 "SCHEMADN": names.schemadn,
519 "SCHEMADN_LDB": schemadn_ldb,
520 "SCHEMADN_MOD2": ",objectguid",
521 "CONFIGDN": names.configdn,
522 "CONFIGDN_LDB": configdn_ldb,
523 "DOMAINDN": names.domaindn,
524 "DOMAINDN_LDB": domaindn_ldb,
525 "SCHEMADN_MOD": "schema_fsmo,instancetype",
526 "CONFIGDN_MOD": "naming_fsmo,instancetype",
527 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
528 "MODULES_LIST": ",".join(modules_list),
529 "TDB_MODULES_LIST": tdb_modules_list_as_string,
530 "MODULES_LIST2": ",".join(modules_list2),
531 "BACKEND_MOD": ",".join(backend_modules),
535 samdb.transaction_cancel()
538 samdb.transaction_commit()
540 samdb = SamDB(samdb_path, session_info=session_info,
541 credentials=credentials, lp=lp)
543 samdb.transaction_start()
545 message("Setting up sam.ldb attributes")
546 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
548 message("Setting up sam.ldb rootDSE")
549 setup_samdb_rootdse(samdb, setup_path, names)
552 message("Erasing data from partitions")
553 samdb.erase_partitions()
556 samdb.transaction_cancel()
559 samdb.transaction_commit()
564 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
565 netbiosname, domainsid, keytab_path, samdb_url,
566 dns_keytab_path, dnspass, machinepass):
567 """Add DC-specific bits to a secrets database.
569 :param secretsdb: Ldb Handle to the secrets database
570 :param setup_path: Setup path function
571 :param machinepass: Machine password
573 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
574 "MACHINEPASS_B64": b64encode(machinepass),
577 "DNSDOMAIN": dnsdomain,
578 "DOMAINSID": str(domainsid),
579 "SECRETS_KEYTAB": keytab_path,
580 "NETBIOSNAME": netbiosname,
581 "SAM_LDB": samdb_url,
582 "DNS_KEYTAB": dns_keytab_path,
583 "DNSPASS_B64": b64encode(dnspass),
587 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
588 """Setup the secrets database.
590 :param path: Path to the secrets database.
591 :param setup_path: Get the path to a setup file.
592 :param session_info: Session info.
593 :param credentials: Credentials
594 :param lp: Loadparm context
595 :return: LDB handle for the created secrets database
597 if os.path.exists(path):
599 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
602 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
603 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
605 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
609 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
610 """Setup the templates database.
612 :param path: Path to the database.
613 :param setup_path: Function for obtaining the path to setup files.
614 :param session_info: Session info
615 :param credentials: Credentials
616 :param lp: Loadparm context
618 templates_ldb = SamDB(path, session_info=session_info,
619 credentials=credentials, lp=lp)
620 templates_ldb.erase()
621 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
624 def setup_registry(path, setup_path, session_info, credentials, lp):
625 """Setup the registry.
627 :param path: Path to the registry database
628 :param setup_path: Function that returns the path to a setup.
629 :param session_info: Session information
630 :param credentials: Credentials
631 :param lp: Loadparm context
633 reg = registry.Registry()
634 hive = registry.open_ldb(path, session_info=session_info,
635 credentials=credentials, lp_ctx=lp)
636 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
637 provision_reg = setup_path("provision.reg")
638 assert os.path.exists(provision_reg)
639 reg.diff_apply(provision_reg)
642 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
643 """Setup the idmap database.
645 :param path: path to the idmap database
646 :param setup_path: Function that returns a path to a setup file
647 :param session_info: Session information
648 :param credentials: Credentials
649 :param lp: Loadparm context
651 if os.path.exists(path):
654 idmap_ldb = IDmapDB(path, session_info=session_info,
655 credentials=credentials, lp=lp)
658 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
662 def setup_samdb_rootdse(samdb, setup_path, names):
663 """Setup the SamDB rootdse.
665 :param samdb: Sam Database handle
666 :param setup_path: Obtain setup path
668 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
669 "SCHEMADN": names.schemadn,
670 "NETBIOSNAME": names.netbiosname,
671 "DNSDOMAIN": names.dnsdomain,
672 "REALM": names.realm,
673 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
674 "DOMAINDN": names.domaindn,
675 "ROOTDN": names.rootdn,
676 "CONFIGDN": names.configdn,
677 "SERVERDN": names.serverdn,
681 def setup_self_join(samdb, names,
682 machinepass, dnspass,
683 domainsid, invocationid, setup_path,
685 """Join a host to its own domain."""
686 assert isinstance(invocationid, str)
687 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
688 "CONFIGDN": names.configdn,
689 "SCHEMADN": names.schemadn,
690 "DOMAINDN": names.domaindn,
691 "SERVERDN": names.serverdn,
692 "INVOCATIONID": invocationid,
693 "NETBIOSNAME": names.netbiosname,
694 "DEFAULTSITE": names.sitename,
695 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
696 "MACHINEPASS_B64": b64encode(machinepass),
697 "DNSPASS_B64": b64encode(dnspass),
698 "REALM": names.realm,
699 "DOMAIN": names.domain,
700 "DNSDOMAIN": names.dnsdomain})
701 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
702 "POLICYGUID": policyguid,
703 "DNSDOMAIN": names.dnsdomain,
704 "DOMAINSID": str(domainsid),
705 "DOMAINDN": names.domaindn})
708 def setup_samdb(path, setup_path, session_info, credentials, lp,
710 domainsid, aci, domainguid, policyguid,
711 fill, adminpass, krbtgtpass,
712 machinepass, invocationid, dnspass,
713 serverrole, ldap_backend=None,
714 ldap_backend_type=None):
715 """Setup a complete SAM Database.
717 :note: This will wipe the main SAM database file!
720 erase = (fill != FILL_DRS)
722 # Also wipes the database
723 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
724 credentials=credentials, session_info=session_info,
726 ldap_backend=ldap_backend, serverrole=serverrole,
727 ldap_backend_type=ldap_backend_type, erase=erase)
729 samdb = SamDB(path, session_info=session_info,
730 credentials=credentials, lp=lp)
733 # We want to finish here, but setup the index before we do so
734 message("Setting up sam.ldb index")
735 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
738 message("Pre-loading the Samba 4 and AD schema")
739 samdb.set_domain_sid(domainsid)
740 if serverrole == "domain controller":
741 samdb.set_invocation_id(invocationid)
743 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
744 names.configdn, names.sitename)
746 samdb.transaction_start()
749 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
750 if serverrole == "domain controller":
751 domain_oc = "domainDNS"
753 domain_oc = "samba4LocalDomain"
755 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
756 "DOMAINDN": names.domaindn,
758 "DOMAIN_OC": domain_oc
761 message("Modifying DomainDN: " + names.domaindn + "")
762 if domainguid is not None:
763 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
767 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
768 "LDAPTIME": timestring(int(time.time())),
769 "DOMAINSID": str(domainsid),
770 "SCHEMADN": names.schemadn,
771 "NETBIOSNAME": names.netbiosname,
772 "DEFAULTSITE": names.sitename,
773 "CONFIGDN": names.configdn,
774 "SERVERDN": names.serverdn,
775 "POLICYGUID": policyguid,
776 "DOMAINDN": names.domaindn,
777 "DOMAINGUID_MOD": domainguid_mod,
780 message("Adding configuration container (permitted to fail)")
781 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
782 "CONFIGDN": names.configdn,
784 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
786 message("Modifying configuration container")
787 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
788 "CONFIGDN": names.configdn,
789 "SCHEMADN": names.schemadn,
792 message("Adding schema container (permitted to fail)")
793 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
794 "SCHEMADN": names.schemadn,
796 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
798 message("Modifying schema container")
799 setup_modify_ldif(samdb,
800 setup_path("provision_schema_basedn_modify.ldif"), {
801 "SCHEMADN": names.schemadn,
802 "NETBIOSNAME": names.netbiosname,
803 "DEFAULTSITE": names.sitename,
804 "CONFIGDN": names.configdn,
805 "SERVERDN": names.serverdn
808 message("Setting up sam.ldb Samba4 schema")
809 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
810 {"SCHEMADN": names.schemadn })
811 message("Setting up sam.ldb AD schema")
812 setup_add_ldif(samdb, setup_path("schema.ldif"),
813 {"SCHEMADN": names.schemadn})
815 message("Setting up sam.ldb configuration data")
816 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
817 "CONFIGDN": names.configdn,
818 "NETBIOSNAME": names.netbiosname,
819 "DEFAULTSITE": names.sitename,
820 "DNSDOMAIN": names.dnsdomain,
821 "DOMAIN": names.domain,
822 "SCHEMADN": names.schemadn,
823 "DOMAINDN": names.domaindn,
824 "SERVERDN": names.serverdn
827 message("Setting up display specifiers")
828 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
829 {"CONFIGDN": names.configdn})
831 message("Adding users container (permitted to fail)")
832 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
833 "DOMAINDN": names.domaindn})
834 message("Modifying users container")
835 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
836 "DOMAINDN": names.domaindn})
837 message("Adding computers container (permitted to fail)")
838 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
839 "DOMAINDN": names.domaindn})
840 message("Modifying computers container")
841 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
842 "DOMAINDN": names.domaindn})
843 message("Setting up sam.ldb data")
844 setup_add_ldif(samdb, setup_path("provision.ldif"), {
845 "DOMAINDN": names.domaindn,
846 "NETBIOSNAME": names.netbiosname,
847 "DEFAULTSITE": names.sitename,
848 "CONFIGDN": names.configdn,
849 "SERVERDN": names.serverdn
852 if fill == FILL_FULL:
853 message("Setting up sam.ldb users and groups")
854 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
855 "DOMAINDN": names.domaindn,
856 "DOMAINSID": str(domainsid),
857 "CONFIGDN": names.configdn,
858 "ADMINPASS_B64": b64encode(adminpass),
859 "KRBTGTPASS_B64": b64encode(krbtgtpass),
862 if serverrole == "domain controller":
863 message("Setting up self join")
864 setup_self_join(samdb, names=names, invocationid=invocationid,
866 machinepass=machinepass,
867 domainsid=domainsid, policyguid=policyguid,
868 setup_path=setup_path)
870 #We want to setup the index last, as adds are faster unindexed
871 message("Setting up sam.ldb index")
872 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
874 samdb.transaction_cancel()
877 samdb.transaction_commit()
882 FILL_NT4SYNC = "NT4SYNC"
885 def provision(setup_dir, message, session_info,
886 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
887 rootdn=None, domaindn=None, schemadn=None, configdn=None,
889 domain=None, hostname=None, hostip=None, hostip6=None,
890 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
891 policyguid=None, invocationid=None, machinepass=None,
892 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
893 wheel=None, backup=None, aci=None, serverrole=None,
894 ldap_backend=None, ldap_backend_type=None, sitename=None):
897 :note: caution, this wipes all existing data!
900 def setup_path(file):
901 return os.path.join(setup_dir, file)
903 if domainsid is None:
904 domainsid = security.random_sid()
906 domainsid = security.Sid(domainsid)
908 if policyguid is None:
909 policyguid = str(uuid.uuid4())
910 if adminpass is None:
911 adminpass = misc.random_password(12)
912 if krbtgtpass is None:
913 krbtgtpass = misc.random_password(12)
914 if machinepass is None:
915 machinepass = misc.random_password(12)
917 dnspass = misc.random_password(12)
918 root_uid = findnss_uid([root or "root"])
919 nobody_uid = findnss_uid([nobody or "nobody"])
920 users_gid = findnss_gid([users or "users"])
922 wheel_gid = findnss_gid(["wheel", "adm"])
924 wheel_gid = findnss_gid([wheel])
926 aci = "# no aci for local ldb"
929 os.makedirs(os.path.join(targetdir, "etc"))
930 smbconf = os.path.join(targetdir, "etc", "smb.conf")
932 # only install a new smb.conf if there isn't one there already
933 if not os.path.exists(smbconf):
934 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
937 lp = param.LoadParm()
940 names = guess_names(lp=lp, hostname=hostname, domain=domain,
941 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
942 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
945 paths = provision_paths_from_lp(lp, names.dnsdomain)
948 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
952 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
953 except socket.gaierror:
956 if serverrole is None:
957 serverrole = lp.get("server role")
959 assert serverrole in ("domain controller", "member server", "standalone")
960 if invocationid is None and serverrole == "domain controller":
961 invocationid = str(uuid.uuid4())
963 if not os.path.exists(paths.private_dir):
964 os.mkdir(paths.private_dir)
966 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
968 if ldap_backend is not None:
969 if ldap_backend == "ldapi":
970 # provision-backend will set this path suggested slapd command line / fedorads.inf
971 ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
973 # only install a new shares config db if there is none
974 if not os.path.exists(paths.shareconf):
975 message("Setting up share.ldb")
976 share_ldb = Ldb(paths.shareconf, session_info=session_info,
977 credentials=credentials, lp=lp)
978 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
981 message("Setting up secrets.ldb")
982 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
983 session_info=session_info,
984 credentials=credentials, lp=lp)
986 message("Setting up the registry")
987 setup_registry(paths.hklm, setup_path, session_info,
988 credentials=credentials, lp=lp)
990 message("Setting up templates db")
991 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
992 credentials=credentials, lp=lp)
994 message("Setting up idmap db")
995 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
996 credentials=credentials, lp=lp)
998 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
999 credentials=credentials, lp=lp, names=names,
1001 domainsid=domainsid,
1002 aci=aci, domainguid=domainguid, policyguid=policyguid,
1004 adminpass=adminpass, krbtgtpass=krbtgtpass,
1005 invocationid=invocationid,
1006 machinepass=machinepass, dnspass=dnspass,
1007 serverrole=serverrole, ldap_backend=ldap_backend,
1008 ldap_backend_type=ldap_backend_type)
1010 if lp.get("server role") == "domain controller":
1011 if paths.netlogon is None:
1012 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1013 message("Please either remove %s or see the template at %s" %
1014 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1015 assert(paths.netlogon is not None)
1017 if paths.sysvol is None:
1018 message("Existing smb.conf does not have a [sysvol] 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.sysvol is not None)
1023 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1024 "{" + policyguid + "}")
1025 os.makedirs(policy_path, 0755)
1026 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1027 os.makedirs(os.path.join(policy_path, "User"), 0755)
1028 if not os.path.isdir(paths.netlogon):
1029 os.makedirs(paths.netlogon, 0755)
1031 if samdb_fill == FILL_FULL:
1032 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1033 root_uid=root_uid, nobody_uid=nobody_uid,
1034 users_gid=users_gid, wheel_gid=wheel_gid)
1036 message("Setting up sam.ldb rootDSE marking as synchronized")
1037 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1039 # Only make a zone file on the first DC, it should be replicated with DNS replication
1040 if serverrole == "domain controller":
1041 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1042 credentials=credentials, lp=lp)
1043 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1044 netbiosname=names.netbiosname, domainsid=domainsid,
1045 keytab_path=paths.keytab, samdb_url=paths.samdb,
1046 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1047 machinepass=machinepass, dnsdomain=names.dnsdomain)
1049 samdb = SamDB(paths.samdb, session_info=session_info,
1050 credentials=credentials, lp=lp)
1052 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1053 assert isinstance(domainguid, str)
1054 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1055 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1056 scope=SCOPE_SUBTREE)
1057 assert isinstance(hostguid, str)
1059 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1060 domaindn=names.domaindn, hostip=hostip,
1061 hostip6=hostip6, hostname=names.hostname,
1062 dnspass=dnspass, realm=names.realm,
1063 domainguid=domainguid, hostguid=hostguid)
1064 message("Please install the zone located in %s into your DNS server" % paths.dns)
1066 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1067 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1068 keytab_name=paths.dns_keytab)
1069 message("See %s for example configuration statements for secure GSS-TSIG updates" % paths.namedconf)
1071 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1072 hostname=names.hostname, realm=names.realm)
1073 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1075 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1078 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1080 message("Once the above files are installed, your Samba4 server will be ready to use")
1081 message("Server Role: %s" % serverrole)
1082 message("Hostname: %s" % names.hostname)
1083 message("NetBIOS Domain: %s" % names.domain)
1084 message("DNS Domain: %s" % names.dnsdomain)
1085 message("DOMAIN SID: %s" % str(domainsid))
1086 message("Admin password: %s" % adminpass)
1088 result = ProvisionResult()
1089 result.domaindn = domaindn
1090 result.paths = paths
1092 result.samdb = samdb
1096 def provision_become_dc(setup_dir=None,
1097 smbconf=None, targetdir=None, realm=None,
1098 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1100 domain=None, hostname=None, domainsid=None,
1101 adminpass=None, krbtgtpass=None, domainguid=None,
1102 policyguid=None, invocationid=None, machinepass=None,
1103 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1104 wheel=None, backup=None, aci=None, serverrole=None,
1105 ldap_backend=None, ldap_backend_type=None, sitename=None):
1108 """print a message if quiet is not set."""
1111 return provision(setup_dir, message, system_session(), None,
1112 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1113 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1114 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1117 def setup_db_config(setup_path, dbdir):
1118 """Setup a Berkeley database.
1120 :param setup_path: Setup path function.
1121 :param dbdir: Database directory."""
1122 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1123 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1124 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1125 os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1127 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1128 {"LDAPDBDIR": dbdir})
1132 def provision_backend(setup_dir=None, message=None,
1133 smbconf=None, targetdir=None, realm=None,
1134 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1135 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1136 ldap_backend_type=None, ldap_backend_port=None):
1138 def setup_path(file):
1139 return os.path.join(setup_dir, file)
1141 if hostname is None:
1142 hostname = socket.gethostname().split(".")[0].lower()
1145 root = findnss(pwd.getpwnam, ["root"])[0]
1148 os.makedirs(os.path.join(targetdir, "etc"))
1149 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1151 # only install a new smb.conf if there isn't one there already
1152 if not os.path.exists(smbconf):
1153 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1154 serverrole, targetdir)
1156 lp = param.LoadParm()
1159 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1160 dnsdomain=realm, serverrole=serverrole,
1161 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1164 paths = provision_paths_from_lp(lp, names.dnsdomain)
1166 if not os.path.isdir(paths.ldapdir):
1167 os.makedirs(paths.ldapdir)
1168 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1170 os.unlink(schemadb_path)
1174 schemadb = Ldb(schemadb_path, lp=lp)
1176 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1177 {"SCHEMADN": names.schemadn,
1179 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1181 setup_modify_ldif(schemadb,
1182 setup_path("provision_schema_basedn_modify.ldif"), \
1183 {"SCHEMADN": names.schemadn,
1184 "NETBIOSNAME": names.netbiosname,
1185 "DEFAULTSITE": DEFAULTSITE,
1186 "CONFIGDN": names.configdn,
1187 "SERVERDN": names.serverdn
1190 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1191 {"SCHEMADN": names.schemadn })
1192 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1193 {"SCHEMADN": names.schemadn})
1195 if ldap_backend_type == "fedora-ds":
1196 if ldap_backend_port is not None:
1197 serverport = "ServerPort=%d" % ldap_backend_port
1201 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1203 "HOSTNAME": hostname,
1204 "DNSDOMAIN": names.dnsdomain,
1205 "LDAPDIR": paths.ldapdir,
1206 "DOMAINDN": names.domaindn,
1207 "LDAPMANAGERDN": names.ldapmanagerdn,
1208 "LDAPMANAGERPASS": adminpass,
1209 "SERVERPORT": serverport})
1211 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1212 {"CONFIGDN": names.configdn,
1213 "SCHEMADN": names.schemadn,
1216 mapping = "schema-map-fedora-ds-1.0"
1217 backend_schema = "99_ad.ldif"
1219 slapdcommand="Initailise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1221 elif ldap_backend_type == "openldap":
1222 attrs = ["linkID", "lDAPDisplayName"]
1223 res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1225 memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1226 refint_attributes = "";
1227 for i in range (0, len(res)):
1228 linkid = res[i]["linkID"][0]
1229 linkid = str(int(linkid) + 1)
1230 expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1231 target = schemadb.searchone(basedn=names.schemadn,
1232 expression=expression,
1233 attribute="lDAPDisplayName",
1234 scope=SCOPE_SUBTREE);
1235 if target is not None:
1236 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1237 memberof_config = memberof_config + """overlay memberof
1238 memberof-dangling error
1239 memberof-refint TRUE
1240 memberof-group-oc top
1241 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1242 memberof-memberof-ad """ + target + """
1243 memberof-dangling-error 32
1247 memberof_config = memberof_config + """
1249 refint_attributes""" + refint_attributes + "\n";
1251 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1252 {"DNSDOMAIN": names.dnsdomain,
1253 "LDAPDIR": paths.ldapdir,
1254 "DOMAINDN": names.domaindn,
1255 "CONFIGDN": names.configdn,
1256 "SCHEMADN": names.schemadn,
1257 "LDAPMANAGERDN": names.ldapmanagerdn,
1258 "LDAPMANAGERPASS": adminpass,
1259 "MEMBEROF_CONFIG": memberof_config})
1260 setup_file(setup_path("modules.conf"), paths.modulesconf,
1261 {"REALM": names.realm})
1263 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "user")))
1264 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "config")))
1265 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "schema")))
1266 mapping = "schema-map-openldap-2.3"
1267 backend_schema = "backend-schema.schema"
1269 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1270 if ldap_backend_port is not None:
1271 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1273 server_port_string = ""
1274 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1276 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);
1278 os.system(schema_command)
1281 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ( ldap_backend_type) )
1282 message("Server Role: %s" % serverrole)
1283 message("Hostname: %s" % names.hostname)
1284 message("DNS Domain: %s" % names.dnsdomain)
1285 message("Base DN: %s" % names.domaindn)
1286 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1287 message("LDAP admin password: %s" % adminpass)
1288 message(slapdcommand)
1291 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1292 """Create a PHP LDAP admin configuration file.
1294 :param path: Path to write the configuration to.
1295 :param setup_path: Function to generate setup paths.
1297 setup_file(setup_path("phpldapadmin-config.php"), path,
1298 {"S4_LDAPI_URI": ldapi_uri})
1301 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1302 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1303 """Write out a DNS zone file, from the info in the current database.
1305 :param path: Path of the new zone file.
1306 :param setup_path: Setup path function.
1307 :param dnsdomain: DNS Domain name
1308 :param domaindn: DN of the Domain
1309 :param hostip: Local IPv4 IP
1310 :param hostip6: Local IPv6 IP
1311 :param hostname: Local hostname
1312 :param dnspass: Password for DNS
1313 :param realm: Realm name
1314 :param domainguid: GUID of the domain.
1315 :param hostguid: GUID of the host.
1317 assert isinstance(domainguid, str)
1319 hostip6_base_line = ""
1320 hostip6_host_line = ""
1322 if hostip6 is not None:
1323 hostip6_base_line = " IN AAAA " + hostip6
1324 hostip6_host_line = hostname + " IN AAAA " + hostip6
1326 setup_file(setup_path("provision.zone"), path, {
1327 "DNSPASS_B64": b64encode(dnspass),
1328 "HOSTNAME": hostname,
1329 "DNSDOMAIN": dnsdomain,
1332 "DOMAINGUID": domainguid,
1333 "DATESTRING": time.strftime("%Y%m%d%H"),
1334 "DEFAULTSITE": DEFAULTSITE,
1335 "HOSTGUID": hostguid,
1336 "HOSTIP6_BASE_LINE": hostip6_base_line,
1337 "HOSTIP6_HOST_LINE": hostip6_host_line,
1340 def create_named_conf(path, setup_path, realm, dnsdomain,
1341 private_dir, keytab_name):
1342 """Write out a file containing zone statements suitable for inclusion in a
1343 named.conf file (including GSS-TSIG configuration).
1345 :param path: Path of the new named.conf file.
1346 :param setup_path: Setup path function.
1347 :param realm: Realm name
1348 :param dnsdomain: DNS Domain name
1349 :param private_dir: Path to private directory
1350 :param keytab_name: File name of DNS keytab file
1353 setup_file(setup_path("named.conf"), path, {
1354 "DNSDOMAIN": dnsdomain,
1356 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1357 "DNS_KEYTAB": keytab_name,
1358 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1361 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1362 """Write out a file containing zone statements suitable for inclusion in a
1363 named.conf file (including GSS-TSIG configuration).
1365 :param path: Path of the new named.conf file.
1366 :param setup_path: Setup path function.
1367 :param dnsdomain: DNS Domain name
1368 :param hostname: Local hostname
1369 :param realm: Realm name
1372 setup_file(setup_path("krb5.conf"), path, {
1373 "DNSDOMAIN": dnsdomain,
1374 "HOSTNAME": hostname,
1378 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1379 """Load schema for the SamDB.
1381 :param samdb: Load a schema into a SamDB.
1382 :param setup_path: Setup path function.
1383 :param schemadn: DN of the schema
1384 :param netbiosname: NetBIOS name of the host.
1385 :param configdn: DN of the configuration
1387 schema_data = open(setup_path("schema.ldif"), 'r').read()
1388 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1389 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1390 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1391 head_data = substitute_var(head_data, {
1392 "SCHEMADN": schemadn,
1393 "NETBIOSNAME": netbiosname,
1394 "CONFIGDN": configdn,
1395 "DEFAULTSITE":sitename
1397 samdb.attach_schema_from_ldif(head_data, schema_data)