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
398 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
399 users_gid, wheel_gid):
400 """setup reasonable name mappings for sam names to unix names.
402 :param samdb: SamDB object.
403 :param idmap: IDmap db object.
404 :param sid: The domain sid.
405 :param domaindn: The domain DN.
406 :param root_uid: uid of the UNIX root user.
407 :param nobody_uid: uid of the UNIX nobody user.
408 :param users_gid: gid of the UNIX users group.
409 :param wheel_gid: gid of the UNIX wheel group."""
410 # add some foreign sids if they are not present already
411 samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
412 samdb.add_foreign(domaindn, "S-1-1-0", "World")
413 samdb.add_foreign(domaindn, "S-1-5-2", "Network")
414 samdb.add_foreign(domaindn, "S-1-5-18", "System")
415 samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
417 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
418 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
420 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
421 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
424 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
426 serverrole, ldap_backend=None,
427 ldap_backend_type=None, erase=False):
428 """Setup the partitions for the SAM database.
430 Alternatively, provision() may call this, and then populate the database.
432 :note: This will wipe the Sam Database!
434 :note: This function always removes the local SAM LDB file. The erase
435 parameter controls whether to erase the existing data, which
436 may not be stored locally but in LDAP.
438 assert session_info is not None
441 samdb = SamDB(samdb_path, session_info=session_info,
442 credentials=credentials, lp=lp)
446 os.unlink(samdb_path)
447 samdb = SamDB(samdb_path, session_info=session_info,
448 credentials=credentials, lp=lp)
453 #Add modules to the list to activate them by default
454 #beware often order is important
456 # Some Known ordering constraints:
457 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
458 # - objectclass must be before password_hash, because password_hash checks
459 # that the objectclass is of type person (filled in by objectclass
460 # module when expanding the objectclass list)
461 # - partition must be last
462 # - each partition has its own module list then
463 modules_list = ["rootdse",
481 "extended_dn_out_ldb"]
482 modules_list2 = ["show_deleted",
485 domaindn_ldb = "users.ldb"
486 if ldap_backend is not None:
487 domaindn_ldb = ldap_backend
488 configdn_ldb = "configuration.ldb"
489 if ldap_backend is not None:
490 configdn_ldb = ldap_backend
491 schemadn_ldb = "schema.ldb"
492 if ldap_backend is not None:
493 schema_ldb = ldap_backend
494 schemadn_ldb = ldap_backend
496 if ldap_backend_type == "fedora-ds":
497 backend_modules = ["nsuniqueid", "paged_searches"]
498 # We can handle linked attributes here, as we don't have directory-side subtree operations
499 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
500 elif ldap_backend_type == "openldap":
501 backend_modules = ["entryuuid", "paged_searches"]
502 # OpenLDAP handles subtree renames, so we don't want to do any of these things
503 tdb_modules_list = ["extended_dn_out_dereference"]
504 elif ldap_backend is not None:
505 raise "LDAP Backend specified, but LDAP Backend Type not specified"
506 elif serverrole == "domain controller":
507 backend_modules = ["repl_meta_data"]
509 backend_modules = ["objectguid"]
511 if tdb_modules_list is None:
512 tdb_modules_list_as_string = ""
514 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
516 samdb.transaction_start()
518 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
519 "SCHEMADN": names.schemadn,
520 "SCHEMADN_LDB": schemadn_ldb,
521 "SCHEMADN_MOD2": ",objectguid",
522 "CONFIGDN": names.configdn,
523 "CONFIGDN_LDB": configdn_ldb,
524 "DOMAINDN": names.domaindn,
525 "DOMAINDN_LDB": domaindn_ldb,
526 "SCHEMADN_MOD": "schema_fsmo,instancetype",
527 "CONFIGDN_MOD": "naming_fsmo,instancetype",
528 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
529 "MODULES_LIST": ",".join(modules_list),
530 "TDB_MODULES_LIST": tdb_modules_list_as_string,
531 "MODULES_LIST2": ",".join(modules_list2),
532 "BACKEND_MOD": ",".join(backend_modules),
536 samdb.transaction_cancel()
539 samdb.transaction_commit()
541 samdb = SamDB(samdb_path, session_info=session_info,
542 credentials=credentials, lp=lp)
544 samdb.transaction_start()
546 message("Setting up sam.ldb attributes")
547 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
549 message("Setting up sam.ldb rootDSE")
550 setup_samdb_rootdse(samdb, setup_path, names)
553 message("Erasing data from partitions")
554 samdb.erase_partitions()
557 samdb.transaction_cancel()
560 samdb.transaction_commit()
565 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
566 netbiosname, domainsid, keytab_path, samdb_url,
567 dns_keytab_path, dnspass, machinepass):
568 """Add DC-specific bits to a secrets database.
570 :param secretsdb: Ldb Handle to the secrets database
571 :param setup_path: Setup path function
572 :param machinepass: Machine password
574 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
575 "MACHINEPASS_B64": b64encode(machinepass),
578 "DNSDOMAIN": dnsdomain,
579 "DOMAINSID": str(domainsid),
580 "SECRETS_KEYTAB": keytab_path,
581 "NETBIOSNAME": netbiosname,
582 "SAM_LDB": samdb_url,
583 "DNS_KEYTAB": dns_keytab_path,
584 "DNSPASS_B64": b64encode(dnspass),
588 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
589 """Setup the secrets database.
591 :param path: Path to the secrets database.
592 :param setup_path: Get the path to a setup file.
593 :param session_info: Session info.
594 :param credentials: Credentials
595 :param lp: Loadparm context
596 :return: LDB handle for the created secrets database
598 if os.path.exists(path):
600 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
603 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
604 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
606 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
608 if credentials is not None and credentials.authentication_requested():
609 if credentials.get_bind_dn() is not None:
610 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
611 "LDAPMANAGERDN": credentials.get_bind_dn(),
612 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
615 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
616 "LDAPADMINUSER": credentials.get_username(),
617 "LDAPADMINREALM": credentials.get_realm(),
618 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
624 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
625 """Setup the templates database.
627 :param path: Path to the database.
628 :param setup_path: Function for obtaining the path to setup files.
629 :param session_info: Session info
630 :param credentials: Credentials
631 :param lp: Loadparm context
633 templates_ldb = SamDB(path, session_info=session_info,
634 credentials=credentials, lp=lp)
637 templates_ldb.erase()
641 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
643 templates_ldb = SamDB(path, session_info=session_info,
644 credentials=credentials, lp=lp)
646 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
649 def setup_registry(path, setup_path, session_info, credentials, lp):
650 """Setup the registry.
652 :param path: Path to the registry database
653 :param setup_path: Function that returns the path to a setup.
654 :param session_info: Session information
655 :param credentials: Credentials
656 :param lp: Loadparm context
658 reg = registry.Registry()
659 hive = registry.open_ldb(path, session_info=session_info,
660 credentials=credentials, lp_ctx=lp)
661 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
662 provision_reg = setup_path("provision.reg")
663 assert os.path.exists(provision_reg)
664 reg.diff_apply(provision_reg)
667 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
668 """Setup the idmap database.
670 :param path: path to the idmap database
671 :param setup_path: Function that returns a path to a setup file
672 :param session_info: Session information
673 :param credentials: Credentials
674 :param lp: Loadparm context
676 if os.path.exists(path):
679 idmap_ldb = IDmapDB(path, session_info=session_info,
680 credentials=credentials, lp=lp)
683 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
687 def setup_samdb_rootdse(samdb, setup_path, names):
688 """Setup the SamDB rootdse.
690 :param samdb: Sam Database handle
691 :param setup_path: Obtain setup path
693 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
694 "SCHEMADN": names.schemadn,
695 "NETBIOSNAME": names.netbiosname,
696 "DNSDOMAIN": names.dnsdomain,
697 "REALM": names.realm,
698 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
699 "DOMAINDN": names.domaindn,
700 "ROOTDN": names.rootdn,
701 "CONFIGDN": names.configdn,
702 "SERVERDN": names.serverdn,
706 def setup_self_join(samdb, names,
707 machinepass, dnspass,
708 domainsid, invocationid, setup_path,
710 """Join a host to its own domain."""
711 assert isinstance(invocationid, str)
712 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
713 "CONFIGDN": names.configdn,
714 "SCHEMADN": names.schemadn,
715 "DOMAINDN": names.domaindn,
716 "SERVERDN": names.serverdn,
717 "INVOCATIONID": invocationid,
718 "NETBIOSNAME": names.netbiosname,
719 "DEFAULTSITE": names.sitename,
720 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
721 "MACHINEPASS_B64": b64encode(machinepass),
722 "DNSPASS_B64": b64encode(dnspass),
723 "REALM": names.realm,
724 "DOMAIN": names.domain,
725 "DNSDOMAIN": names.dnsdomain})
726 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
727 "POLICYGUID": policyguid,
728 "DNSDOMAIN": names.dnsdomain,
729 "DOMAINSID": str(domainsid),
730 "DOMAINDN": names.domaindn})
733 def setup_samdb(path, setup_path, session_info, credentials, lp,
735 domainsid, aci, domainguid, policyguid,
736 fill, adminpass, krbtgtpass,
737 machinepass, invocationid, dnspass,
738 serverrole, ldap_backend=None,
739 ldap_backend_type=None):
740 """Setup a complete SAM Database.
742 :note: This will wipe the main SAM database file!
745 erase = (fill != FILL_DRS)
747 # Also wipes the database
748 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
749 credentials=credentials, session_info=session_info,
751 ldap_backend=ldap_backend, serverrole=serverrole,
752 ldap_backend_type=ldap_backend_type, erase=erase)
754 samdb = SamDB(path, session_info=session_info,
755 credentials=credentials, lp=lp)
759 message("Pre-loading the Samba 4 and AD schema")
760 samdb.set_domain_sid(str(domainsid))
761 if serverrole == "domain controller":
762 samdb.set_invocation_id(invocationid)
764 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
765 names.configdn, names.sitename, names.serverdn,
768 samdb.transaction_start()
771 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
772 if serverrole == "domain controller":
773 domain_oc = "domainDNS"
775 domain_oc = "samba4LocalDomain"
777 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
778 "DOMAINDN": names.domaindn,
780 "DOMAIN_OC": domain_oc
783 message("Modifying DomainDN: " + names.domaindn + "")
784 if domainguid is not None:
785 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
789 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
790 "LDAPTIME": timestring(int(time.time())),
791 "DOMAINSID": str(domainsid),
792 "SCHEMADN": names.schemadn,
793 "NETBIOSNAME": names.netbiosname,
794 "DEFAULTSITE": names.sitename,
795 "CONFIGDN": names.configdn,
796 "SERVERDN": names.serverdn,
797 "POLICYGUID": policyguid,
798 "DOMAINDN": names.domaindn,
799 "DOMAINGUID_MOD": domainguid_mod,
802 message("Adding configuration container (permitted to fail)")
803 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
804 "CONFIGDN": names.configdn,
807 message("Modifying configuration container")
808 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
809 "CONFIGDN": names.configdn,
810 "SCHEMADN": names.schemadn,
813 message("Adding schema container (permitted to fail)")
814 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
815 "SCHEMADN": names.schemadn,
818 message("Modifying schema container")
820 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
822 setup_modify_ldif(samdb,
823 setup_path("provision_schema_basedn_modify.ldif"), {
824 "SCHEMADN": names.schemadn,
825 "NETBIOSNAME": names.netbiosname,
826 "DEFAULTSITE": names.sitename,
827 "CONFIGDN": names.configdn,
828 "SERVERDN": names.serverdn,
829 "PREFIXMAP_B64": b64encode(prefixmap)
832 message("Setting up sam.ldb Samba4 schema")
833 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
834 {"SCHEMADN": names.schemadn })
835 message("Setting up sam.ldb AD schema")
836 setup_add_ldif(samdb, setup_path("schema.ldif"),
837 {"SCHEMADN": names.schemadn})
838 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
839 {"SCHEMADN": names.schemadn})
841 message("Setting up sam.ldb configuration data")
842 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
843 "CONFIGDN": names.configdn,
844 "NETBIOSNAME": names.netbiosname,
845 "DEFAULTSITE": names.sitename,
846 "DNSDOMAIN": names.dnsdomain,
847 "DOMAIN": names.domain,
848 "SCHEMADN": names.schemadn,
849 "DOMAINDN": names.domaindn,
850 "SERVERDN": names.serverdn
853 message("Setting up display specifiers")
854 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
855 {"CONFIGDN": names.configdn})
857 message("Adding users container (permitted to fail)")
858 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
859 "DOMAINDN": names.domaindn})
860 message("Modifying users container")
861 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
862 "DOMAINDN": names.domaindn})
863 message("Adding computers container (permitted to fail)")
864 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
865 "DOMAINDN": names.domaindn})
866 message("Modifying computers container")
867 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
868 "DOMAINDN": names.domaindn})
869 message("Setting up sam.ldb data")
870 setup_add_ldif(samdb, setup_path("provision.ldif"), {
871 "DOMAINDN": names.domaindn,
872 "NETBIOSNAME": names.netbiosname,
873 "DEFAULTSITE": names.sitename,
874 "CONFIGDN": names.configdn,
875 "SERVERDN": names.serverdn
878 if fill == FILL_FULL:
879 message("Setting up sam.ldb users and groups")
880 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
881 "DOMAINDN": names.domaindn,
882 "DOMAINSID": str(domainsid),
883 "CONFIGDN": names.configdn,
884 "ADMINPASS_B64": b64encode(adminpass),
885 "KRBTGTPASS_B64": b64encode(krbtgtpass),
888 if serverrole == "domain controller":
889 message("Setting up self join")
890 setup_self_join(samdb, names=names, invocationid=invocationid,
892 machinepass=machinepass,
893 domainsid=domainsid, policyguid=policyguid,
894 setup_path=setup_path)
897 samdb.transaction_cancel()
900 samdb.transaction_commit()
905 FILL_NT4SYNC = "NT4SYNC"
908 def provision(setup_dir, message, session_info,
909 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
910 rootdn=None, domaindn=None, schemadn=None, configdn=None,
912 domain=None, hostname=None, hostip=None, hostip6=None,
913 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
914 policyguid=None, invocationid=None, machinepass=None,
915 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
916 wheel=None, backup=None, aci=None, serverrole=None,
917 ldap_backend=None, ldap_backend_type=None, sitename=None):
920 :note: caution, this wipes all existing data!
923 def setup_path(file):
924 return os.path.join(setup_dir, file)
926 if domainsid is None:
927 domainsid = security.random_sid()
929 if policyguid is None:
930 policyguid = str(uuid.uuid4())
931 if adminpass is None:
932 adminpass = glue.generate_random_str(12)
933 if krbtgtpass is None:
934 krbtgtpass = glue.generate_random_str(12)
935 if machinepass is None:
936 machinepass = glue.generate_random_str(12)
938 dnspass = glue.generate_random_str(12)
939 root_uid = findnss_uid([root or "root"])
940 nobody_uid = findnss_uid([nobody or "nobody"])
941 users_gid = findnss_gid([users or "users"])
943 wheel_gid = findnss_gid(["wheel", "adm"])
945 wheel_gid = findnss_gid([wheel])
947 aci = "# no aci for local ldb"
949 if targetdir is not None:
950 if (not os.path.exists(os.path.join(targetdir, "etc"))):
951 os.makedirs(os.path.join(targetdir, "etc"))
952 smbconf = os.path.join(targetdir, "etc", "smb.conf")
953 elif smbconf is None:
954 smbconf = param.default_path()
956 # only install a new smb.conf if there isn't one there already
957 if not os.path.exists(smbconf):
958 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
961 lp = param.LoadParm()
964 names = guess_names(lp=lp, hostname=hostname, domain=domain,
965 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
966 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
969 paths = provision_paths_from_lp(lp, names.dnsdomain)
973 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
974 except socket.gaierror, (socket.EAI_NODATA, msg):
979 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
980 except socket.gaierror, (socket.EAI_NODATA, msg):
983 if serverrole is None:
984 serverrole = lp.get("server role")
986 assert serverrole in ("domain controller", "member server", "standalone")
987 if invocationid is None and serverrole == "domain controller":
988 invocationid = str(uuid.uuid4())
990 if not os.path.exists(paths.private_dir):
991 os.mkdir(paths.private_dir)
993 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
995 if ldap_backend is not None:
996 if ldap_backend == "ldapi":
997 # provision-backend will set this path suggested slapd command line / fedorads.inf
998 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1000 # only install a new shares config db if there is none
1001 if not os.path.exists(paths.shareconf):
1002 message("Setting up share.ldb")
1003 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1004 credentials=credentials, lp=lp)
1005 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1008 message("Setting up secrets.ldb")
1009 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1010 session_info=session_info,
1011 credentials=credentials, lp=lp)
1013 message("Setting up the registry")
1014 setup_registry(paths.hklm, setup_path, session_info,
1015 credentials=credentials, lp=lp)
1017 message("Setting up templates db")
1018 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1019 credentials=credentials, lp=lp)
1021 message("Setting up idmap db")
1022 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1023 credentials=credentials, lp=lp)
1025 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1026 credentials=credentials, lp=lp, names=names,
1028 domainsid=domainsid,
1029 aci=aci, domainguid=domainguid, policyguid=policyguid,
1031 adminpass=adminpass, krbtgtpass=krbtgtpass,
1032 invocationid=invocationid,
1033 machinepass=machinepass, dnspass=dnspass,
1034 serverrole=serverrole, ldap_backend=ldap_backend,
1035 ldap_backend_type=ldap_backend_type)
1037 if lp.get("server role") == "domain controller":
1038 if paths.netlogon is None:
1039 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1040 message("Please either remove %s or see the template at %s" %
1041 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1042 assert(paths.netlogon is not None)
1044 if paths.sysvol is None:
1045 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1046 message("Please either remove %s or see the template at %s" %
1047 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1048 assert(paths.sysvol is not None)
1050 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1051 "{" + policyguid + "}")
1052 os.makedirs(policy_path, 0755)
1053 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1054 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1055 os.makedirs(os.path.join(policy_path, "User"), 0755)
1056 if not os.path.isdir(paths.netlogon):
1057 os.makedirs(paths.netlogon, 0755)
1059 if samdb_fill == FILL_FULL:
1060 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1061 root_uid=root_uid, nobody_uid=nobody_uid,
1062 users_gid=users_gid, wheel_gid=wheel_gid)
1064 message("Setting up sam.ldb rootDSE marking as synchronized")
1065 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1067 # Only make a zone file on the first DC, it should be replicated with DNS replication
1068 if serverrole == "domain controller":
1069 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1070 credentials=credentials, lp=lp)
1071 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1072 netbiosname=names.netbiosname, domainsid=domainsid,
1073 keytab_path=paths.keytab, samdb_url=paths.samdb,
1074 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1075 machinepass=machinepass, dnsdomain=names.dnsdomain)
1077 samdb = SamDB(paths.samdb, session_info=session_info,
1078 credentials=credentials, lp=lp)
1080 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1081 assert isinstance(domainguid, str)
1082 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1083 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1084 scope=SCOPE_SUBTREE)
1085 assert isinstance(hostguid, str)
1087 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1088 domaindn=names.domaindn, hostip=hostip,
1089 hostip6=hostip6, hostname=names.hostname,
1090 dnspass=dnspass, realm=names.realm,
1091 domainguid=domainguid, hostguid=hostguid)
1093 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1094 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1096 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1097 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1098 keytab_name=paths.dns_keytab)
1099 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1100 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1102 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1103 hostname=names.hostname, realm=names.realm)
1104 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1106 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1109 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1111 message("Once the above files are installed, your Samba4 server will be ready to use")
1112 message("Server Role: %s" % serverrole)
1113 message("Hostname: %s" % names.hostname)
1114 message("NetBIOS Domain: %s" % names.domain)
1115 message("DNS Domain: %s" % names.dnsdomain)
1116 message("DOMAIN SID: %s" % str(domainsid))
1117 message("Admin password: %s" % adminpass)
1119 result = ProvisionResult()
1120 result.domaindn = domaindn
1121 result.paths = paths
1123 result.samdb = samdb
1127 def provision_become_dc(setup_dir=None,
1128 smbconf=None, targetdir=None, realm=None,
1129 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1131 domain=None, hostname=None, domainsid=None,
1132 adminpass=None, krbtgtpass=None, domainguid=None,
1133 policyguid=None, invocationid=None, machinepass=None,
1134 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1135 wheel=None, backup=None, aci=None, serverrole=None,
1136 ldap_backend=None, ldap_backend_type=None, sitename=None):
1139 """print a message if quiet is not set."""
1142 return provision(setup_dir, message, system_session(), None,
1143 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1144 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1145 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1148 def setup_db_config(setup_path, dbdir):
1149 """Setup a Berkeley database.
1151 :param setup_path: Setup path function.
1152 :param dbdir: Database directory."""
1153 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1154 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1155 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1156 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1158 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1159 {"LDAPDBDIR": dbdir})
1163 def provision_backend(setup_dir=None, message=None,
1164 smbconf=None, targetdir=None, realm=None,
1165 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1166 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1167 ldap_backend_type=None, ldap_backend_port=None,
1170 def setup_path(file):
1171 return os.path.join(setup_dir, file)
1173 if hostname is None:
1174 hostname = socket.gethostname().split(".")[0].lower()
1177 root = findnss(pwd.getpwnam, ["root"])[0]
1179 if adminpass is None:
1180 adminpass = glue.generate_random_str(12)
1182 if targetdir is not None:
1183 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1184 os.makedirs(os.path.join(targetdir, "etc"))
1185 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1186 elif smbconf is None:
1187 smbconf = param.default_path()
1188 assert smbconf is not None
1190 # only install a new smb.conf if there isn't one there already
1191 if not os.path.exists(smbconf):
1192 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1195 lp = param.LoadParm()
1198 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1199 dnsdomain=realm, serverrole=serverrole,
1200 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1203 paths = provision_paths_from_lp(lp, names.dnsdomain)
1205 if not os.path.isdir(paths.ldapdir):
1206 os.makedirs(paths.ldapdir, 0700)
1207 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1209 os.unlink(schemadb_path)
1213 schemadb = Ldb(schemadb_path, lp=lp)
1215 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1217 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1218 {"SCHEMADN": names.schemadn,
1221 setup_modify_ldif(schemadb,
1222 setup_path("provision_schema_basedn_modify.ldif"), \
1223 {"SCHEMADN": names.schemadn,
1224 "NETBIOSNAME": names.netbiosname,
1225 "DEFAULTSITE": DEFAULTSITE,
1226 "CONFIGDN": names.configdn,
1227 "SERVERDN": names.serverdn,
1228 "PREFIXMAP_B64": b64encode(prefixmap)
1231 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1232 {"SCHEMADN": names.schemadn })
1233 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1234 {"SCHEMADN": names.schemadn})
1236 if ldap_backend_type == "fedora-ds":
1237 if ldap_backend_port is not None:
1238 serverport = "ServerPort=%d" % ldap_backend_port
1242 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1244 "HOSTNAME": hostname,
1245 "DNSDOMAIN": names.dnsdomain,
1246 "LDAPDIR": paths.ldapdir,
1247 "DOMAINDN": names.domaindn,
1248 "LDAPMANAGERDN": names.ldapmanagerdn,
1249 "LDAPMANAGERPASS": adminpass,
1250 "SERVERPORT": serverport})
1252 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1253 {"CONFIGDN": names.configdn,
1254 "SCHEMADN": names.schemadn,
1257 mapping = "schema-map-fedora-ds-1.0"
1258 backend_schema = "99_ad.ldif"
1260 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1262 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1264 elif ldap_backend_type == "openldap":
1265 attrs = ["linkID", "lDAPDisplayName"]
1266 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)
1268 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1269 refint_attributes = ""
1270 for i in range (0, len(res)):
1271 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1272 target = schemadb.searchone(basedn=names.schemadn,
1273 expression=expression,
1274 attribute="lDAPDisplayName",
1275 scope=SCOPE_SUBTREE)
1276 if target is not None:
1277 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1279 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1280 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1281 "MEMBEROF_ATTR" : str(target) })
1283 refint_config = read_and_sub_file(setup_path("refint.conf"),
1284 { "LINK_ATTRS" : refint_attributes})
1286 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1288 mmr_replicator_acl = ""
1289 mmr_serverids_config = ""
1290 mmr_syncrepl_schema_config = ""
1291 mmr_syncrepl_config_config = ""
1292 mmr_syncrepl_user_config = ""
1294 if ol_mmr_urls is not None:
1295 # For now, make these equal
1296 mmr_pass = adminpass
1298 url_list=filter(None,ol_mmr_urls.split(' '))
1299 if (len(url_list) == 1):
1300 url_list=filter(None,ol_mmr_urls.split(','))
1303 mmr_on_config = "MirrorMode On"
1304 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1306 for url in url_list:
1308 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1309 { "SERVERID" : str(serverid),
1310 "LDAPSERVER" : url })
1313 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1315 "MMRDN": names.schemadn,
1317 "MMR_PASSWORD": mmr_pass})
1320 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1322 "MMRDN": names.configdn,
1324 "MMR_PASSWORD": mmr_pass})
1327 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1329 "MMRDN": names.domaindn,
1331 "MMR_PASSWORD": mmr_pass })
1334 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1335 {"DNSDOMAIN": names.dnsdomain,
1336 "LDAPDIR": paths.ldapdir,
1337 "DOMAINDN": names.domaindn,
1338 "CONFIGDN": names.configdn,
1339 "SCHEMADN": names.schemadn,
1340 "MEMBEROF_CONFIG": memberof_config,
1341 "MIRRORMODE": mmr_on_config,
1342 "REPLICATOR_ACL": mmr_replicator_acl,
1343 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1344 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1345 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1346 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1347 "REFINT_CONFIG": refint_config})
1348 setup_file(setup_path("modules.conf"), paths.modulesconf,
1349 {"REALM": names.realm})
1351 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1352 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1353 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1355 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1356 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1358 setup_file(setup_path("cn=samba.ldif"),
1359 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1360 { "UUID": str(uuid.uuid4()),
1361 "LDAPTIME": timestring(int(time.time()))} )
1362 setup_file(setup_path("cn=samba-admin.ldif"),
1363 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1364 {"LDAPADMINPASS_B64": b64encode(adminpass),
1365 "UUID": str(uuid.uuid4()),
1366 "LDAPTIME": timestring(int(time.time()))} )
1368 if ol_mmr_urls is not None:
1369 setup_file(setup_path("cn=replicator.ldif"),
1370 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1371 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1372 "UUID": str(uuid.uuid4()),
1373 "LDAPTIME": timestring(int(time.time()))} )
1377 mapping = "schema-map-openldap-2.3"
1378 backend_schema = "backend-schema.schema"
1380 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1381 if ldap_backend_port is not None:
1382 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1384 server_port_string = ""
1386 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1388 ldapuser = "--username=samba-admin"
1391 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)
1393 os.system(schema_command)
1395 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1396 message("Server Role: %s" % serverrole)
1397 message("Hostname: %s" % names.hostname)
1398 message("DNS Domain: %s" % names.dnsdomain)
1399 message("Base DN: %s" % names.domaindn)
1401 if ldap_backend_type == "openldap":
1402 message("LDAP admin user: samba-admin")
1404 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1406 message("LDAP admin password: %s" % adminpass)
1407 message(slapdcommand)
1408 message("Run provision with: --ldap-backend=ldapi --ldap-backend-type=" + ldap_backend_type + " --password=" + adminpass + " " + ldapuser + "--realm=" + names.dnsdomain + " --domain=" + names.domain + " --server-role='" + serverrole + "'")
1410 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1411 """Create a PHP LDAP admin configuration file.
1413 :param path: Path to write the configuration to.
1414 :param setup_path: Function to generate setup paths.
1416 setup_file(setup_path("phpldapadmin-config.php"), path,
1417 {"S4_LDAPI_URI": ldapi_uri})
1420 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1421 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1422 """Write out a DNS zone file, from the info in the current database.
1424 :param path: Path of the new zone file.
1425 :param setup_path: Setup path function.
1426 :param dnsdomain: DNS Domain name
1427 :param domaindn: DN of the Domain
1428 :param hostip: Local IPv4 IP
1429 :param hostip6: Local IPv6 IP
1430 :param hostname: Local hostname
1431 :param dnspass: Password for DNS
1432 :param realm: Realm name
1433 :param domainguid: GUID of the domain.
1434 :param hostguid: GUID of the host.
1436 assert isinstance(domainguid, str)
1438 if hostip6 is not None:
1439 hostip6_base_line = " IN AAAA " + hostip6
1440 hostip6_host_line = hostname + " IN AAAA " + hostip6
1442 hostip6_base_line = ""
1443 hostip6_host_line = ""
1445 if hostip is not None:
1446 hostip_base_line = " IN A " + hostip
1447 hostip_host_line = hostname + " IN A " + hostip
1449 hostip_base_line = ""
1450 hostip_host_line = ""
1452 setup_file(setup_path("provision.zone"), path, {
1453 "DNSPASS_B64": b64encode(dnspass),
1454 "HOSTNAME": hostname,
1455 "DNSDOMAIN": dnsdomain,
1457 "HOSTIP_BASE_LINE": hostip_base_line,
1458 "HOSTIP_HOST_LINE": hostip_host_line,
1459 "DOMAINGUID": domainguid,
1460 "DATESTRING": time.strftime("%Y%m%d%H"),
1461 "DEFAULTSITE": DEFAULTSITE,
1462 "HOSTGUID": hostguid,
1463 "HOSTIP6_BASE_LINE": hostip6_base_line,
1464 "HOSTIP6_HOST_LINE": hostip6_host_line,
1468 def create_named_conf(path, setup_path, realm, dnsdomain,
1470 """Write out a file containing zone statements suitable for inclusion in a
1471 named.conf file (including GSS-TSIG configuration).
1473 :param path: Path of the new named.conf file.
1474 :param setup_path: Setup path function.
1475 :param realm: Realm name
1476 :param dnsdomain: DNS Domain name
1477 :param private_dir: Path to private directory
1478 :param keytab_name: File name of DNS keytab file
1481 setup_file(setup_path("named.conf"), path, {
1482 "DNSDOMAIN": dnsdomain,
1484 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1485 "PRIVATE_DIR": private_dir
1488 def create_named_txt(path, setup_path, realm, dnsdomain,
1489 private_dir, keytab_name):
1490 """Write out a file containing zone statements suitable for inclusion in a
1491 named.conf file (including GSS-TSIG configuration).
1493 :param path: Path of the new named.conf file.
1494 :param setup_path: Setup path function.
1495 :param realm: Realm name
1496 :param dnsdomain: DNS Domain name
1497 :param private_dir: Path to private directory
1498 :param keytab_name: File name of DNS keytab file
1501 setup_file(setup_path("named.txt"), path, {
1502 "DNSDOMAIN": dnsdomain,
1504 "DNS_KEYTAB": keytab_name,
1505 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1506 "PRIVATE_DIR": private_dir
1509 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1510 """Write out a file containing zone statements suitable for inclusion in a
1511 named.conf file (including GSS-TSIG configuration).
1513 :param path: Path of the new named.conf file.
1514 :param setup_path: Setup path function.
1515 :param dnsdomain: DNS Domain name
1516 :param hostname: Local hostname
1517 :param realm: Realm name
1520 setup_file(setup_path("krb5.conf"), path, {
1521 "DNSDOMAIN": dnsdomain,
1522 "HOSTNAME": hostname,
1527 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1528 serverdn, servername):
1529 """Load schema for the SamDB.
1531 :param samdb: Load a schema into a SamDB.
1532 :param setup_path: Setup path function.
1533 :param schemadn: DN of the schema
1534 :param netbiosname: NetBIOS name of the host.
1535 :param configdn: DN of the configuration
1536 :param serverdn: DN of the server
1537 :param servername: Host name of the server
1539 schema_data = open(setup_path("schema.ldif"), 'r').read()
1540 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1541 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1542 check_all_substituted(schema_data)
1543 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1544 prefixmap = b64encode(prefixmap)
1546 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1547 head_data = substitute_var(head_data, {
1548 "SCHEMADN": schemadn,
1549 "NETBIOSNAME": netbiosname,
1550 "CONFIGDN": configdn,
1551 "DEFAULTSITE": sitename,
1552 "PREFIXMAP_B64": prefixmap,
1553 "SERVERDN": serverdn,
1554 "SERVERNAME": servername,
1556 check_all_substituted(head_data)
1557 samdb.attach_schema_from_ldif(head_data, schema_data)