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"
50 """Find the setup directory used by provision."""
51 dirname = os.path.dirname(__file__)
52 if "/site-packages/" in dirname:
53 prefix = dirname[:dirname.index("/site-packages/")]
54 for suffix in ["share/setup", "share/samba/setup", "setup"]:
55 ret = os.path.join(prefix, suffix)
56 if os.path.isdir(ret):
59 ret = os.path.join(dirname, "../../../setup")
60 if os.path.isdir(ret):
62 raise Exception("Unable to find setup directory.")
65 DEFAULTSITE = "Default-First-Site-Name"
67 class InvalidNetbiosName(Exception):
68 """A specified name was not a valid NetBIOS name."""
69 def __init__(self, name):
70 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
73 class ProvisionPaths(object):
86 self.dns_keytab = None
89 self.private_dir = None
92 self.modulesconf = None
93 self.memberofconf = None
94 self.fedoradsinf = None
95 self.fedoradspartitions = None
97 self.olmmrserveridsconf = None
98 self.olmmrsyncreplconf = None
101 class ProvisionNames(object):
107 self.ldapmanagerdn = None
108 self.dnsdomain = None
110 self.netbiosname = None
117 class ProvisionResult(object):
124 def check_install(lp, session_info, credentials):
125 """Check whether the current install seems ok.
127 :param lp: Loadparm context
128 :param session_info: Session information
129 :param credentials: Credentials
131 if lp.get("realm") == "":
132 raise Exception("Realm empty")
133 ldb = Ldb(lp.get("sam database"), session_info=session_info,
134 credentials=credentials, lp=lp)
135 if len(ldb.search("(cn=Administrator)")) != 1:
136 raise "No administrator account found"
139 def findnss(nssfn, names):
140 """Find a user or group from a list of possibilities.
142 :param nssfn: NSS Function to try (should raise KeyError if not found)
143 :param names: Names to check.
144 :return: Value return by first names list.
151 raise KeyError("Unable to find user/group %r" % names)
154 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
155 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
158 def read_and_sub_file(file, subst_vars):
159 """Read a file and sub in variables found in it
161 :param file: File to be read (typically from setup directory)
162 param subst_vars: Optional variables to subsitute in the file.
164 data = open(file, 'r').read()
165 if subst_vars is not None:
166 data = substitute_var(data, subst_vars)
167 check_all_substituted(data)
171 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
172 """Setup a ldb in the private dir.
174 :param ldb: LDB file to import data into
175 :param ldif_path: Path of the LDIF file to load
176 :param subst_vars: Optional variables to subsitute in LDIF.
178 assert isinstance(ldif_path, str)
180 data = read_and_sub_file(ldif_path, subst_vars)
184 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
185 """Modify a ldb in the private dir.
187 :param ldb: LDB object.
188 :param ldif_path: LDIF file path.
189 :param subst_vars: Optional dictionary with substitution variables.
191 data = read_and_sub_file(ldif_path, subst_vars)
193 ldb.modify_ldif(data)
196 def setup_ldb(ldb, ldif_path, subst_vars):
197 """Import a LDIF a file into a LDB handle, optionally substituting variables.
199 :note: Either all LDIF data will be added or none (using transactions).
201 :param ldb: LDB file to import into.
202 :param ldif_path: Path to the LDIF file.
203 :param subst_vars: Dictionary with substitution variables.
205 assert ldb is not None
206 ldb.transaction_start()
208 setup_add_ldif(ldb, ldif_path, subst_vars)
210 ldb.transaction_cancel()
212 ldb.transaction_commit()
215 def setup_file(template, fname, subst_vars):
216 """Setup a file in the private dir.
218 :param template: Path of the template file.
219 :param fname: Path of the file to create.
220 :param subst_vars: Substitution variables.
224 if os.path.exists(f):
227 data = read_and_sub_file(template, subst_vars)
228 open(f, 'w').write(data)
231 def provision_paths_from_lp(lp, dnsdomain):
232 """Set the default paths for provisioning.
234 :param lp: Loadparm context.
235 :param dnsdomain: DNS Domain name
237 paths = ProvisionPaths()
238 paths.private_dir = lp.get("private dir")
239 paths.keytab = "secrets.keytab"
240 paths.dns_keytab = "dns.keytab"
242 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
243 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
244 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
245 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
246 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
247 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
248 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
249 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
250 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
251 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
252 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
253 paths.phpldapadminconfig = os.path.join(paths.private_dir,
254 "phpldapadmin-config.php")
255 paths.ldapdir = os.path.join(paths.private_dir,
257 paths.slapdconf = os.path.join(paths.ldapdir,
259 paths.modulesconf = os.path.join(paths.ldapdir,
261 paths.memberofconf = os.path.join(paths.ldapdir,
263 paths.fedoradsinf = os.path.join(paths.ldapdir,
265 paths.fedoradspartitions = os.path.join(paths.ldapdir,
266 "fedorads-partitions.ldif")
267 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
268 "mmr_serverids.conf")
269 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
271 paths.hklm = "hklm.ldb"
272 paths.hkcr = "hkcr.ldb"
273 paths.hkcu = "hkcu.ldb"
274 paths.hku = "hku.ldb"
275 paths.hkpd = "hkpd.ldb"
276 paths.hkpt = "hkpt.ldb"
278 paths.sysvol = lp.get("path", "sysvol")
280 paths.netlogon = lp.get("path", "netlogon")
282 paths.smbconf = lp.configfile
287 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
288 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
290 """Guess configuration settings to use."""
293 hostname = socket.gethostname().split(".")[0].lower()
295 netbiosname = hostname.upper()
296 if not valid_netbios_name(netbiosname):
297 raise InvalidNetbiosName(netbiosname)
299 hostname = hostname.lower()
301 if dnsdomain is None:
302 dnsdomain = lp.get("realm")
304 if serverrole is None:
305 serverrole = lp.get("server role")
307 assert dnsdomain is not None
308 realm = dnsdomain.upper()
310 if lp.get("realm").upper() != realm:
311 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
312 (lp.get("realm"), lp.configfile, realm))
314 dnsdomain = dnsdomain.lower()
316 if serverrole == "domain controller":
318 domain = lp.get("workgroup")
320 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
321 if lp.get("workgroup").upper() != domain.upper():
322 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
323 lp.get("workgroup"), domain)
327 domaindn = "CN=" + netbiosname
329 assert domain is not None
330 domain = domain.upper()
331 if not valid_netbios_name(domain):
332 raise InvalidNetbiosName(domain)
338 configdn = "CN=Configuration," + rootdn
340 schemadn = "CN=Schema," + configdn
345 names = ProvisionNames()
346 names.rootdn = rootdn
347 names.domaindn = domaindn
348 names.configdn = configdn
349 names.schemadn = schemadn
350 names.ldapmanagerdn = "CN=Manager," + rootdn
351 names.dnsdomain = dnsdomain
352 names.domain = domain
354 names.netbiosname = netbiosname
355 names.hostname = hostname
356 names.sitename = sitename
357 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
362 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
364 """Create a new smb.conf file based on a couple of basic settings.
366 assert smbconf is not None
368 hostname = socket.gethostname().split(".")[0].lower()
370 if serverrole is None:
371 serverrole = "standalone"
373 assert serverrole in ("domain controller", "member server", "standalone")
374 if serverrole == "domain controller":
376 elif serverrole == "member server":
377 smbconfsuffix = "member"
378 elif serverrole == "standalone":
379 smbconfsuffix = "standalone"
381 assert domain is not None
382 assert realm is not None
384 default_lp = param.LoadParm()
385 #Load non-existant file
386 if os.path.exists(smbconf):
387 default_lp.load(smbconf)
389 if targetdir is not None:
390 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
391 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
393 default_lp.set("lock dir", os.path.abspath(targetdir))
398 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
399 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
401 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
403 "HOSTNAME": hostname,
406 "SERVERROLE": serverrole,
407 "NETLOGONPATH": netlogon,
408 "SYSVOLPATH": sysvol,
409 "PRIVATEDIR_LINE": privatedir_line,
410 "LOCKDIR_LINE": lockdir_line
414 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
415 users_gid, wheel_gid):
416 """setup reasonable name mappings for sam names to unix names.
418 :param samdb: SamDB object.
419 :param idmap: IDmap db object.
420 :param sid: The domain sid.
421 :param domaindn: The domain DN.
422 :param root_uid: uid of the UNIX root user.
423 :param nobody_uid: uid of the UNIX nobody user.
424 :param users_gid: gid of the UNIX users group.
425 :param wheel_gid: gid of the UNIX wheel group."""
426 # add some foreign sids if they are not present already
427 samdb.add_stock_foreign_sids()
429 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
430 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
432 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
433 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
436 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
438 serverrole, ldap_backend=None,
439 ldap_backend_type=None, erase=False):
440 """Setup the partitions for the SAM database.
442 Alternatively, provision() may call this, and then populate the database.
444 :note: This will wipe the Sam Database!
446 :note: This function always removes the local SAM LDB file. The erase
447 parameter controls whether to erase the existing data, which
448 may not be stored locally but in LDAP.
450 assert session_info is not None
453 samdb = SamDB(samdb_path, session_info=session_info,
454 credentials=credentials, lp=lp)
458 os.unlink(samdb_path)
459 samdb = SamDB(samdb_path, session_info=session_info,
460 credentials=credentials, lp=lp)
465 #Add modules to the list to activate them by default
466 #beware often order is important
468 # Some Known ordering constraints:
469 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
470 # - objectclass must be before password_hash, because password_hash checks
471 # that the objectclass is of type person (filled in by objectclass
472 # module when expanding the objectclass list)
473 # - partition must be last
474 # - each partition has its own module list then
475 modules_list = ["rootdse",
493 "extended_dn_out_ldb"]
494 modules_list2 = ["show_deleted",
497 domaindn_ldb = "users.ldb"
498 if ldap_backend is not None:
499 domaindn_ldb = ldap_backend
500 configdn_ldb = "configuration.ldb"
501 if ldap_backend is not None:
502 configdn_ldb = ldap_backend
503 schemadn_ldb = "schema.ldb"
504 if ldap_backend is not None:
505 schema_ldb = ldap_backend
506 schemadn_ldb = ldap_backend
508 if ldap_backend_type == "fedora-ds":
509 backend_modules = ["nsuniqueid", "paged_searches"]
510 # We can handle linked attributes here, as we don't have directory-side subtree operations
511 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
512 elif ldap_backend_type == "openldap":
513 backend_modules = ["entryuuid", "paged_searches"]
514 # OpenLDAP handles subtree renames, so we don't want to do any of these things
515 tdb_modules_list = ["extended_dn_out_dereference"]
516 elif ldap_backend is not None:
517 raise "LDAP Backend specified, but LDAP Backend Type not specified"
518 elif serverrole == "domain controller":
519 backend_modules = ["repl_meta_data"]
521 backend_modules = ["objectguid"]
523 if tdb_modules_list is None:
524 tdb_modules_list_as_string = ""
526 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
528 samdb.transaction_start()
530 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
531 "SCHEMADN": names.schemadn,
532 "SCHEMADN_LDB": schemadn_ldb,
533 "SCHEMADN_MOD2": ",objectguid",
534 "CONFIGDN": names.configdn,
535 "CONFIGDN_LDB": configdn_ldb,
536 "DOMAINDN": names.domaindn,
537 "DOMAINDN_LDB": domaindn_ldb,
538 "SCHEMADN_MOD": "schema_fsmo,instancetype",
539 "CONFIGDN_MOD": "naming_fsmo,instancetype",
540 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
541 "MODULES_LIST": ",".join(modules_list),
542 "TDB_MODULES_LIST": tdb_modules_list_as_string,
543 "MODULES_LIST2": ",".join(modules_list2),
544 "BACKEND_MOD": ",".join(backend_modules),
548 samdb.transaction_cancel()
551 samdb.transaction_commit()
553 samdb = SamDB(samdb_path, session_info=session_info,
554 credentials=credentials, lp=lp)
556 samdb.transaction_start()
558 message("Setting up sam.ldb attributes")
559 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
561 message("Setting up sam.ldb rootDSE")
562 setup_samdb_rootdse(samdb, setup_path, names)
565 message("Erasing data from partitions")
566 samdb.erase_partitions()
569 samdb.transaction_cancel()
572 samdb.transaction_commit()
577 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
578 netbiosname, domainsid, keytab_path, samdb_url,
579 dns_keytab_path, dnspass, machinepass):
580 """Add DC-specific bits to a secrets database.
582 :param secretsdb: Ldb Handle to the secrets database
583 :param setup_path: Setup path function
584 :param machinepass: Machine password
586 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
587 "MACHINEPASS_B64": b64encode(machinepass),
590 "DNSDOMAIN": dnsdomain,
591 "DOMAINSID": str(domainsid),
592 "SECRETS_KEYTAB": keytab_path,
593 "NETBIOSNAME": netbiosname,
594 "SAM_LDB": samdb_url,
595 "DNS_KEYTAB": dns_keytab_path,
596 "DNSPASS_B64": b64encode(dnspass),
600 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
601 """Setup the secrets database.
603 :param path: Path to the secrets database.
604 :param setup_path: Get the path to a setup file.
605 :param session_info: Session info.
606 :param credentials: Credentials
607 :param lp: Loadparm context
608 :return: LDB handle for the created secrets database
610 if os.path.exists(path):
612 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
615 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
616 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
618 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
620 if credentials is not None and credentials.authentication_requested():
621 if credentials.get_bind_dn() is not None:
622 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
623 "LDAPMANAGERDN": credentials.get_bind_dn(),
624 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
627 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
628 "LDAPADMINUSER": credentials.get_username(),
629 "LDAPADMINREALM": credentials.get_realm(),
630 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
636 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
637 """Setup the templates database.
639 :param path: Path to the database.
640 :param setup_path: Function for obtaining the path to setup files.
641 :param session_info: Session info
642 :param credentials: Credentials
643 :param lp: Loadparm context
645 templates_ldb = SamDB(path, session_info=session_info,
646 credentials=credentials, lp=lp)
649 templates_ldb.erase()
653 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
655 templates_ldb = SamDB(path, session_info=session_info,
656 credentials=credentials, lp=lp)
658 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
661 def setup_registry(path, setup_path, session_info, credentials, lp):
662 """Setup the registry.
664 :param path: Path to the registry database
665 :param setup_path: Function that returns the path to a setup.
666 :param session_info: Session information
667 :param credentials: Credentials
668 :param lp: Loadparm context
670 reg = registry.Registry()
671 hive = registry.open_ldb(path, session_info=session_info,
672 credentials=credentials, lp_ctx=lp)
673 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
674 provision_reg = setup_path("provision.reg")
675 assert os.path.exists(provision_reg)
676 reg.diff_apply(provision_reg)
679 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
680 """Setup the idmap database.
682 :param path: path to the idmap database
683 :param setup_path: Function that returns a path to a setup file
684 :param session_info: Session information
685 :param credentials: Credentials
686 :param lp: Loadparm context
688 if os.path.exists(path):
691 idmap_ldb = IDmapDB(path, session_info=session_info,
692 credentials=credentials, lp=lp)
695 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
699 def setup_samdb_rootdse(samdb, setup_path, names):
700 """Setup the SamDB rootdse.
702 :param samdb: Sam Database handle
703 :param setup_path: Obtain setup path
705 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
706 "SCHEMADN": names.schemadn,
707 "NETBIOSNAME": names.netbiosname,
708 "DNSDOMAIN": names.dnsdomain,
709 "REALM": names.realm,
710 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
711 "DOMAINDN": names.domaindn,
712 "ROOTDN": names.rootdn,
713 "CONFIGDN": names.configdn,
714 "SERVERDN": names.serverdn,
718 def setup_self_join(samdb, names,
719 machinepass, dnspass,
720 domainsid, invocationid, setup_path,
722 """Join a host to its own domain."""
723 assert isinstance(invocationid, str)
724 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
725 "CONFIGDN": names.configdn,
726 "SCHEMADN": names.schemadn,
727 "DOMAINDN": names.domaindn,
728 "SERVERDN": names.serverdn,
729 "INVOCATIONID": invocationid,
730 "NETBIOSNAME": names.netbiosname,
731 "DEFAULTSITE": names.sitename,
732 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
733 "MACHINEPASS_B64": b64encode(machinepass),
734 "DNSPASS_B64": b64encode(dnspass),
735 "REALM": names.realm,
736 "DOMAIN": names.domain,
737 "DNSDOMAIN": names.dnsdomain})
738 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
739 "POLICYGUID": policyguid,
740 "DNSDOMAIN": names.dnsdomain,
741 "DOMAINSID": str(domainsid),
742 "DOMAINDN": names.domaindn})
745 def setup_samdb(path, setup_path, session_info, credentials, lp,
747 domainsid, aci, domainguid, policyguid,
748 fill, adminpass, krbtgtpass,
749 machinepass, invocationid, dnspass,
750 serverrole, ldap_backend=None,
751 ldap_backend_type=None):
752 """Setup a complete SAM Database.
754 :note: This will wipe the main SAM database file!
757 erase = (fill != FILL_DRS)
759 # Also wipes the database
760 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
761 credentials=credentials, session_info=session_info,
763 ldap_backend=ldap_backend, serverrole=serverrole,
764 ldap_backend_type=ldap_backend_type, erase=erase)
766 samdb = SamDB(path, session_info=session_info,
767 credentials=credentials, lp=lp)
771 message("Pre-loading the Samba 4 and AD schema")
772 samdb.set_domain_sid(str(domainsid))
773 if serverrole == "domain controller":
774 samdb.set_invocation_id(invocationid)
776 load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
777 names.configdn, names.sitename, names.serverdn,
780 samdb.transaction_start()
783 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
784 if serverrole == "domain controller":
785 domain_oc = "domainDNS"
787 domain_oc = "samba4LocalDomain"
789 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
790 "DOMAINDN": names.domaindn,
792 "DOMAIN_OC": domain_oc
795 message("Modifying DomainDN: " + names.domaindn + "")
796 if domainguid is not None:
797 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
801 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
802 "LDAPTIME": timestring(int(time.time())),
803 "DOMAINSID": str(domainsid),
804 "SCHEMADN": names.schemadn,
805 "NETBIOSNAME": names.netbiosname,
806 "DEFAULTSITE": names.sitename,
807 "CONFIGDN": names.configdn,
808 "SERVERDN": names.serverdn,
809 "POLICYGUID": policyguid,
810 "DOMAINDN": names.domaindn,
811 "DOMAINGUID_MOD": domainguid_mod,
814 message("Adding configuration container (permitted to fail)")
815 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
816 "CONFIGDN": names.configdn,
819 message("Modifying configuration container")
820 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
821 "CONFIGDN": names.configdn,
822 "SCHEMADN": names.schemadn,
825 message("Adding schema container (permitted to fail)")
826 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
827 "SCHEMADN": names.schemadn,
830 message("Modifying schema container")
832 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
834 setup_modify_ldif(samdb,
835 setup_path("provision_schema_basedn_modify.ldif"), {
836 "SCHEMADN": names.schemadn,
837 "NETBIOSNAME": names.netbiosname,
838 "DEFAULTSITE": names.sitename,
839 "CONFIGDN": names.configdn,
840 "SERVERDN": names.serverdn,
841 "PREFIXMAP_B64": b64encode(prefixmap)
844 message("Setting up sam.ldb Samba4 schema")
845 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
846 {"SCHEMADN": names.schemadn })
847 message("Setting up sam.ldb AD schema")
848 setup_add_ldif(samdb, setup_path("schema.ldif"),
849 {"SCHEMADN": names.schemadn})
850 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
851 {"SCHEMADN": names.schemadn})
853 message("Setting up sam.ldb configuration data")
854 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
855 "CONFIGDN": names.configdn,
856 "NETBIOSNAME": names.netbiosname,
857 "DEFAULTSITE": names.sitename,
858 "DNSDOMAIN": names.dnsdomain,
859 "DOMAIN": names.domain,
860 "SCHEMADN": names.schemadn,
861 "DOMAINDN": names.domaindn,
862 "SERVERDN": names.serverdn
865 message("Setting up display specifiers")
866 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
867 {"CONFIGDN": names.configdn})
869 message("Adding users container (permitted to fail)")
870 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
871 "DOMAINDN": names.domaindn})
872 message("Modifying users container")
873 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
874 "DOMAINDN": names.domaindn})
875 message("Adding computers container (permitted to fail)")
876 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
877 "DOMAINDN": names.domaindn})
878 message("Modifying computers container")
879 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
880 "DOMAINDN": names.domaindn})
881 message("Setting up sam.ldb data")
882 setup_add_ldif(samdb, setup_path("provision.ldif"), {
883 "DOMAINDN": names.domaindn,
884 "NETBIOSNAME": names.netbiosname,
885 "DEFAULTSITE": names.sitename,
886 "CONFIGDN": names.configdn,
887 "SERVERDN": names.serverdn
890 if fill == FILL_FULL:
891 message("Setting up sam.ldb users and groups")
892 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
893 "DOMAINDN": names.domaindn,
894 "DOMAINSID": str(domainsid),
895 "CONFIGDN": names.configdn,
896 "ADMINPASS_B64": b64encode(adminpass),
897 "KRBTGTPASS_B64": b64encode(krbtgtpass),
900 if serverrole == "domain controller":
901 message("Setting up self join")
902 setup_self_join(samdb, names=names, invocationid=invocationid,
904 machinepass=machinepass,
905 domainsid=domainsid, policyguid=policyguid,
906 setup_path=setup_path)
909 samdb.transaction_cancel()
912 samdb.transaction_commit()
917 FILL_NT4SYNC = "NT4SYNC"
920 def provision(setup_dir, message, session_info,
921 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
922 rootdn=None, domaindn=None, schemadn=None, configdn=None,
924 domain=None, hostname=None, hostip=None, hostip6=None,
925 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
926 policyguid=None, invocationid=None, machinepass=None,
927 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
928 wheel=None, backup=None, aci=None, serverrole=None,
929 ldap_backend=None, ldap_backend_type=None, sitename=None):
932 :note: caution, this wipes all existing data!
935 def setup_path(file):
936 return os.path.join(setup_dir, file)
938 if domainsid is None:
939 domainsid = security.random_sid()
941 if policyguid is None:
942 policyguid = str(uuid.uuid4())
943 if adminpass is None:
944 adminpass = glue.generate_random_str(12)
945 if krbtgtpass is None:
946 krbtgtpass = glue.generate_random_str(12)
947 if machinepass is None:
948 machinepass = glue.generate_random_str(12)
950 dnspass = glue.generate_random_str(12)
951 root_uid = findnss_uid([root or "root"])
952 nobody_uid = findnss_uid([nobody or "nobody"])
953 users_gid = findnss_gid([users or "users"])
955 wheel_gid = findnss_gid(["wheel", "adm"])
957 wheel_gid = findnss_gid([wheel])
959 aci = "# no aci for local ldb"
961 if targetdir is not None:
962 if (not os.path.exists(os.path.join(targetdir, "etc"))):
963 os.makedirs(os.path.join(targetdir, "etc"))
964 smbconf = os.path.join(targetdir, "etc", "smb.conf")
965 elif smbconf is None:
966 smbconf = param.default_path()
968 # only install a new smb.conf if there isn't one there already
969 if not os.path.exists(smbconf):
970 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
973 lp = param.LoadParm()
976 names = guess_names(lp=lp, hostname=hostname, domain=domain,
977 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
978 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
981 paths = provision_paths_from_lp(lp, names.dnsdomain)
985 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
986 except socket.gaierror, (socket.EAI_NODATA, msg):
991 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
992 except socket.gaierror, (socket.EAI_NODATA, msg):
995 if serverrole is None:
996 serverrole = lp.get("server role")
998 assert serverrole in ("domain controller", "member server", "standalone")
999 if invocationid is None and serverrole == "domain controller":
1000 invocationid = str(uuid.uuid4())
1002 if not os.path.exists(paths.private_dir):
1003 os.mkdir(paths.private_dir)
1005 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1007 if ldap_backend is not None:
1008 if ldap_backend == "ldapi":
1009 # provision-backend will set this path suggested slapd command line / fedorads.inf
1010 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1012 # only install a new shares config db if there is none
1013 if not os.path.exists(paths.shareconf):
1014 message("Setting up share.ldb")
1015 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1016 credentials=credentials, lp=lp)
1017 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1020 message("Setting up secrets.ldb")
1021 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1022 session_info=session_info,
1023 credentials=credentials, lp=lp)
1025 message("Setting up the registry")
1026 setup_registry(paths.hklm, setup_path, session_info,
1027 credentials=credentials, lp=lp)
1029 message("Setting up templates db")
1030 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1031 credentials=credentials, lp=lp)
1033 message("Setting up idmap db")
1034 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1035 credentials=credentials, lp=lp)
1037 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1038 credentials=credentials, lp=lp, names=names,
1040 domainsid=domainsid,
1041 aci=aci, domainguid=domainguid, policyguid=policyguid,
1043 adminpass=adminpass, krbtgtpass=krbtgtpass,
1044 invocationid=invocationid,
1045 machinepass=machinepass, dnspass=dnspass,
1046 serverrole=serverrole, ldap_backend=ldap_backend,
1047 ldap_backend_type=ldap_backend_type)
1049 if lp.get("server role") == "domain controller":
1050 if paths.netlogon is None:
1051 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1052 message("Please either remove %s or see the template at %s" %
1053 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1054 assert(paths.netlogon is not None)
1056 if paths.sysvol is None:
1057 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1058 message("Please either remove %s or see the template at %s" %
1059 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1060 assert(paths.sysvol is not None)
1062 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1063 "{" + policyguid + "}")
1064 os.makedirs(policy_path, 0755)
1065 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1066 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1067 os.makedirs(os.path.join(policy_path, "User"), 0755)
1068 if not os.path.isdir(paths.netlogon):
1069 os.makedirs(paths.netlogon, 0755)
1071 if samdb_fill == FILL_FULL:
1072 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1073 root_uid=root_uid, nobody_uid=nobody_uid,
1074 users_gid=users_gid, wheel_gid=wheel_gid)
1076 message("Setting up sam.ldb rootDSE marking as synchronized")
1077 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1079 # Only make a zone file on the first DC, it should be replicated with DNS replication
1080 if serverrole == "domain controller":
1081 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1082 credentials=credentials, lp=lp)
1083 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1084 netbiosname=names.netbiosname, domainsid=domainsid,
1085 keytab_path=paths.keytab, samdb_url=paths.samdb,
1086 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1087 machinepass=machinepass, dnsdomain=names.dnsdomain)
1089 samdb = SamDB(paths.samdb, session_info=session_info,
1090 credentials=credentials, lp=lp)
1092 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1093 assert isinstance(domainguid, str)
1094 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1095 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1096 scope=SCOPE_SUBTREE)
1097 assert isinstance(hostguid, str)
1099 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1100 domaindn=names.domaindn, hostip=hostip,
1101 hostip6=hostip6, hostname=names.hostname,
1102 dnspass=dnspass, realm=names.realm,
1103 domainguid=domainguid, hostguid=hostguid)
1105 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1106 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1108 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1109 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1110 keytab_name=paths.dns_keytab)
1111 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1112 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1114 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1115 hostname=names.hostname, realm=names.realm)
1116 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1118 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1121 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1123 message("Once the above files are installed, your Samba4 server will be ready to use")
1124 message("Server Role: %s" % serverrole)
1125 message("Hostname: %s" % names.hostname)
1126 message("NetBIOS Domain: %s" % names.domain)
1127 message("DNS Domain: %s" % names.dnsdomain)
1128 message("DOMAIN SID: %s" % str(domainsid))
1129 message("Admin password: %s" % adminpass)
1131 result = ProvisionResult()
1132 result.domaindn = domaindn
1133 result.paths = paths
1135 result.samdb = samdb
1139 def provision_become_dc(setup_dir=None,
1140 smbconf=None, targetdir=None, realm=None,
1141 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1143 domain=None, hostname=None, domainsid=None,
1144 adminpass=None, krbtgtpass=None, domainguid=None,
1145 policyguid=None, invocationid=None, machinepass=None,
1146 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1147 wheel=None, backup=None, aci=None, serverrole=None,
1148 ldap_backend=None, ldap_backend_type=None, sitename=None):
1151 """print a message if quiet is not set."""
1154 return provision(setup_dir, message, system_session(), None,
1155 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1156 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1157 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1160 def setup_db_config(setup_path, dbdir):
1161 """Setup a Berkeley database.
1163 :param setup_path: Setup path function.
1164 :param dbdir: Database directory."""
1165 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1166 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1167 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1168 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1170 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1171 {"LDAPDBDIR": dbdir})
1175 def provision_backend(setup_dir=None, message=None,
1176 smbconf=None, targetdir=None, realm=None,
1177 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1178 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1179 ldap_backend_type=None, ldap_backend_port=None,
1182 def setup_path(file):
1183 return os.path.join(setup_dir, file)
1185 if hostname is None:
1186 hostname = socket.gethostname().split(".")[0].lower()
1189 root = findnss(pwd.getpwnam, ["root"])[0]
1191 if adminpass is None:
1192 adminpass = glue.generate_random_str(12)
1194 if targetdir is not None:
1195 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1196 os.makedirs(os.path.join(targetdir, "etc"))
1197 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1198 elif smbconf is None:
1199 smbconf = param.default_path()
1200 assert smbconf is not None
1202 # only install a new smb.conf if there isn't one there already
1203 if not os.path.exists(smbconf):
1204 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1207 lp = param.LoadParm()
1210 if serverrole is None:
1211 serverrole = lp.get("server role")
1213 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1214 dnsdomain=realm, serverrole=serverrole,
1215 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1218 paths = provision_paths_from_lp(lp, names.dnsdomain)
1220 if not os.path.isdir(paths.ldapdir):
1221 os.makedirs(paths.ldapdir, 0700)
1222 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1224 os.unlink(schemadb_path)
1228 schemadb = Ldb(schemadb_path, lp=lp)
1230 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1232 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1233 {"SCHEMADN": names.schemadn,
1236 setup_modify_ldif(schemadb,
1237 setup_path("provision_schema_basedn_modify.ldif"), \
1238 {"SCHEMADN": names.schemadn,
1239 "NETBIOSNAME": names.netbiosname,
1240 "DEFAULTSITE": DEFAULTSITE,
1241 "CONFIGDN": names.configdn,
1242 "SERVERDN": names.serverdn,
1243 "PREFIXMAP_B64": b64encode(prefixmap)
1246 setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
1247 {"SCHEMADN": names.schemadn })
1248 setup_add_ldif(schemadb, setup_path("schema.ldif"),
1249 {"SCHEMADN": names.schemadn})
1251 if ldap_backend_type == "fedora-ds":
1252 if ldap_backend_port is not None:
1253 serverport = "ServerPort=%d" % ldap_backend_port
1257 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1259 "HOSTNAME": hostname,
1260 "DNSDOMAIN": names.dnsdomain,
1261 "LDAPDIR": paths.ldapdir,
1262 "DOMAINDN": names.domaindn,
1263 "LDAPMANAGERDN": names.ldapmanagerdn,
1264 "LDAPMANAGERPASS": adminpass,
1265 "SERVERPORT": serverport})
1267 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1268 {"CONFIGDN": names.configdn,
1269 "SCHEMADN": names.schemadn,
1272 mapping = "schema-map-fedora-ds-1.0"
1273 backend_schema = "99_ad.ldif"
1275 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1277 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1279 elif ldap_backend_type == "openldap":
1280 attrs = ["linkID", "lDAPDisplayName"]
1281 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)
1283 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1284 refint_attributes = ""
1285 for i in range (0, len(res)):
1286 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1287 target = schemadb.searchone(basedn=names.schemadn,
1288 expression=expression,
1289 attribute="lDAPDisplayName",
1290 scope=SCOPE_SUBTREE)
1291 if target is not None:
1292 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1294 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1295 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1296 "MEMBEROF_ATTR" : str(target) })
1298 refint_config = read_and_sub_file(setup_path("refint.conf"),
1299 { "LINK_ATTRS" : refint_attributes})
1301 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1303 mmr_replicator_acl = ""
1304 mmr_serverids_config = ""
1305 mmr_syncrepl_schema_config = ""
1306 mmr_syncrepl_config_config = ""
1307 mmr_syncrepl_user_config = ""
1309 if ol_mmr_urls is not None:
1310 # For now, make these equal
1311 mmr_pass = adminpass
1313 url_list=filter(None,ol_mmr_urls.split(' '))
1314 if (len(url_list) == 1):
1315 url_list=filter(None,ol_mmr_urls.split(','))
1318 mmr_on_config = "MirrorMode On"
1319 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1321 for url in url_list:
1323 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1324 { "SERVERID" : str(serverid),
1325 "LDAPSERVER" : url })
1328 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1330 "MMRDN": names.schemadn,
1332 "MMR_PASSWORD": mmr_pass})
1335 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1337 "MMRDN": names.configdn,
1339 "MMR_PASSWORD": mmr_pass})
1342 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1344 "MMRDN": names.domaindn,
1346 "MMR_PASSWORD": mmr_pass })
1349 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1350 {"DNSDOMAIN": names.dnsdomain,
1351 "LDAPDIR": paths.ldapdir,
1352 "DOMAINDN": names.domaindn,
1353 "CONFIGDN": names.configdn,
1354 "SCHEMADN": names.schemadn,
1355 "MEMBEROF_CONFIG": memberof_config,
1356 "MIRRORMODE": mmr_on_config,
1357 "REPLICATOR_ACL": mmr_replicator_acl,
1358 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1359 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1360 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1361 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1362 "REFINT_CONFIG": refint_config})
1363 setup_file(setup_path("modules.conf"), paths.modulesconf,
1364 {"REALM": names.realm})
1366 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1367 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1368 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1370 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1371 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1373 setup_file(setup_path("cn=samba.ldif"),
1374 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1375 { "UUID": str(uuid.uuid4()),
1376 "LDAPTIME": timestring(int(time.time()))} )
1377 setup_file(setup_path("cn=samba-admin.ldif"),
1378 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1379 {"LDAPADMINPASS_B64": b64encode(adminpass),
1380 "UUID": str(uuid.uuid4()),
1381 "LDAPTIME": timestring(int(time.time()))} )
1383 if ol_mmr_urls is not None:
1384 setup_file(setup_path("cn=replicator.ldif"),
1385 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1386 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1387 "UUID": str(uuid.uuid4()),
1388 "LDAPTIME": timestring(int(time.time()))} )
1392 mapping = "schema-map-openldap-2.3"
1393 backend_schema = "backend-schema.schema"
1395 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1396 if ldap_backend_port is not None:
1397 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1399 server_port_string = ""
1401 slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1403 ldapuser = "--username=samba-admin"
1406 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)
1408 os.system(schema_command)
1410 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1411 message("Server Role: %s" % serverrole)
1412 message("Hostname: %s" % names.hostname)
1413 message("DNS Domain: %s" % names.dnsdomain)
1414 message("Base DN: %s" % names.domaindn)
1416 if ldap_backend_type == "openldap":
1417 message("LDAP admin user: samba-admin")
1419 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1421 message("LDAP admin password: %s" % adminpass)
1422 message(slapdcommand)
1423 assert isinstance(ldap_backend_type, str)
1424 assert isinstance(ldapuser, str)
1425 assert isinstance(adminpass, str)
1426 assert isinstance(names.dnsdomain, str)
1427 assert isinstance(names.domain, str)
1428 assert isinstance(serverrole, str)
1429 args = ["--ldap-backend=ldapi",
1430 "--ldap-backend-type=" + ldap_backend_type,
1431 "--password=" + adminpass,
1433 "--realm=" + names.dnsdomain,
1434 "--domain=" + names.domain,
1435 "--server-role='" + serverrole + "'"]
1436 message("Run provision with: " + " ".join(args))
1439 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1440 """Create a PHP LDAP admin configuration file.
1442 :param path: Path to write the configuration to.
1443 :param setup_path: Function to generate setup paths.
1445 setup_file(setup_path("phpldapadmin-config.php"), path,
1446 {"S4_LDAPI_URI": ldapi_uri})
1449 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1450 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1451 """Write out a DNS zone file, from the info in the current database.
1453 :param path: Path of the new zone file.
1454 :param setup_path: Setup path function.
1455 :param dnsdomain: DNS Domain name
1456 :param domaindn: DN of the Domain
1457 :param hostip: Local IPv4 IP
1458 :param hostip6: Local IPv6 IP
1459 :param hostname: Local hostname
1460 :param dnspass: Password for DNS
1461 :param realm: Realm name
1462 :param domainguid: GUID of the domain.
1463 :param hostguid: GUID of the host.
1465 assert isinstance(domainguid, str)
1467 if hostip6 is not None:
1468 hostip6_base_line = " IN AAAA " + hostip6
1469 hostip6_host_line = hostname + " IN AAAA " + hostip6
1471 hostip6_base_line = ""
1472 hostip6_host_line = ""
1474 if hostip is not None:
1475 hostip_base_line = " IN A " + hostip
1476 hostip_host_line = hostname + " IN A " + hostip
1478 hostip_base_line = ""
1479 hostip_host_line = ""
1481 setup_file(setup_path("provision.zone"), path, {
1482 "DNSPASS_B64": b64encode(dnspass),
1483 "HOSTNAME": hostname,
1484 "DNSDOMAIN": dnsdomain,
1486 "HOSTIP_BASE_LINE": hostip_base_line,
1487 "HOSTIP_HOST_LINE": hostip_host_line,
1488 "DOMAINGUID": domainguid,
1489 "DATESTRING": time.strftime("%Y%m%d%H"),
1490 "DEFAULTSITE": DEFAULTSITE,
1491 "HOSTGUID": hostguid,
1492 "HOSTIP6_BASE_LINE": hostip6_base_line,
1493 "HOSTIP6_HOST_LINE": hostip6_host_line,
1497 def create_named_conf(path, setup_path, realm, dnsdomain,
1499 """Write out a file containing zone statements suitable for inclusion in a
1500 named.conf file (including GSS-TSIG configuration).
1502 :param path: Path of the new named.conf file.
1503 :param setup_path: Setup path function.
1504 :param realm: Realm name
1505 :param dnsdomain: DNS Domain name
1506 :param private_dir: Path to private directory
1507 :param keytab_name: File name of DNS keytab file
1510 setup_file(setup_path("named.conf"), path, {
1511 "DNSDOMAIN": dnsdomain,
1513 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1514 "PRIVATE_DIR": private_dir
1517 def create_named_txt(path, setup_path, realm, dnsdomain,
1518 private_dir, keytab_name):
1519 """Write out a file containing zone statements suitable for inclusion in a
1520 named.conf file (including GSS-TSIG configuration).
1522 :param path: Path of the new named.conf file.
1523 :param setup_path: Setup path function.
1524 :param realm: Realm name
1525 :param dnsdomain: DNS Domain name
1526 :param private_dir: Path to private directory
1527 :param keytab_name: File name of DNS keytab file
1530 setup_file(setup_path("named.txt"), path, {
1531 "DNSDOMAIN": dnsdomain,
1533 "DNS_KEYTAB": keytab_name,
1534 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1535 "PRIVATE_DIR": private_dir
1538 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1539 """Write out a file containing zone statements suitable for inclusion in a
1540 named.conf file (including GSS-TSIG configuration).
1542 :param path: Path of the new named.conf file.
1543 :param setup_path: Setup path function.
1544 :param dnsdomain: DNS Domain name
1545 :param hostname: Local hostname
1546 :param realm: Realm name
1549 setup_file(setup_path("krb5.conf"), path, {
1550 "DNSDOMAIN": dnsdomain,
1551 "HOSTNAME": hostname,
1556 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1557 serverdn, servername):
1558 """Load schema for the SamDB.
1560 :param samdb: Load a schema into a SamDB.
1561 :param setup_path: Setup path function.
1562 :param schemadn: DN of the schema
1563 :param netbiosname: NetBIOS name of the host.
1564 :param configdn: DN of the configuration
1565 :param serverdn: DN of the server
1566 :param servername: Host name of the server
1568 schema_data = open(setup_path("schema.ldif"), 'r').read()
1569 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1570 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1571 check_all_substituted(schema_data)
1572 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1573 prefixmap = b64encode(prefixmap)
1575 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1576 head_data = substitute_var(head_data, {
1577 "SCHEMADN": schemadn,
1578 "NETBIOSNAME": netbiosname,
1579 "CONFIGDN": configdn,
1580 "DEFAULTSITE": sitename,
1581 "PREFIXMAP_B64": prefixmap,
1582 "SERVERDN": serverdn,
1583 "SERVERNAME": servername,
1585 check_all_substituted(head_data)
1586 samdb.attach_schema_from_ldif(head_data, schema_data)