2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 from base64 import b64encode
37 from auth import system_session
38 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
39 from samba.samdb import SamDB
40 from samba.idmap import IDmapDB
41 from samba.dcerpc import security
43 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
44 timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
46 __docformat__ = "restructuredText"
48 DEFAULTSITE = "Default-First-Site-Name"
50 class InvalidNetbiosName(Exception):
51 """A specified name was not a valid NetBIOS name."""
52 def __init__(self, name):
53 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
56 class ProvisionPaths(object):
69 self.dns_keytab = None
72 self.private_dir = None
75 self.modulesconf = None
76 self.memberofconf = None
77 self.fedoradsinf = None
78 self.fedoradspartitions = None
80 self.olmmrserveridsconf = None
81 self.olmmrsyncreplconf = None
84 class ProvisionNames(object):
90 self.ldapmanagerdn = None
93 self.netbiosname = None
100 class ProvisionResult(object):
107 def check_install(lp, session_info, credentials):
108 """Check whether the current install seems ok.
110 :param lp: Loadparm context
111 :param session_info: Session information
112 :param credentials: Credentials
114 if lp.get("realm") == "":
115 raise Exception("Realm empty")
116 ldb = Ldb(lp.get("sam database"), session_info=session_info,
117 credentials=credentials, lp=lp)
118 if len(ldb.search("(cn=Administrator)")) != 1:
119 raise "No administrator account found"
122 def findnss(nssfn, names):
123 """Find a user or group from a list of possibilities.
125 :param nssfn: NSS Function to try (should raise KeyError if not found)
126 :param names: Names to check.
127 :return: Value return by first names list.
134 raise KeyError("Unable to find user/group %r" % names)
137 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
138 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
141 def read_and_sub_file(file, subst_vars):
142 """Read a file and sub in variables found in it
144 :param file: File to be read (typically from setup directory)
145 param subst_vars: Optional variables to subsitute in the file.
147 data = open(file, 'r').read()
148 if subst_vars is not None:
149 data = substitute_var(data, subst_vars)
150 check_all_substituted(data)
154 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
155 """Setup a ldb in the private dir.
157 :param ldb: LDB file to import data into
158 :param ldif_path: Path of the LDIF file to load
159 :param subst_vars: Optional variables to subsitute in LDIF.
161 assert isinstance(ldif_path, str)
163 data = read_and_sub_file(ldif_path, subst_vars)
167 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
168 """Modify a ldb in the private dir.
170 :param ldb: LDB object.
171 :param ldif_path: LDIF file path.
172 :param subst_vars: Optional dictionary with substitution variables.
174 data = read_and_sub_file(ldif_path, subst_vars)
176 ldb.modify_ldif(data)
179 def setup_ldb(ldb, ldif_path, subst_vars):
180 """Import a LDIF a file into a LDB handle, optionally substituting variables.
182 :note: Either all LDIF data will be added or none (using transactions).
184 :param ldb: LDB file to import into.
185 :param ldif_path: Path to the LDIF file.
186 :param subst_vars: Dictionary with substitution variables.
188 assert ldb is not None
189 ldb.transaction_start()
191 setup_add_ldif(ldb, ldif_path, subst_vars)
193 ldb.transaction_cancel()
195 ldb.transaction_commit()
198 def setup_file(template, fname, subst_vars):
199 """Setup a file in the private dir.
201 :param template: Path of the template file.
202 :param fname: Path of the file to create.
203 :param subst_vars: Substitution variables.
207 if os.path.exists(f):
210 data = read_and_sub_file(template, subst_vars)
211 open(f, 'w').write(data)
214 def provision_paths_from_lp(lp, dnsdomain):
215 """Set the default paths for provisioning.
217 :param lp: Loadparm context.
218 :param dnsdomain: DNS Domain name
220 paths = ProvisionPaths()
221 paths.private_dir = lp.get("private dir")
222 paths.keytab = "secrets.keytab"
223 paths.dns_keytab = "dns.keytab"
225 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
226 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
227 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
228 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
229 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
230 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
231 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
232 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
233 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
234 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
235 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
236 paths.phpldapadminconfig = os.path.join(paths.private_dir,
237 "phpldapadmin-config.php")
238 paths.ldapdir = os.path.join(paths.private_dir,
240 paths.slapdconf = os.path.join(paths.ldapdir,
242 paths.modulesconf = os.path.join(paths.ldapdir,
244 paths.memberofconf = os.path.join(paths.ldapdir,
246 paths.fedoradsinf = os.path.join(paths.ldapdir,
248 paths.fedoradspartitions = os.path.join(paths.ldapdir,
249 "fedorads-partitions.ldif")
250 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
251 "mmr_serverids.conf")
252 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
254 paths.hklm = "hklm.ldb"
255 paths.hkcr = "hkcr.ldb"
256 paths.hkcu = "hkcu.ldb"
257 paths.hku = "hku.ldb"
258 paths.hkpd = "hkpd.ldb"
259 paths.hkpt = "hkpt.ldb"
261 paths.sysvol = lp.get("path", "sysvol")
263 paths.netlogon = lp.get("path", "netlogon")
265 paths.smbconf = lp.configfile
270 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
271 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
273 """Guess configuration settings to use."""
276 hostname = socket.gethostname().split(".")[0].lower()
278 netbiosname = hostname.upper()
279 if not valid_netbios_name(netbiosname):
280 raise InvalidNetbiosName(netbiosname)
282 hostname = hostname.lower()
284 if dnsdomain is None:
285 dnsdomain = lp.get("realm")
287 if serverrole is None:
288 serverrole = lp.get("server role")
290 assert dnsdomain is not None
291 realm = dnsdomain.upper()
293 if lp.get("realm").upper() != realm:
294 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
295 (lp.get("realm"), lp.configfile, realm))
297 dnsdomain = dnsdomain.lower()
299 if serverrole == "domain controller":
301 domain = lp.get("workgroup")
303 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
304 if lp.get("workgroup").upper() != domain.upper():
305 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
306 lp.get("workgroup"), domain)
310 domaindn = "CN=" + netbiosname
312 assert domain is not None
313 domain = domain.upper()
314 if not valid_netbios_name(domain):
315 raise InvalidNetbiosName(domain)
321 configdn = "CN=Configuration," + rootdn
323 schemadn = "CN=Schema," + configdn
328 names = ProvisionNames()
329 names.rootdn = rootdn
330 names.domaindn = domaindn
331 names.configdn = configdn
332 names.schemadn = schemadn
333 names.ldapmanagerdn = "CN=Manager," + rootdn
334 names.dnsdomain = dnsdomain
335 names.domain = domain
337 names.netbiosname = netbiosname
338 names.hostname = hostname
339 names.sitename = sitename
340 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
345 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
347 """Create a new smb.conf file based on a couple of basic settings.
349 assert smbconf is not None
351 hostname = socket.gethostname().split(".")[0].lower()
353 if serverrole is None:
354 serverrole = "standalone"
356 assert serverrole in ("domain controller", "member server", "standalone")
357 if serverrole == "domain controller":
359 elif serverrole == "member server":
360 smbconfsuffix = "member"
361 elif serverrole == "standalone":
362 smbconfsuffix = "standalone"
364 assert domain is not None
365 assert realm is not None
367 default_lp = param.LoadParm()
368 #Load non-existant file
369 if os.path.exists(smbconf):
370 default_lp.load(smbconf)
372 if targetdir is not None:
373 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
374 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
376 default_lp.set("lock dir", os.path.abspath(targetdir))
381 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
382 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
384 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
386 "HOSTNAME": hostname,
389 "SERVERROLE": serverrole,
390 "NETLOGONPATH": netlogon,
391 "SYSVOLPATH": sysvol,
392 "PRIVATEDIR_LINE": privatedir_line,
393 "LOCKDIR_LINE": lockdir_line
397 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
398 users_gid, wheel_gid):
399 """setup reasonable name mappings for sam names to unix names.
401 :param samdb: SamDB object.
402 :param idmap: IDmap db object.
403 :param sid: The domain sid.
404 :param domaindn: The domain DN.
405 :param root_uid: uid of the UNIX root user.
406 :param nobody_uid: uid of the UNIX nobody user.
407 :param users_gid: gid of the UNIX users group.
408 :param wheel_gid: gid of the UNIX wheel group."""
409 # add some foreign sids if they are not present already
410 samdb.add_stock_foreign_sids()
412 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
413 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
415 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
416 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
419 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
421 serverrole, ldap_backend=None,
422 ldap_backend_type=None, erase=False):
423 """Setup the partitions for the SAM database.
425 Alternatively, provision() may call this, and then populate the database.
427 :note: This will wipe the Sam Database!
429 :note: This function always removes the local SAM LDB file. The erase
430 parameter controls whether to erase the existing data, which
431 may not be stored locally but in LDAP.
433 assert session_info is not None
436 samdb = SamDB(samdb_path, session_info=session_info,
437 credentials=credentials, lp=lp)
441 os.unlink(samdb_path)
442 samdb = SamDB(samdb_path, session_info=session_info,
443 credentials=credentials, lp=lp)
448 #Add modules to the list to activate them by default
449 #beware often order is important
451 # Some Known ordering constraints:
452 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
453 # - objectclass must be before password_hash, because password_hash checks
454 # that the objectclass is of type person (filled in by objectclass
455 # module when expanding the objectclass list)
456 # - partition must be last
457 # - each partition has its own module list then
458 modules_list = ["rootdse",
476 "extended_dn_out_ldb"]
477 modules_list2 = ["show_deleted",
480 domaindn_ldb = "users.ldb"
481 if ldap_backend is not None:
482 domaindn_ldb = ldap_backend
483 configdn_ldb = "configuration.ldb"
484 if ldap_backend is not None:
485 configdn_ldb = ldap_backend
486 schemadn_ldb = "schema.ldb"
487 if ldap_backend is not None:
488 schema_ldb = ldap_backend
489 schemadn_ldb = ldap_backend
491 if ldap_backend_type == "fedora-ds":
492 backend_modules = ["nsuniqueid", "paged_searches"]
493 # We can handle linked attributes here, as we don't have directory-side subtree operations
494 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
495 elif ldap_backend_type == "openldap":
496 backend_modules = ["entryuuid", "paged_searches"]
497 # OpenLDAP handles subtree renames, so we don't want to do any of these things
498 tdb_modules_list = ["extended_dn_out_dereference"]
499 elif ldap_backend is not None:
500 raise "LDAP Backend specified, but LDAP Backend Type not specified"
501 elif serverrole == "domain controller":
502 backend_modules = ["repl_meta_data"]
504 backend_modules = ["objectguid"]
506 if tdb_modules_list is None:
507 tdb_modules_list_as_string = ""
509 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
511 samdb.transaction_start()
513 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
514 "SCHEMADN": names.schemadn,
515 "SCHEMADN_LDB": schemadn_ldb,
516 "SCHEMADN_MOD2": ",objectguid",
517 "CONFIGDN": names.configdn,
518 "CONFIGDN_LDB": configdn_ldb,
519 "DOMAINDN": names.domaindn,
520 "DOMAINDN_LDB": domaindn_ldb,
521 "SCHEMADN_MOD": "schema_fsmo,instancetype",
522 "CONFIGDN_MOD": "naming_fsmo,instancetype",
523 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
524 "MODULES_LIST": ",".join(modules_list),
525 "TDB_MODULES_LIST": tdb_modules_list_as_string,
526 "MODULES_LIST2": ",".join(modules_list2),
527 "BACKEND_MOD": ",".join(backend_modules),
531 samdb.transaction_cancel()
534 samdb.transaction_commit()
536 samdb = SamDB(samdb_path, session_info=session_info,
537 credentials=credentials, lp=lp)
539 samdb.transaction_start()
541 message("Setting up sam.ldb attributes")
542 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
544 message("Setting up sam.ldb rootDSE")
545 setup_samdb_rootdse(samdb, setup_path, names)
548 message("Erasing data from partitions")
549 samdb.erase_partitions()
552 samdb.transaction_cancel()
555 samdb.transaction_commit()
560 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
561 netbiosname, domainsid, keytab_path, samdb_url,
562 dns_keytab_path, dnspass, machinepass):
563 """Add DC-specific bits to a secrets database.
565 :param secretsdb: Ldb Handle to the secrets database
566 :param setup_path: Setup path function
567 :param machinepass: Machine password
569 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
570 "MACHINEPASS_B64": b64encode(machinepass),
573 "DNSDOMAIN": dnsdomain,
574 "DOMAINSID": str(domainsid),
575 "SECRETS_KEYTAB": keytab_path,
576 "NETBIOSNAME": netbiosname,
577 "SAM_LDB": samdb_url,
578 "DNS_KEYTAB": dns_keytab_path,
579 "DNSPASS_B64": b64encode(dnspass),
583 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
584 """Setup the secrets database.
586 :param path: Path to the secrets database.
587 :param setup_path: Get the path to a setup file.
588 :param session_info: Session info.
589 :param credentials: Credentials
590 :param lp: Loadparm context
591 :return: LDB handle for the created secrets database
593 if os.path.exists(path):
595 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
598 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
599 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
601 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
603 if credentials is not None and credentials.authentication_requested():
604 if credentials.get_bind_dn() is not None:
605 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
606 "LDAPMANAGERDN": credentials.get_bind_dn(),
607 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
610 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
611 "LDAPADMINUSER": credentials.get_username(),
612 "LDAPADMINREALM": credentials.get_realm(),
613 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
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)
632 templates_ldb.erase()
636 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
638 templates_ldb = SamDB(path, session_info=session_info,
639 credentials=credentials, lp=lp)
641 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
644 def setup_registry(path, setup_path, session_info, credentials, lp):
645 """Setup the registry.
647 :param path: Path to the registry database
648 :param setup_path: Function that returns the path to a setup.
649 :param session_info: Session information
650 :param credentials: Credentials
651 :param lp: Loadparm context
653 reg = registry.Registry()
654 hive = registry.open_ldb(path, session_info=session_info,
655 credentials=credentials, lp_ctx=lp)
656 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
657 provision_reg = setup_path("provision.reg")
658 assert os.path.exists(provision_reg)
659 reg.diff_apply(provision_reg)
662 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
663 """Setup the idmap database.
665 :param path: path to the idmap database
666 :param setup_path: Function that returns a path to a setup file
667 :param session_info: Session information
668 :param credentials: Credentials
669 :param lp: Loadparm context
671 if os.path.exists(path):
674 idmap_ldb = IDmapDB(path, session_info=session_info,
675 credentials=credentials, lp=lp)
678 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
682 def setup_samdb_rootdse(samdb, setup_path, names):
683 """Setup the SamDB rootdse.
685 :param samdb: Sam Database handle
686 :param setup_path: Obtain setup path
688 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
689 "SCHEMADN": names.schemadn,
690 "NETBIOSNAME": names.netbiosname,
691 "DNSDOMAIN": names.dnsdomain,
692 "REALM": names.realm,
693 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
694 "DOMAINDN": names.domaindn,
695 "ROOTDN": names.rootdn,
696 "CONFIGDN": names.configdn,
697 "SERVERDN": names.serverdn,
701 def setup_self_join(samdb, names,
702 machinepass, dnspass,
703 domainsid, invocationid, setup_path,
705 """Join a host to its own domain."""
706 assert isinstance(invocationid, str)
707 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
708 "CONFIGDN": names.configdn,
709 "SCHEMADN": names.schemadn,
710 "DOMAINDN": names.domaindn,
711 "SERVERDN": names.serverdn,
712 "INVOCATIONID": invocationid,
713 "NETBIOSNAME": names.netbiosname,
714 "DEFAULTSITE": names.sitename,
715 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
716 "MACHINEPASS_B64": b64encode(machinepass),
717 "DNSPASS_B64": b64encode(dnspass),
718 "REALM": names.realm,
719 "DOMAIN": names.domain,
720 "DNSDOMAIN": names.dnsdomain})
721 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
722 "POLICYGUID": policyguid,
723 "DNSDOMAIN": names.dnsdomain,
724 "DOMAINSID": str(domainsid),
725 "DOMAINDN": names.domaindn})
728 def setup_samdb(path, setup_path, session_info, credentials, lp,
730 domainsid, aci, domainguid, policyguid,
731 fill, adminpass, krbtgtpass,
732 machinepass, invocationid, dnspass,
733 serverrole, ldap_backend=None,
734 ldap_backend_type=None):
735 """Setup a complete SAM Database.
737 :note: This will wipe the main SAM database file!
740 erase = (fill != FILL_DRS)
742 # Also wipes the database
743 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
744 credentials=credentials, session_info=session_info,
746 ldap_backend=ldap_backend, serverrole=serverrole,
747 ldap_backend_type=ldap_backend_type, erase=erase)
749 samdb = SamDB(path, session_info=session_info,
750 credentials=credentials, lp=lp)
754 message("Pre-loading the Samba 4 and AD schema")
755 samdb.set_domain_sid(str(domainsid))
756 if serverrole == "domain controller":
757 samdb.set_invocation_id(invocationid)
759 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
760 names.configdn, names.sitename, names.serverdn,
763 samdb.transaction_start()
766 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
767 if serverrole == "domain controller":
768 domain_oc = "domainDNS"
770 domain_oc = "samba4LocalDomain"
772 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
773 "DOMAINDN": names.domaindn,
775 "DOMAIN_OC": domain_oc
778 message("Modifying DomainDN: " + names.domaindn + "")
779 if domainguid is not None:
780 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
784 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
785 "LDAPTIME": timestring(int(time.time())),
786 "DOMAINSID": str(domainsid),
787 "SCHEMADN": names.schemadn,
788 "NETBIOSNAME": names.netbiosname,
789 "DEFAULTSITE": names.sitename,
790 "CONFIGDN": names.configdn,
791 "SERVERDN": names.serverdn,
792 "POLICYGUID": policyguid,
793 "DOMAINDN": names.domaindn,
794 "DOMAINGUID_MOD": domainguid_mod,
797 message("Adding configuration container (permitted to fail)")
798 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
799 "CONFIGDN": names.configdn,
802 message("Modifying configuration container")
803 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
804 "CONFIGDN": names.configdn,
805 "SCHEMADN": names.schemadn,
808 message("Adding schema container (permitted to fail)")
809 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
810 "SCHEMADN": names.schemadn,
813 message("Modifying schema container")
815 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
817 setup_modify_ldif(samdb,
818 setup_path("provision_schema_basedn_modify.ldif"), {
819 "SCHEMADN": names.schemadn,
820 "NETBIOSNAME": names.netbiosname,
821 "DEFAULTSITE": names.sitename,
822 "CONFIGDN": names.configdn,
823 "SERVERDN": names.serverdn,
824 "PREFIXMAP_B64": b64encode(prefixmap)
827 message("Setting up sam.ldb Samba4 schema")
828 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
829 {"SCHEMADN": names.schemadn })
830 message("Setting up sam.ldb AD schema")
831 setup_add_ldif(samdb, setup_path("schema.ldif"),
832 {"SCHEMADN": names.schemadn})
833 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
834 {"SCHEMADN": names.schemadn})
836 message("Setting up sam.ldb configuration data")
837 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
838 "CONFIGDN": names.configdn,
839 "NETBIOSNAME": names.netbiosname,
840 "DEFAULTSITE": names.sitename,
841 "DNSDOMAIN": names.dnsdomain,
842 "DOMAIN": names.domain,
843 "SCHEMADN": names.schemadn,
844 "DOMAINDN": names.domaindn,
845 "SERVERDN": names.serverdn
848 message("Setting up display specifiers")
849 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
850 {"CONFIGDN": names.configdn})
852 message("Adding users container (permitted to fail)")
853 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
854 "DOMAINDN": names.domaindn})
855 message("Modifying users container")
856 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
857 "DOMAINDN": names.domaindn})
858 message("Adding computers container (permitted to fail)")
859 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
860 "DOMAINDN": names.domaindn})
861 message("Modifying computers container")
862 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
863 "DOMAINDN": names.domaindn})
864 message("Setting up sam.ldb data")
865 setup_add_ldif(samdb, setup_path("provision.ldif"), {
866 "DOMAINDN": names.domaindn,
867 "NETBIOSNAME": names.netbiosname,
868 "DEFAULTSITE": names.sitename,
869 "CONFIGDN": names.configdn,
870 "SERVERDN": names.serverdn
873 if fill == FILL_FULL:
874 message("Setting up sam.ldb users and groups")
875 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
876 "DOMAINDN": names.domaindn,
877 "DOMAINSID": str(domainsid),
878 "CONFIGDN": names.configdn,
879 "ADMINPASS_B64": b64encode(adminpass),
880 "KRBTGTPASS_B64": b64encode(krbtgtpass),
883 if serverrole == "domain controller":
884 message("Setting up self join")
885 setup_self_join(samdb, names=names, invocationid=invocationid,
887 machinepass=machinepass,
888 domainsid=domainsid, policyguid=policyguid,
889 setup_path=setup_path)
892 samdb.transaction_cancel()
895 samdb.transaction_commit()
900 FILL_NT4SYNC = "NT4SYNC"
903 def provision(setup_dir, message, session_info,
904 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
905 rootdn=None, domaindn=None, schemadn=None, configdn=None,
907 domain=None, hostname=None, hostip=None, hostip6=None,
908 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
909 policyguid=None, invocationid=None, machinepass=None,
910 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
911 wheel=None, backup=None, aci=None, serverrole=None,
912 ldap_backend=None, ldap_backend_type=None, sitename=None):
915 :note: caution, this wipes all existing data!
918 def setup_path(file):
919 return os.path.join(setup_dir, file)
921 if domainsid is None:
922 domainsid = security.random_sid()
924 if policyguid is None:
925 policyguid = str(uuid.uuid4())
926 if adminpass is None:
927 adminpass = glue.generate_random_str(12)
928 if krbtgtpass is None:
929 krbtgtpass = glue.generate_random_str(12)
930 if machinepass is None:
931 machinepass = glue.generate_random_str(12)
933 dnspass = glue.generate_random_str(12)
934 root_uid = findnss_uid([root or "root"])
935 nobody_uid = findnss_uid([nobody or "nobody"])
936 users_gid = findnss_gid([users or "users"])
938 wheel_gid = findnss_gid(["wheel", "adm"])
940 wheel_gid = findnss_gid([wheel])
942 aci = "# no aci for local ldb"
944 if targetdir is not None:
945 if (not os.path.exists(os.path.join(targetdir, "etc"))):
946 os.makedirs(os.path.join(targetdir, "etc"))
947 smbconf = os.path.join(targetdir, "etc", "smb.conf")
948 elif smbconf is None:
949 smbconf = param.default_path()
951 # only install a new smb.conf if there isn't one there already
952 if not os.path.exists(smbconf):
953 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
956 lp = param.LoadParm()
959 names = guess_names(lp=lp, hostname=hostname, domain=domain,
960 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
961 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
964 paths = provision_paths_from_lp(lp, names.dnsdomain)
968 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
969 except socket.gaierror, (socket.EAI_NODATA, msg):
974 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
975 except socket.gaierror, (socket.EAI_NODATA, msg):
978 if serverrole is None:
979 serverrole = lp.get("server role")
981 assert serverrole in ("domain controller", "member server", "standalone")
982 if invocationid is None and serverrole == "domain controller":
983 invocationid = str(uuid.uuid4())
985 if not os.path.exists(paths.private_dir):
986 os.mkdir(paths.private_dir)
988 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
990 if ldap_backend is not None:
991 if ldap_backend == "ldapi":
992 # provision-backend will set this path suggested slapd command line / fedorads.inf
993 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
995 # only install a new shares config db if there is none
996 if not os.path.exists(paths.shareconf):
997 message("Setting up share.ldb")
998 share_ldb = Ldb(paths.shareconf, session_info=session_info,
999 credentials=credentials, lp=lp)
1000 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1003 message("Setting up secrets.ldb")
1004 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1005 session_info=session_info,
1006 credentials=credentials, lp=lp)
1008 message("Setting up the registry")
1009 setup_registry(paths.hklm, setup_path, session_info,
1010 credentials=credentials, lp=lp)
1012 message("Setting up templates db")
1013 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1014 credentials=credentials, lp=lp)
1016 message("Setting up idmap db")
1017 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1018 credentials=credentials, lp=lp)
1020 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1021 credentials=credentials, lp=lp, names=names,
1023 domainsid=domainsid,
1024 aci=aci, domainguid=domainguid, policyguid=policyguid,
1026 adminpass=adminpass, krbtgtpass=krbtgtpass,
1027 invocationid=invocationid,
1028 machinepass=machinepass, dnspass=dnspass,
1029 serverrole=serverrole, ldap_backend=ldap_backend,
1030 ldap_backend_type=ldap_backend_type)
1032 if lp.get("server role") == "domain controller":
1033 if paths.netlogon is None:
1034 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1035 message("Please either remove %s or see the template at %s" %
1036 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1037 assert(paths.netlogon is not None)
1039 if paths.sysvol is None:
1040 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1041 message("Please either remove %s or see the template at %s" %
1042 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1043 assert(paths.sysvol is not None)
1045 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1046 "{" + policyguid + "}")
1047 os.makedirs(policy_path, 0755)
1048 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1049 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1050 os.makedirs(os.path.join(policy_path, "User"), 0755)
1051 if not os.path.isdir(paths.netlogon):
1052 os.makedirs(paths.netlogon, 0755)
1054 if samdb_fill == FILL_FULL:
1055 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1056 root_uid=root_uid, nobody_uid=nobody_uid,
1057 users_gid=users_gid, wheel_gid=wheel_gid)
1059 message("Setting up sam.ldb rootDSE marking as synchronized")
1060 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1062 # Only make a zone file on the first DC, it should be replicated with DNS replication
1063 if serverrole == "domain controller":
1064 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1065 credentials=credentials, lp=lp)
1066 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1067 netbiosname=names.netbiosname, domainsid=domainsid,
1068 keytab_path=paths.keytab, samdb_url=paths.samdb,
1069 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1070 machinepass=machinepass, dnsdomain=names.dnsdomain)
1072 samdb = SamDB(paths.samdb, session_info=session_info,
1073 credentials=credentials, lp=lp)
1075 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1076 assert isinstance(domainguid, str)
1077 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1078 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1079 scope=SCOPE_SUBTREE)
1080 assert isinstance(hostguid, str)
1082 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1083 domaindn=names.domaindn, hostip=hostip,
1084 hostip6=hostip6, hostname=names.hostname,
1085 dnspass=dnspass, realm=names.realm,
1086 domainguid=domainguid, hostguid=hostguid)
1088 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1089 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1091 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1092 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1093 keytab_name=paths.dns_keytab)
1094 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1095 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1097 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1098 hostname=names.hostname, realm=names.realm)
1099 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1101 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1104 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1106 message("Once the above files are installed, your Samba4 server will be ready to use")
1107 message("Server Role: %s" % serverrole)
1108 message("Hostname: %s" % names.hostname)
1109 message("NetBIOS Domain: %s" % names.domain)
1110 message("DNS Domain: %s" % names.dnsdomain)
1111 message("DOMAIN SID: %s" % str(domainsid))
1112 message("Admin password: %s" % adminpass)
1114 result = ProvisionResult()
1115 result.domaindn = domaindn
1116 result.paths = paths
1118 result.samdb = samdb
1122 def provision_become_dc(setup_dir=None,
1123 smbconf=None, targetdir=None, realm=None,
1124 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1126 domain=None, hostname=None, domainsid=None,
1127 adminpass=None, krbtgtpass=None, domainguid=None,
1128 policyguid=None, invocationid=None, machinepass=None,
1129 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1130 wheel=None, backup=None, aci=None, serverrole=None,
1131 ldap_backend=None, ldap_backend_type=None, sitename=None):
1134 """print a message if quiet is not set."""
1137 return provision(setup_dir, message, system_session(), None,
1138 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1139 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1140 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1143 def setup_db_config(setup_path, dbdir):
1144 """Setup a Berkeley database.
1146 :param setup_path: Setup path function.
1147 :param dbdir: Database directory."""
1148 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1149 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1150 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1151 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1153 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1154 {"LDAPDBDIR": dbdir})
1158 def provision_backend(setup_dir=None, message=None,
1159 smbconf=None, targetdir=None, realm=None,
1160 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1161 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1162 ldap_backend_type=None, ldap_backend_port=None,
1165 def setup_path(file):
1166 return os.path.join(setup_dir, file)
1168 if hostname is None:
1169 hostname = socket.gethostname().split(".")[0].lower()
1172 root = findnss(pwd.getpwnam, ["root"])[0]
1174 if adminpass is None:
1175 adminpass = glue.generate_random_str(12)
1177 if targetdir is not None:
1178 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1179 os.makedirs(os.path.join(targetdir, "etc"))
1180 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1181 elif smbconf is None:
1182 smbconf = param.default_path()
1183 assert smbconf is not None
1185 # only install a new smb.conf if there isn't one there already
1186 if not os.path.exists(smbconf):
1187 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1190 lp = param.LoadParm()
1193 if serverrole is None:
1194 serverrole = lp.get("server role")
1196 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1197 dnsdomain=realm, serverrole=serverrole,
1198 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1201 paths = provision_paths_from_lp(lp, names.dnsdomain)
1203 if not os.path.isdir(paths.ldapdir):
1204 os.makedirs(paths.ldapdir, 0700)
1205 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1207 os.unlink(schemadb_path)
1211 schemadb = Ldb(schemadb_path, lp=lp)
1213 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1215 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1216 {"SCHEMADN": names.schemadn,
1219 setup_modify_ldif(schemadb,
1220 setup_path("provision_schema_basedn_modify.ldif"), \
1221 {"SCHEMADN": names.schemadn,
1222 "NETBIOSNAME": names.netbiosname,
1223 "DEFAULTSITE": DEFAULTSITE,
1224 "CONFIGDN": names.configdn,
1225 "SERVERDN": names.serverdn,
1226 "PREFIXMAP_B64": b64encode(prefixmap)
1229 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1230 {"SCHEMADN": names.schemadn })
1231 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1232 {"SCHEMADN": names.schemadn})
1234 if ldap_backend_type == "fedora-ds":
1235 if ldap_backend_port is not None:
1236 serverport = "ServerPort=%d" % ldap_backend_port
1240 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1242 "HOSTNAME": hostname,
1243 "DNSDOMAIN": names.dnsdomain,
1244 "LDAPDIR": paths.ldapdir,
1245 "DOMAINDN": names.domaindn,
1246 "LDAPMANAGERDN": names.ldapmanagerdn,
1247 "LDAPMANAGERPASS": adminpass,
1248 "SERVERPORT": serverport})
1250 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1251 {"CONFIGDN": names.configdn,
1252 "SCHEMADN": names.schemadn,
1255 mapping = "schema-map-fedora-ds-1.0"
1256 backend_schema = "99_ad.ldif"
1258 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1260 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1262 elif ldap_backend_type == "openldap":
1263 attrs = ["linkID", "lDAPDisplayName"]
1264 res = schemadb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
1266 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1267 refint_attributes = ""
1268 for i in range (0, len(res)):
1269 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1270 target = schemadb.searchone(basedn=names.schemadn,
1271 expression=expression,
1272 attribute="lDAPDisplayName",
1273 scope=SCOPE_SUBTREE)
1274 if target is not None:
1275 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1277 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1278 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1279 "MEMBEROF_ATTR" : str(target) })
1281 refint_config = read_and_sub_file(setup_path("refint.conf"),
1282 { "LINK_ATTRS" : refint_attributes})
1284 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1286 mmr_replicator_acl = ""
1287 mmr_serverids_config = ""
1288 mmr_syncrepl_schema_config = ""
1289 mmr_syncrepl_config_config = ""
1290 mmr_syncrepl_user_config = ""
1292 if ol_mmr_urls is not None:
1293 # For now, make these equal
1294 mmr_pass = adminpass
1296 url_list=filter(None,ol_mmr_urls.split(' '))
1297 if (len(url_list) == 1):
1298 url_list=filter(None,ol_mmr_urls.split(','))
1301 mmr_on_config = "MirrorMode On"
1302 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1304 for url in url_list:
1306 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1307 { "SERVERID" : str(serverid),
1308 "LDAPSERVER" : url })
1311 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1313 "MMRDN": names.schemadn,
1315 "MMR_PASSWORD": mmr_pass})
1318 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1320 "MMRDN": names.configdn,
1322 "MMR_PASSWORD": mmr_pass})
1325 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1327 "MMRDN": names.domaindn,
1329 "MMR_PASSWORD": mmr_pass })
1332 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1333 {"DNSDOMAIN": names.dnsdomain,
1334 "LDAPDIR": paths.ldapdir,
1335 "DOMAINDN": names.domaindn,
1336 "CONFIGDN": names.configdn,
1337 "SCHEMADN": names.schemadn,
1338 "MEMBEROF_CONFIG": memberof_config,
1339 "MIRRORMODE": mmr_on_config,
1340 "REPLICATOR_ACL": mmr_replicator_acl,
1341 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1342 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1343 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1344 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1345 "REFINT_CONFIG": refint_config})
1346 setup_file(setup_path("modules.conf"), paths.modulesconf,
1347 {"REALM": names.realm})
1349 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1350 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1351 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1353 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1354 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1356 setup_file(setup_path("cn=samba.ldif"),
1357 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1358 { "UUID": str(uuid.uuid4()),
1359 "LDAPTIME": timestring(int(time.time()))} )
1360 setup_file(setup_path("cn=samba-admin.ldif"),
1361 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1362 {"LDAPADMINPASS_B64": b64encode(adminpass),
1363 "UUID": str(uuid.uuid4()),
1364 "LDAPTIME": timestring(int(time.time()))} )
1366 if ol_mmr_urls is not None:
1367 setup_file(setup_path("cn=replicator.ldif"),
1368 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1369 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1370 "UUID": str(uuid.uuid4()),
1371 "LDAPTIME": timestring(int(time.time()))} )
1375 mapping = "schema-map-openldap-2.3"
1376 backend_schema = "backend-schema.schema"
1378 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1379 if ldap_backend_port is not None:
1380 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1382 server_port_string = ""
1384 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1386 ldapuser = "--username=samba-admin"
1389 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)
1391 os.system(schema_command)
1393 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1394 message("Server Role: %s" % serverrole)
1395 message("Hostname: %s" % names.hostname)
1396 message("DNS Domain: %s" % names.dnsdomain)
1397 message("Base DN: %s" % names.domaindn)
1399 if ldap_backend_type == "openldap":
1400 message("LDAP admin user: samba-admin")
1402 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1404 message("LDAP admin password: %s" % adminpass)
1405 message(slapdcommand)
1406 assert isinstance(ldap_backend_type, str)
1407 assert isinstance(ldapuser, str)
1408 assert isinstance(adminpass, str)
1409 assert isinstance(names.dnsdomain, str)
1410 assert isinstance(names.domain, str)
1411 assert isinstance(serverrole, str)
1412 args = ["--ldap-backend=ldapi",
1413 "--ldap-backend-type=" + ldap_backend_type,
1414 "--password=" + adminpass,
1416 "--realm=" + names.dnsdomain,
1417 "--domain=" + names.domain,
1418 "--server-role='" + serverrole + "'"]
1419 message("Run provision with: " + " ".join(args))
1422 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1423 """Create a PHP LDAP admin configuration file.
1425 :param path: Path to write the configuration to.
1426 :param setup_path: Function to generate setup paths.
1428 setup_file(setup_path("phpldapadmin-config.php"), path,
1429 {"S4_LDAPI_URI": ldapi_uri})
1432 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1433 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1434 """Write out a DNS zone file, from the info in the current database.
1436 :param path: Path of the new zone file.
1437 :param setup_path: Setup path function.
1438 :param dnsdomain: DNS Domain name
1439 :param domaindn: DN of the Domain
1440 :param hostip: Local IPv4 IP
1441 :param hostip6: Local IPv6 IP
1442 :param hostname: Local hostname
1443 :param dnspass: Password for DNS
1444 :param realm: Realm name
1445 :param domainguid: GUID of the domain.
1446 :param hostguid: GUID of the host.
1448 assert isinstance(domainguid, str)
1450 if hostip6 is not None:
1451 hostip6_base_line = " IN AAAA " + hostip6
1452 hostip6_host_line = hostname + " IN AAAA " + hostip6
1454 hostip6_base_line = ""
1455 hostip6_host_line = ""
1457 if hostip is not None:
1458 hostip_base_line = " IN A " + hostip
1459 hostip_host_line = hostname + " IN A " + hostip
1461 hostip_base_line = ""
1462 hostip_host_line = ""
1464 setup_file(setup_path("provision.zone"), path, {
1465 "DNSPASS_B64": b64encode(dnspass),
1466 "HOSTNAME": hostname,
1467 "DNSDOMAIN": dnsdomain,
1469 "HOSTIP_BASE_LINE": hostip_base_line,
1470 "HOSTIP_HOST_LINE": hostip_host_line,
1471 "DOMAINGUID": domainguid,
1472 "DATESTRING": time.strftime("%Y%m%d%H"),
1473 "DEFAULTSITE": DEFAULTSITE,
1474 "HOSTGUID": hostguid,
1475 "HOSTIP6_BASE_LINE": hostip6_base_line,
1476 "HOSTIP6_HOST_LINE": hostip6_host_line,
1480 def create_named_conf(path, setup_path, realm, dnsdomain,
1482 """Write out a file containing zone statements suitable for inclusion in a
1483 named.conf file (including GSS-TSIG configuration).
1485 :param path: Path of the new named.conf file.
1486 :param setup_path: Setup path function.
1487 :param realm: Realm name
1488 :param dnsdomain: DNS Domain name
1489 :param private_dir: Path to private directory
1490 :param keytab_name: File name of DNS keytab file
1493 setup_file(setup_path("named.conf"), path, {
1494 "DNSDOMAIN": dnsdomain,
1496 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1497 "PRIVATE_DIR": private_dir
1500 def create_named_txt(path, setup_path, realm, dnsdomain,
1501 private_dir, keytab_name):
1502 """Write out a file containing zone statements suitable for inclusion in a
1503 named.conf file (including GSS-TSIG configuration).
1505 :param path: Path of the new named.conf file.
1506 :param setup_path: Setup path function.
1507 :param realm: Realm name
1508 :param dnsdomain: DNS Domain name
1509 :param private_dir: Path to private directory
1510 :param keytab_name: File name of DNS keytab file
1513 setup_file(setup_path("named.txt"), path, {
1514 "DNSDOMAIN": dnsdomain,
1516 "DNS_KEYTAB": keytab_name,
1517 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1518 "PRIVATE_DIR": private_dir
1521 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1522 """Write out a file containing zone statements suitable for inclusion in a
1523 named.conf file (including GSS-TSIG configuration).
1525 :param path: Path of the new named.conf file.
1526 :param setup_path: Setup path function.
1527 :param dnsdomain: DNS Domain name
1528 :param hostname: Local hostname
1529 :param realm: Realm name
1532 setup_file(setup_path("krb5.conf"), path, {
1533 "DNSDOMAIN": dnsdomain,
1534 "HOSTNAME": hostname,
1539 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1540 serverdn, servername):
1541 """Load schema for the SamDB.
1543 :param samdb: Load a schema into a SamDB.
1544 :param setup_path: Setup path function.
1545 :param schemadn: DN of the schema
1546 :param netbiosname: NetBIOS name of the host.
1547 :param configdn: DN of the configuration
1548 :param serverdn: DN of the server
1549 :param servername: Host name of the server
1551 schema_data = open(setup_path("schema.ldif"), 'r').read()
1552 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1553 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1554 check_all_substituted(schema_data)
1555 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1556 prefixmap = b64encode(prefixmap)
1558 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1559 head_data = substitute_var(head_data, {
1560 "SCHEMADN": schemadn,
1561 "NETBIOSNAME": netbiosname,
1562 "CONFIGDN": configdn,
1563 "DEFAULTSITE": sitename,
1564 "PREFIXMAP_B64": prefixmap,
1565 "SERVERDN": serverdn,
1566 "SERVERNAME": servername,
1568 check_all_substituted(head_data)
1569 samdb.attach_schema_from_ldif(head_data, schema_data)