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 Error("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 def open_ldb(session_info, credentials, lp, dbname):
132 """Open a LDB, thrashing it if it is corrupt.
134 :param session_info: auth session information
135 :param credentials: credentials
136 :param lp: Loadparm context
137 :param dbname: Path of the database to open.
138 :return: a Ldb object
140 assert session_info is not None
142 return Ldb(dbname, session_info=session_info, credentials=credentials,
147 return Ldb(dbname, session_info=session_info, credentials=credentials,
151 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
152 """Setup a ldb in the private dir.
154 :param ldb: LDB file to import data into
155 :param ldif_path: Path of the LDIF file to load
156 :param subst_vars: Optional variables to subsitute in LDIF.
158 assert isinstance(ldif_path, str)
160 data = open(ldif_path, 'r').read()
161 if subst_vars is not None:
162 data = substitute_var(data, subst_vars)
164 check_all_substituted(data)
169 def setup_modify_ldif(ldb, ldif_path, substvars=None):
170 """Modify a ldb in the private dir.
172 :param ldb: LDB object.
173 :param ldif_path: LDIF file path.
174 :param substvars: Optional dictionary with substitution variables.
176 data = open(ldif_path, 'r').read()
177 if substvars is not None:
178 data = substitute_var(data, substvars)
180 check_all_substituted(data)
182 ldb.modify_ldif(data)
185 def setup_ldb(ldb, ldif_path, subst_vars):
186 """Import a LDIF a file into a LDB handle, optionally substituting variables.
188 :note: Either all LDIF data will be added or none (using transactions).
190 :param ldb: LDB file to import into.
191 :param ldif_path: Path to the LDIF file.
192 :param subst_vars: Dictionary with substitution variables.
194 assert ldb is not None
195 ldb.transaction_start()
197 setup_add_ldif(ldb, ldif_path, subst_vars)
199 ldb.transaction_cancel()
201 ldb.transaction_commit()
204 def setup_file(template, fname, substvars):
205 """Setup a file in the private dir.
207 :param template: Path of the template file.
208 :param fname: Path of the file to create.
209 :param substvars: Substitution variables.
213 if os.path.exists(f):
216 data = open(template, 'r').read()
218 data = substitute_var(data, substvars)
219 check_all_substituted(data)
221 open(f, 'w').write(data)
224 def provision_paths_from_lp(lp, dnsdomain):
225 """Set the default paths for provisioning.
227 :param lp: Loadparm context.
228 :param dnsdomain: DNS Domain name
230 paths = ProvisionPaths()
231 paths.private_dir = lp.get("private dir")
232 paths.keytab = "secrets.keytab"
233 paths.dns_keytab = "dns.keytab"
235 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
236 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
237 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
238 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
239 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
240 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
241 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
242 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
243 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
244 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
245 paths.phpldapadminconfig = os.path.join(paths.private_dir,
246 "phpldapadmin-config.php")
247 paths.ldapdir = os.path.join(paths.private_dir,
249 paths.slapdconf = os.path.join(paths.ldapdir,
251 paths.modulesconf = os.path.join(paths.ldapdir,
253 paths.memberofconf = os.path.join(paths.ldapdir,
255 paths.fedoradsinf = os.path.join(paths.ldapdir,
257 paths.fedoradspartitions = os.path.join(paths.ldapdir,
258 "fedorads-partitions.ldif")
259 paths.hklm = "hklm.ldb"
260 paths.hkcr = "hkcr.ldb"
261 paths.hkcu = "hkcu.ldb"
262 paths.hku = "hku.ldb"
263 paths.hkpd = "hkpd.ldb"
264 paths.hkpt = "hkpt.ldb"
266 paths.sysvol = lp.get("path", "sysvol")
268 paths.netlogon = lp.get("path", "netlogon")
270 paths.smbconf = lp.configfile()
275 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
276 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
278 """Guess configuration settings to use."""
281 hostname = socket.gethostname().split(".")[0].lower()
283 netbiosname = hostname.upper()
284 if not valid_netbios_name(netbiosname):
285 raise InvalidNetbiosName(netbiosname)
287 hostname = hostname.lower()
289 if dnsdomain is None:
290 dnsdomain = lp.get("realm")
292 if serverrole is None:
293 serverrole = lp.get("server role")
295 assert dnsdomain is not None
296 realm = dnsdomain.upper()
298 if lp.get("realm").upper() != realm:
299 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
300 (lp.get("realm"), lp.configfile(), realm))
302 dnsdomain = dnsdomain.lower()
304 if (serverrole == "domain controller"):
306 domain = lp.get("workgroup")
308 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
309 if lp.get("workgroup").upper() != domain.upper():
310 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
311 lp.get("workgroup"), domain)
315 domaindn = "CN=" + netbiosname
317 assert domain is not None
318 domain = domain.upper()
319 if not valid_netbios_name(domain):
320 raise InvalidNetbiosName(domain)
326 configdn = "CN=Configuration," + rootdn
328 schemadn = "CN=Schema," + configdn
333 names = ProvisionNames()
334 names.rootdn = rootdn
335 names.domaindn = domaindn
336 names.configdn = configdn
337 names.schemadn = schemadn
338 names.ldapmanagerdn = "CN=Manager," + rootdn
339 names.dnsdomain = dnsdomain
340 names.domain = domain
342 names.netbiosname = netbiosname
343 names.hostname = hostname
344 names.sitename = sitename
345 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
350 def load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir):
351 if targetdir is not None:
352 if not os.path.exists(targetdir):
354 if not os.path.exists(os.path.join(targetdir, "etc")):
355 os.mkdir(os.path.join(targetdir, "etc"))
357 smbconf = os.path.join(targetdir, "etc", "smb.conf")
359 # only install a new smb.conf if there isn't one there already
361 if not os.path.exists(smbconf):
363 hostname = socket.gethostname().split(".")[0].lower()
365 if serverrole is None:
366 serverrole = "standalone"
368 assert serverrole in ("domain controller", "member server", "standalone")
369 if serverrole == "domain controller":
371 elif serverrole == "member server":
372 smbconfsuffix = "member"
373 elif serverrole == "standalone":
374 smbconfsuffix = "standalone"
376 assert domain is not None
377 assert realm is not None
379 default_lp = param.LoadParm()
380 #Load non-existant file
381 default_lp.load(smbconf)
383 if targetdir is not None:
384 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
385 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
387 default_lp.set("lock dir", os.path.abspath(targetdir))
392 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
393 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
395 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
397 "HOSTNAME": hostname,
400 "SERVERROLE": serverrole,
401 "NETLOGONPATH": netlogon,
402 "SYSVOLPATH": sysvol,
403 "PRIVATEDIR_LINE": privatedir_line,
404 "LOCKDIR_LINE": lockdir_line
407 lp = param.LoadParm()
413 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
414 users_gid, wheel_gid):
415 """setup reasonable name mappings for sam names to unix names.
417 :param samdb: SamDB object.
418 :param idmap: IDmap db object.
419 :param sid: The domain sid.
420 :param domaindn: The domain DN.
421 :param root_uid: uid of the UNIX root user.
422 :param nobody_uid: uid of the UNIX nobody user.
423 :param users_gid: gid of the UNIX users group.
424 :param wheel_gid: gid of the UNIX wheel group."""
425 # add some foreign sids if they are not present already
426 samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
427 samdb.add_foreign(domaindn, "S-1-1-0", "World")
428 samdb.add_foreign(domaindn, "S-1-5-2", "Network")
429 samdb.add_foreign(domaindn, "S-1-5-18", "System")
430 samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
432 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
433 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
435 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
436 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
439 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
441 serverrole, ldap_backend=None,
442 ldap_backend_type=None, erase=False):
443 """Setup the partitions for the SAM database.
445 Alternatively, provision() may call this, and then populate the database.
447 :note: This will wipe the Sam Database!
449 :note: This function always removes the local SAM LDB file. The erase
450 parameter controls whether to erase the existing data, which
451 may not be stored locally but in LDAP.
453 assert session_info is not None
455 samdb = SamDB(samdb_path, session_info=session_info,
456 credentials=credentials, lp=lp)
462 os.unlink(samdb_path)
464 samdb = SamDB(samdb_path, session_info=session_info,
465 credentials=credentials, lp=lp)
467 #Add modules to the list to activate them by default
468 #beware often order is important
470 # Some Known ordering constraints:
471 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
472 # - objectclass must be before password_hash, because password_hash checks
473 # that the objectclass is of type person (filled in by objectclass
474 # module when expanding the objectclass list)
475 # - partition must be last
476 # - each partition has its own module list then
477 modules_list = ["rootdse",
493 modules_list2 = ["show_deleted",
496 domaindn_ldb = "users.ldb"
497 if ldap_backend is not None:
498 domaindn_ldb = ldap_backend
499 configdn_ldb = "configuration.ldb"
500 if ldap_backend is not None:
501 configdn_ldb = ldap_backend
502 schemadn_ldb = "schema.ldb"
503 if ldap_backend is not None:
504 schema_ldb = ldap_backend
505 schemadn_ldb = ldap_backend
507 if ldap_backend_type == "fedora-ds":
508 backend_modules = ["nsuniqueid", "paged_searches"]
509 # We can handle linked attributes here, as we don't have directory-side subtree operations
510 tdb_modules_list = ["linked_attributes"]
511 elif ldap_backend_type == "openldap":
512 backend_modules = ["normalise", "entryuuid", "paged_searches"]
513 # OpenLDAP handles subtree renames, so we don't want to do any of these things
514 tdb_modules_list = None
515 elif serverrole == "domain controller":
516 backend_modules = ["repl_meta_data"]
518 backend_modules = ["objectguid"]
520 if tdb_modules_list is None:
521 tdb_modules_list_as_string = ""
523 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
525 samdb.transaction_start()
527 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
528 "SCHEMADN": names.schemadn,
529 "SCHEMADN_LDB": schemadn_ldb,
530 "SCHEMADN_MOD2": ",objectguid",
531 "CONFIGDN": names.configdn,
532 "CONFIGDN_LDB": configdn_ldb,
533 "DOMAINDN": names.domaindn,
534 "DOMAINDN_LDB": domaindn_ldb,
535 "SCHEMADN_MOD": "schema_fsmo,instancetype",
536 "CONFIGDN_MOD": "naming_fsmo,instancetype",
537 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
538 "MODULES_LIST": ",".join(modules_list),
539 "TDB_MODULES_LIST": tdb_modules_list_as_string,
540 "MODULES_LIST2": ",".join(modules_list2),
541 "BACKEND_MOD": ",".join(backend_modules),
545 samdb.transaction_cancel()
548 samdb.transaction_commit()
550 samdb = SamDB(samdb_path, session_info=session_info,
551 credentials=credentials, lp=lp)
553 samdb.transaction_start()
555 message("Setting up sam.ldb attributes")
556 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
558 message("Setting up sam.ldb rootDSE")
559 setup_samdb_rootdse(samdb, setup_path, names)
562 message("Erasing data from partitions")
563 samdb.erase_partitions()
566 samdb.transaction_cancel()
569 samdb.transaction_commit()
574 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
575 netbiosname, domainsid, keytab_path, samdb_url,
576 dns_keytab_path, dnspass, machinepass):
577 """Add DC-specific bits to a secrets database.
579 :param secretsdb: Ldb Handle to the secrets database
580 :param setup_path: Setup path function
581 :param machinepass: Machine password
583 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
584 "MACHINEPASS_B64": b64encode(machinepass),
587 "DNSDOMAIN": dnsdomain,
588 "DOMAINSID": str(domainsid),
589 "SECRETS_KEYTAB": keytab_path,
590 "NETBIOSNAME": netbiosname,
591 "SAM_LDB": samdb_url,
592 "DNS_KEYTAB": dns_keytab_path,
593 "DNSPASS_B64": b64encode(dnspass),
597 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
598 """Setup the secrets database.
600 :param path: Path to the secrets database.
601 :param setup_path: Get the path to a setup file.
602 :param session_info: Session info.
603 :param credentials: Credentials
604 :param lp: Loadparm context
605 :return: LDB handle for the created secrets database
607 if os.path.exists(path):
609 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
612 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
613 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
615 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
619 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
620 """Setup the templates database.
622 :param path: Path to the database.
623 :param setup_path: Function for obtaining the path to setup files.
624 :param session_info: Session info
625 :param credentials: Credentials
626 :param lp: Loadparm context
628 templates_ldb = SamDB(path, session_info=session_info,
629 credentials=credentials, lp=lp)
630 templates_ldb.erase()
631 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
634 def setup_registry(path, setup_path, session_info, credentials, lp):
635 """Setup the registry.
637 :param path: Path to the registry database
638 :param setup_path: Function that returns the path to a setup.
639 :param session_info: Session information
640 :param credentials: Credentials
641 :param lp: Loadparm context
643 reg = registry.Registry()
644 hive = registry.open_ldb(path, session_info=session_info,
645 credentials=credentials, lp_ctx=lp)
646 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
647 provision_reg = setup_path("provision.reg")
648 assert os.path.exists(provision_reg)
649 reg.diff_apply(provision_reg)
652 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
653 """Setup the idmap database.
655 :param path: path to the idmap database
656 :param setup_path: Function that returns a path to a setup file
657 :param session_info: Session information
658 :param credentials: Credentials
659 :param lp: Loadparm context
661 if os.path.exists(path):
664 idmap_ldb = IDmapDB(path, session_info=session_info,
665 credentials=credentials, lp=lp)
668 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
672 def setup_samdb_rootdse(samdb, setup_path, names):
673 """Setup the SamDB rootdse.
675 :param samdb: Sam Database handle
676 :param setup_path: Obtain setup path
678 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
679 "SCHEMADN": names.schemadn,
680 "NETBIOSNAME": names.netbiosname,
681 "DNSDOMAIN": names.dnsdomain,
682 "REALM": names.realm,
683 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
684 "DOMAINDN": names.domaindn,
685 "ROOTDN": names.rootdn,
686 "CONFIGDN": names.configdn,
687 "SERVERDN": names.serverdn,
691 def setup_self_join(samdb, names,
692 machinepass, dnspass,
693 domainsid, invocationid, setup_path,
695 """Join a host to its own domain."""
696 assert isinstance(invocationid, str)
697 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
698 "CONFIGDN": names.configdn,
699 "SCHEMADN": names.schemadn,
700 "DOMAINDN": names.domaindn,
701 "SERVERDN": names.serverdn,
702 "INVOCATIONID": invocationid,
703 "NETBIOSNAME": names.netbiosname,
704 "DEFAULTSITE": names.sitename,
705 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
706 "MACHINEPASS_B64": b64encode(machinepass),
707 "DNSPASS_B64": b64encode(dnspass),
708 "REALM": names.realm,
709 "DOMAIN": names.domain,
710 "DNSDOMAIN": names.dnsdomain})
711 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
712 "POLICYGUID": policyguid,
713 "DNSDOMAIN": names.dnsdomain,
714 "DOMAINSID": str(domainsid),
715 "DOMAINDN": names.domaindn})
718 def setup_samdb(path, setup_path, session_info, credentials, lp,
720 domainsid, aci, domainguid, policyguid,
721 fill, adminpass, krbtgtpass,
722 machinepass, invocationid, dnspass,
723 serverrole, ldap_backend=None,
724 ldap_backend_type=None):
725 """Setup a complete SAM Database.
727 :note: This will wipe the main SAM database file!
730 erase = (fill != FILL_DRS)
732 # Also wipes the database
733 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
734 credentials=credentials, session_info=session_info,
736 ldap_backend=ldap_backend, serverrole=serverrole,
737 ldap_backend_type=ldap_backend_type, erase=erase)
739 samdb = SamDB(path, session_info=session_info,
740 credentials=credentials, lp=lp)
743 # We want to finish here, but setup the index before we do so
744 message("Setting up sam.ldb index")
745 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
748 message("Pre-loading the Samba 4 and AD schema")
749 samdb.set_domain_sid(domainsid)
750 if serverrole == "domain controller":
751 samdb.set_invocation_id(invocationid)
753 load_schema(setup_path, samdb, names.schemadn, names.netbiosname, names.configdn, names.sitename)
755 samdb.transaction_start()
758 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
759 if serverrole == "domain controller":
760 domain_oc = "domainDNS"
762 domain_oc = "samba4LocalDomain"
764 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
765 "DOMAINDN": names.domaindn,
767 "DOMAIN_OC": domain_oc
770 message("Modifying DomainDN: " + names.domaindn + "")
771 if domainguid is not None:
772 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
776 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
777 "LDAPTIME": timestring(int(time.time())),
778 "DOMAINSID": str(domainsid),
779 "SCHEMADN": names.schemadn,
780 "NETBIOSNAME": names.netbiosname,
781 "DEFAULTSITE": names.sitename,
782 "CONFIGDN": names.configdn,
783 "SERVERDN": names.serverdn,
784 "POLICYGUID": policyguid,
785 "DOMAINDN": names.domaindn,
786 "DOMAINGUID_MOD": domainguid_mod,
789 message("Adding configuration container (permitted to fail)")
790 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
791 "CONFIGDN": names.configdn,
793 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
795 message("Modifying configuration container")
796 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
797 "CONFIGDN": names.configdn,
798 "SCHEMADN": names.schemadn,
801 message("Adding schema container (permitted to fail)")
802 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
803 "SCHEMADN": names.schemadn,
805 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
807 message("Modifying schema container")
808 setup_modify_ldif(samdb,
809 setup_path("provision_schema_basedn_modify.ldif"), {
810 "SCHEMADN": names.schemadn,
811 "NETBIOSNAME": names.netbiosname,
812 "DEFAULTSITE": names.sitename,
813 "CONFIGDN": names.configdn,
814 "SERVERDN": names.serverdn
817 message("Setting up sam.ldb Samba4 schema")
818 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
819 {"SCHEMADN": names.schemadn })
820 message("Setting up sam.ldb AD schema")
821 setup_add_ldif(samdb, setup_path("schema.ldif"),
822 {"SCHEMADN": names.schemadn})
824 message("Setting up sam.ldb configuration data")
825 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
826 "CONFIGDN": names.configdn,
827 "NETBIOSNAME": names.netbiosname,
828 "DEFAULTSITE": names.sitename,
829 "DNSDOMAIN": names.dnsdomain,
830 "DOMAIN": names.domain,
831 "SCHEMADN": names.schemadn,
832 "DOMAINDN": names.domaindn,
833 "SERVERDN": names.serverdn
836 message("Setting up display specifiers")
837 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
838 {"CONFIGDN": names.configdn})
840 message("Adding users container (permitted to fail)")
841 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
842 "DOMAINDN": names.domaindn})
843 message("Modifying users container")
844 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
845 "DOMAINDN": names.domaindn})
846 message("Adding computers container (permitted to fail)")
847 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
848 "DOMAINDN": names.domaindn})
849 message("Modifying computers container")
850 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
851 "DOMAINDN": names.domaindn})
852 message("Setting up sam.ldb data")
853 setup_add_ldif(samdb, setup_path("provision.ldif"), {
854 "DOMAINDN": names.domaindn,
855 "NETBIOSNAME": names.netbiosname,
856 "DEFAULTSITE": names.sitename,
857 "CONFIGDN": names.configdn,
858 "SERVERDN": names.serverdn
861 if fill == FILL_FULL:
862 message("Setting up sam.ldb users and groups")
863 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
864 "DOMAINDN": names.domaindn,
865 "DOMAINSID": str(domainsid),
866 "CONFIGDN": names.configdn,
867 "ADMINPASS_B64": b64encode(adminpass),
868 "KRBTGTPASS_B64": b64encode(krbtgtpass),
871 if serverrole == "domain controller":
872 message("Setting up self join")
873 setup_self_join(samdb, names=names, invocationid=invocationid,
875 machinepass=machinepass,
876 domainsid=domainsid, policyguid=policyguid,
877 setup_path=setup_path)
879 #We want to setup the index last, as adds are faster unindexed
880 message("Setting up sam.ldb index")
881 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
883 samdb.transaction_cancel()
886 samdb.transaction_commit()
891 FILL_NT4SYNC = "NT4SYNC"
894 def provision(setup_dir, message, session_info,
895 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
896 rootdn=None, domaindn=None, schemadn=None, configdn=None,
898 domain=None, hostname=None, hostip=None, hostip6=None,
899 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
900 policyguid=None, invocationid=None, machinepass=None,
901 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
902 wheel=None, backup=None, aci=None, serverrole=None,
903 ldap_backend=None, ldap_backend_type=None, sitename=None):
906 :note: caution, this wipes all existing data!
909 def setup_path(file):
910 return os.path.join(setup_dir, file)
912 if domainsid is None:
913 domainsid = security.random_sid()
915 domainsid = security.Sid(domainsid)
917 if policyguid is None:
918 policyguid = str(uuid.uuid4())
919 if adminpass is None:
920 adminpass = misc.random_password(12)
921 if krbtgtpass is None:
922 krbtgtpass = misc.random_password(12)
923 if machinepass is None:
924 machinepass = misc.random_password(12)
926 dnspass = misc.random_password(12)
928 root_uid = findnss(pwd.getpwnam, ["root"])[2]
930 root_uid = findnss(pwd.getpwnam, [root])[2]
932 nobody_uid = findnss(pwd.getpwnam, ["nobody"])[2]
934 nobody_uid = findnss(pwd.getpwnam, [nobody])[2]
936 users_gid = findnss(grp.getgrnam, ["users"])[2]
938 users_gid = findnss(grp.getgrnam, [users])[2]
940 wheel_gid = findnss(grp.getgrnam, ["wheel", "adm"])[2]
942 wheel_gid = findnss(grp.getgrnam, [wheel])[2]
944 aci = "# no aci for local ldb"
946 lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
948 names = guess_names(lp=lp, hostname=hostname, domain=domain,
949 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
950 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
953 paths = provision_paths_from_lp(lp, names.dnsdomain)
956 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
960 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
961 except socket.gaierror: pass
963 if serverrole is None:
964 serverrole = lp.get("server role")
966 assert serverrole in ("domain controller", "member server", "standalone")
967 if invocationid is None and serverrole == "domain controller":
968 invocationid = str(uuid.uuid4())
970 if not os.path.exists(paths.private_dir):
971 os.mkdir(paths.private_dir)
973 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
975 if ldap_backend is not None:
976 if ldap_backend == "ldapi":
977 # provision-backend will set this path suggested slapd command line / fedorads.inf
978 ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
980 # only install a new shares config db if there is none
981 if not os.path.exists(paths.shareconf):
982 message("Setting up share.ldb")
983 share_ldb = Ldb(paths.shareconf, session_info=session_info,
984 credentials=credentials, lp=lp)
985 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
988 message("Setting up secrets.ldb")
989 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
990 session_info=session_info,
991 credentials=credentials, lp=lp)
993 message("Setting up the registry")
994 setup_registry(paths.hklm, setup_path, session_info,
995 credentials=credentials, lp=lp)
997 message("Setting up templates db")
998 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
999 credentials=credentials, lp=lp)
1001 message("Setting up idmap db")
1002 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1003 credentials=credentials, lp=lp)
1005 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1006 credentials=credentials, lp=lp, names=names,
1008 domainsid=domainsid,
1009 aci=aci, domainguid=domainguid, policyguid=policyguid,
1011 adminpass=adminpass, krbtgtpass=krbtgtpass,
1012 invocationid=invocationid,
1013 machinepass=machinepass, dnspass=dnspass,
1014 serverrole=serverrole, ldap_backend=ldap_backend,
1015 ldap_backend_type=ldap_backend_type)
1017 if lp.get("server role") == "domain controller":
1018 if paths.netlogon is None:
1019 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1020 message("Please either remove %s or see the template at %s" %
1021 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1022 assert(paths.netlogon is not None)
1024 if paths.sysvol is None:
1025 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1026 message("Please either remove %s or see the template at %s" %
1027 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1028 assert(paths.sysvol is not None)
1030 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1031 "{" + policyguid + "}")
1032 os.makedirs(policy_path, 0755)
1033 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1034 os.makedirs(os.path.join(policy_path, "User"), 0755)
1035 if not os.path.isdir(paths.netlogon):
1036 os.makedirs(paths.netlogon, 0755)
1038 if samdb_fill == FILL_FULL:
1039 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1040 root_uid=root_uid, nobody_uid=nobody_uid,
1041 users_gid=users_gid, wheel_gid=wheel_gid)
1043 message("Setting up sam.ldb rootDSE marking as synchronized")
1044 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1046 # Only make a zone file on the first DC, it should be replicated with DNS replication
1047 if serverrole == "domain controller":
1048 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1049 credentials=credentials, lp=lp)
1050 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1051 netbiosname=names.netbiosname, domainsid=domainsid,
1052 keytab_path=paths.keytab, samdb_url=paths.samdb,
1053 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1054 machinepass=machinepass, dnsdomain=names.dnsdomain)
1056 samdb = SamDB(paths.samdb, session_info=session_info,
1057 credentials=credentials, lp=lp)
1059 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1060 assert isinstance(domainguid, str)
1061 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1062 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1063 scope=SCOPE_SUBTREE)
1064 assert isinstance(hostguid, str)
1066 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1067 domaindn=names.domaindn, hostip=hostip,
1068 hostip6=hostip6, hostname=names.hostname,
1069 dnspass=dnspass, realm=names.realm,
1070 domainguid=domainguid, hostguid=hostguid)
1071 message("Please install the zone located in %s into your DNS server" % paths.dns)
1073 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1074 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1075 keytab_name=paths.dns_keytab)
1076 message("See %s for example configuration statements for secure GSS-TSIG updates" % paths.namedconf)
1078 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1079 hostname=names.hostname, realm=names.realm)
1080 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1082 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1085 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1087 message("Once the above files are installed, your Samba4 server will be ready to use")
1088 message("Server Role: %s" % serverrole)
1089 message("Hostname: %s" % names.hostname)
1090 message("NetBIOS Domain: %s" % names.domain)
1091 message("DNS Domain: %s" % names.dnsdomain)
1092 message("DOMAIN SID: %s" % str(domainsid))
1093 message("Admin password: %s" % adminpass)
1095 result = ProvisionResult()
1096 result.domaindn = domaindn
1097 result.paths = paths
1099 result.samdb = samdb
1103 def provision_become_dc(setup_dir=None,
1104 smbconf=None, targetdir=None, realm=None,
1105 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1107 domain=None, hostname=None, domainsid=None,
1108 adminpass=None, krbtgtpass=None, domainguid=None,
1109 policyguid=None, invocationid=None, machinepass=None,
1110 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1111 wheel=None, backup=None, aci=None, serverrole=None,
1112 ldap_backend=None, ldap_backend_type=None, sitename=None):
1115 """print a message if quiet is not set."""
1118 return provision(setup_dir, message, system_session(), None,
1119 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1120 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1121 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1124 def setup_db_config(setup_path, dbdir):
1125 """Setup a Berkeley database.
1127 :param setup_path: Setup path function.
1128 :param dbdir: Database directory."""
1129 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1130 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1131 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1132 os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1134 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1135 {"LDAPDBDIR": dbdir})
1139 def provision_backend(setup_dir=None, message=None,
1140 smbconf=None, targetdir=None, realm=None,
1141 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1142 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1143 ldap_backend_type=None, ldap_backend_port=None):
1145 def setup_path(file):
1146 return os.path.join(setup_dir, file)
1148 if hostname is None:
1149 hostname = socket.gethostname().split(".")[0].lower()
1152 root = findnss(pwd.getpwnam, ["root"])[0]
1154 lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1156 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1157 dnsdomain=realm, serverrole=serverrole,
1158 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1160 paths = provision_paths_from_lp(lp, names.dnsdomain)
1162 if not os.path.isdir(paths.ldapdir):
1163 os.makedirs(paths.ldapdir)
1164 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1166 os.unlink(schemadb_path)
1170 schemadb = Ldb(schemadb_path, lp=lp)
1172 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1173 {"SCHEMADN": names.schemadn,
1175 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1177 setup_modify_ldif(schemadb,
1178 setup_path("provision_schema_basedn_modify.ldif"), \
1179 {"SCHEMADN": names.schemadn,
1180 "NETBIOSNAME": names.netbiosname,
1181 "DEFAULTSITE": DEFAULTSITE,
1182 "CONFIGDN": names.configdn,
1183 "SERVERDN": names.serverdn
1186 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1187 {"SCHEMADN": names.schemadn })
1188 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1189 {"SCHEMADN": names.schemadn})
1191 if ldap_backend_type == "fedora-ds":
1192 if ldap_backend_port is not None:
1193 serverport = "ServerPort=%d" % ldap_backend_port
1197 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1199 "HOSTNAME": hostname,
1200 "DNSDOMAIN": names.dnsdomain,
1201 "LDAPDIR": paths.ldapdir,
1202 "DOMAINDN": names.domaindn,
1203 "LDAPMANAGERDN": names.ldapmanagerdn,
1204 "LDAPMANAGERPASS": adminpass,
1205 "SERVERPORT": serverport})
1207 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1208 {"CONFIGDN": names.configdn,
1209 "SCHEMADN": names.schemadn,
1212 mapping = "schema-map-fedora-ds-1.0"
1213 backend_schema = "99_ad.ldif"
1215 slapdcommand="Initailise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1217 elif ldap_backend_type == "openldap":
1218 attrs = ["linkID", "lDAPDisplayName"]
1219 res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1221 memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1222 refint_attributes = "";
1223 for i in range (0, len(res)):
1224 linkid = res[i]["linkID"][0]
1225 linkid = str(int(linkid) + 1)
1226 expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1227 target = schemadb.searchone(basedn=names.schemadn,
1228 expression=expression,
1229 attribute="lDAPDisplayName",
1230 scope=SCOPE_SUBTREE);
1231 if target is not None:
1232 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1233 memberof_config = memberof_config + """overlay memberof
1234 memberof-dangling error
1235 memberof-refint TRUE
1236 memberof-group-oc top
1237 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1238 memberof-memberof-ad """ + target + """
1239 memberof-dangling-error 32
1243 memberof_config = memberof_config + """
1245 refint_attributes""" + refint_attributes + "\n";
1247 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1248 {"DNSDOMAIN": names.dnsdomain,
1249 "LDAPDIR": paths.ldapdir,
1250 "DOMAINDN": names.domaindn,
1251 "CONFIGDN": names.configdn,
1252 "SCHEMADN": names.schemadn,
1253 "LDAPMANAGERDN": names.ldapmanagerdn,
1254 "LDAPMANAGERPASS": adminpass,
1255 "MEMBEROF_CONFIG": memberof_config})
1256 setup_file(setup_path("modules.conf"), paths.modulesconf,
1257 {"REALM": names.realm})
1259 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "user")))
1260 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "config")))
1261 setup_db_config(setup_path, os.path.join(paths.ldapdir, os.path.join("db", "schema")))
1262 mapping = "schema-map-openldap-2.3"
1263 backend_schema = "backend-schema.schema"
1265 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1266 if ldap_backend_port is not None:
1267 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1269 server_port_string = ""
1270 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1272 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);
1274 os.system(schema_command)
1277 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ( ldap_backend_type) )
1278 message("Server Role: %s" % serverrole)
1279 message("Hostname: %s" % names.hostname)
1280 message("DNS Domain: %s" % names.dnsdomain)
1281 message("Base DN: %s" % names.domaindn)
1282 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1283 message("LDAP admin password: %s" % adminpass)
1284 message(slapdcommand)
1287 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1288 """Create a PHP LDAP admin configuration file.
1290 :param path: Path to write the configuration to.
1291 :param setup_path: Function to generate setup paths.
1293 setup_file(setup_path("phpldapadmin-config.php"), path,
1294 {"S4_LDAPI_URI": ldapi_uri})
1297 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1298 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1299 """Write out a DNS zone file, from the info in the current database.
1301 :param path: Path of the new zone file.
1302 :param setup_path: Setup path function.
1303 :param dnsdomain: DNS Domain name
1304 :param domaindn: DN of the Domain
1305 :param hostip: Local IPv4 IP
1306 :param hostip6: Local IPv6 IP
1307 :param hostname: Local hostname
1308 :param dnspass: Password for DNS
1309 :param realm: Realm name
1310 :param domainguid: GUID of the domain.
1311 :param hostguid: GUID of the host.
1313 assert isinstance(domainguid, str)
1315 hostip6_base_line = ""
1316 hostip6_host_line = ""
1318 if hostip6 is not None:
1319 hostip6_base_line = " IN AAAA " + hostip6
1320 hostip6_host_line = hostname + " IN AAAA " + hostip6
1322 setup_file(setup_path("provision.zone"), path, {
1323 "DNSPASS_B64": b64encode(dnspass),
1324 "HOSTNAME": hostname,
1325 "DNSDOMAIN": dnsdomain,
1328 "DOMAINGUID": domainguid,
1329 "DATESTRING": time.strftime("%Y%m%d%H"),
1330 "DEFAULTSITE": DEFAULTSITE,
1331 "HOSTGUID": hostguid,
1332 "HOSTIP6_BASE_LINE": hostip6_base_line,
1333 "HOSTIP6_HOST_LINE": hostip6_host_line,
1336 def create_named_conf(path, setup_path, realm, dnsdomain,
1337 private_dir, keytab_name):
1338 """Write out a file containing zone statements suitable for inclusion in a
1339 named.conf file (including GSS-TSIG configuration).
1341 :param path: Path of the new named.conf file.
1342 :param setup_path: Setup path function.
1343 :param realm: Realm name
1344 :param dnsdomain: DNS Domain name
1345 :param private_dir: Path to private directory
1346 :param keytab_name: File name of DNS keytab file
1349 setup_file(setup_path("named.conf"), path, {
1350 "DNSDOMAIN": dnsdomain,
1352 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1353 "DNS_KEYTAB": keytab_name,
1354 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1357 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1358 """Write out a file containing zone statements suitable for inclusion in a
1359 named.conf file (including GSS-TSIG configuration).
1361 :param path: Path of the new named.conf file.
1362 :param setup_path: Setup path function.
1363 :param dnsdomain: DNS Domain name
1364 :param hostname: Local hostname
1365 :param realm: Realm name
1368 setup_file(setup_path("krb5.conf"), path, {
1369 "DNSDOMAIN": dnsdomain,
1370 "HOSTNAME": hostname,
1374 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1375 """Load schema for the SamDB.
1377 :param samdb: Load a schema into a SamDB.
1378 :param setup_path: Setup path function.
1379 :param schemadn: DN of the schema
1380 :param netbiosname: NetBIOS name of the host.
1381 :param configdn: DN of the configuration
1383 schema_data = open(setup_path("schema.ldif"), 'r').read()
1384 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1385 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1386 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1387 head_data = substitute_var(head_data, {
1388 "SCHEMADN": schemadn,
1389 "NETBIOSNAME": netbiosname,
1390 "CONFIGDN": configdn,
1391 "DEFAULTSITE":sitename
1393 samdb.attach_schema_from_ldif(head_data, schema_data)