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-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted, read_and_sub_file
47 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_display_specifiers import read_ms_ldif
55 from schema import Schema
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
74 raise Exception("Unable to find setup directory.")
76 def get_config_descriptor(domain_sid):
77 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
78 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
81 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
84 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
85 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
86 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
87 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
88 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
89 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
90 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
91 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
92 sec = security.descriptor.from_sddl(sddl, domain_sid)
93 return b64encode(ndr_pack(sec))
96 DEFAULTSITE = "Default-First-Site-Name"
100 class ProvisioningError(Exception):
101 """A generic provision error."""
103 class InvalidNetbiosName(Exception):
104 """A specified name was not a valid NetBIOS name."""
105 def __init__(self, name):
106 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
109 class ProvisionPaths(object):
111 self.shareconf = None
122 self.dns_keytab = None
125 self.private_dir = None
127 self.slapdconf = None
128 self.modulesconf = None
129 self.memberofconf = None
130 self.fedoradsinf = None
131 self.fedoradspartitions = None
132 self.fedoradssasl = None
133 self.fedoradsdna = None
134 self.fedoradspam = None
135 self.fedoradsrefint = None
136 self.fedoradslinkedattributes = None
137 self.fedoradsindex = None
138 self.fedoradssamba = None
140 self.olmmrserveridsconf = None
141 self.olmmrsyncreplconf = None
144 self.olcseedldif = None
147 class ProvisionNames(object):
154 self.ldapmanagerdn = None
155 self.dnsdomain = None
157 self.netbiosname = None
164 class ProvisionResult(object):
171 def check_install(lp, session_info, credentials):
172 """Check whether the current install seems ok.
174 :param lp: Loadparm context
175 :param session_info: Session information
176 :param credentials: Credentials
178 if lp.get("realm") == "":
179 raise Exception("Realm empty")
180 ldb = Ldb(lp.get("sam database"), session_info=session_info,
181 credentials=credentials, lp=lp)
182 if len(ldb.search("(cn=Administrator)")) != 1:
183 raise ProvisioningError("No administrator account found")
186 def findnss(nssfn, names):
187 """Find a user or group from a list of possibilities.
189 :param nssfn: NSS Function to try (should raise KeyError if not found)
190 :param names: Names to check.
191 :return: Value return by first names list.
198 raise KeyError("Unable to find user/group %r" % names)
201 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
202 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
205 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
206 """Setup a ldb in the private dir.
208 :param ldb: LDB file to import data into
209 :param ldif_path: Path of the LDIF file to load
210 :param subst_vars: Optional variables to subsitute in LDIF.
211 :param nocontrols: Optional list of controls, can be None for no controls
213 assert isinstance(ldif_path, str)
214 data = read_and_sub_file(ldif_path, subst_vars)
215 ldb.add_ldif(data,controls)
218 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
219 """Modify a ldb in the private dir.
221 :param ldb: LDB object.
222 :param ldif_path: LDIF file path.
223 :param subst_vars: Optional dictionary with substitution variables.
225 data = read_and_sub_file(ldif_path, subst_vars)
227 ldb.modify_ldif(data)
230 def setup_ldb(ldb, ldif_path, subst_vars):
231 """Import a LDIF a file into a LDB handle, optionally substituting variables.
233 :note: Either all LDIF data will be added or none (using transactions).
235 :param ldb: LDB file to import into.
236 :param ldif_path: Path to the LDIF file.
237 :param subst_vars: Dictionary with substitution variables.
239 assert ldb is not None
240 ldb.transaction_start()
242 setup_add_ldif(ldb, ldif_path, subst_vars)
244 ldb.transaction_cancel()
246 ldb.transaction_commit()
249 def setup_file(template, fname, subst_vars=None):
250 """Setup a file in the private dir.
252 :param template: Path of the template file.
253 :param fname: Path of the file to create.
254 :param subst_vars: Substitution variables.
258 if os.path.exists(f):
261 data = read_and_sub_file(template, subst_vars)
262 open(f, 'w').write(data)
265 def provision_paths_from_lp(lp, dnsdomain):
266 """Set the default paths for provisioning.
268 :param lp: Loadparm context.
269 :param dnsdomain: DNS Domain name
271 paths = ProvisionPaths()
272 paths.private_dir = lp.get("private dir")
273 paths.dns_keytab = "dns.keytab"
275 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
276 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
277 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
278 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
279 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
280 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
281 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
282 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
283 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
284 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
285 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
286 paths.phpldapadminconfig = os.path.join(paths.private_dir,
287 "phpldapadmin-config.php")
288 paths.ldapdir = os.path.join(paths.private_dir,
290 paths.slapdconf = os.path.join(paths.ldapdir,
292 paths.slapdpid = os.path.join(paths.ldapdir,
294 paths.modulesconf = os.path.join(paths.ldapdir,
296 paths.memberofconf = os.path.join(paths.ldapdir,
298 paths.fedoradsinf = os.path.join(paths.ldapdir,
300 paths.fedoradspartitions = os.path.join(paths.ldapdir,
301 "fedorads-partitions.ldif")
302 paths.fedoradssasl = os.path.join(paths.ldapdir,
303 "fedorads-sasl.ldif")
304 paths.fedoradsdna = os.path.join(paths.ldapdir,
306 paths.fedoradspam = os.path.join(paths.ldapdir,
308 paths.fedoradsrefint = os.path.join(paths.ldapdir,
309 "fedorads-refint.ldif")
310 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
311 "fedorads-linked-attributes.ldif")
312 paths.fedoradsindex = os.path.join(paths.ldapdir,
313 "fedorads-index.ldif")
314 paths.fedoradssamba = os.path.join(paths.ldapdir,
315 "fedorads-samba.ldif")
316 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
317 "mmr_serverids.conf")
318 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
320 paths.olcdir = os.path.join(paths.ldapdir,
322 paths.olcseedldif = os.path.join(paths.ldapdir,
324 paths.hklm = "hklm.ldb"
325 paths.hkcr = "hkcr.ldb"
326 paths.hkcu = "hkcu.ldb"
327 paths.hku = "hku.ldb"
328 paths.hkpd = "hkpd.ldb"
329 paths.hkpt = "hkpt.ldb"
331 paths.sysvol = lp.get("path", "sysvol")
333 paths.netlogon = lp.get("path", "netlogon")
335 paths.smbconf = lp.configfile
340 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
341 serverrole=None, rootdn=None, domaindn=None, configdn=None,
342 schemadn=None, serverdn=None, sitename=None, sambadn=None):
343 """Guess configuration settings to use."""
346 hostname = socket.gethostname().split(".")[0]
348 netbiosname = lp.get("netbios name")
349 if netbiosname is None:
350 netbiosname = hostname
351 assert netbiosname is not None
352 netbiosname = netbiosname.upper()
353 if not valid_netbios_name(netbiosname):
354 raise InvalidNetbiosName(netbiosname)
356 if dnsdomain is None:
357 dnsdomain = lp.get("realm")
358 assert dnsdomain is not None
359 dnsdomain = dnsdomain.lower()
361 if serverrole is None:
362 serverrole = lp.get("server role")
363 assert serverrole is not None
364 serverrole = serverrole.lower()
366 realm = dnsdomain.upper()
368 if lp.get("realm").upper() != realm:
369 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
371 if serverrole == "domain controller":
373 domain = lp.get("workgroup")
374 assert domain is not None
375 domain = domain.upper()
377 if lp.get("workgroup").upper() != domain:
378 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
381 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
385 domaindn = "DC=" + netbiosname
387 if not valid_netbios_name(domain):
388 raise InvalidNetbiosName(domain)
390 if hostname.upper() == realm:
391 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
392 if netbiosname == realm:
393 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
395 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
401 configdn = "CN=Configuration," + rootdn
403 schemadn = "CN=Schema," + configdn
410 names = ProvisionNames()
411 names.rootdn = rootdn
412 names.domaindn = domaindn
413 names.configdn = configdn
414 names.schemadn = schemadn
415 names.sambadn = sambadn
416 names.ldapmanagerdn = "CN=Manager," + rootdn
417 names.dnsdomain = dnsdomain
418 names.domain = domain
420 names.netbiosname = netbiosname
421 names.hostname = hostname
422 names.sitename = sitename
423 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
428 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
429 targetdir, sid_generator):
430 """Create a new smb.conf file based on a couple of basic settings.
432 assert smbconf is not None
434 hostname = socket.gethostname().split(".")[0]
435 netbiosname = hostname.upper()
437 if serverrole is None:
438 serverrole = "standalone"
440 assert serverrole in ("domain controller", "member server", "standalone")
441 if serverrole == "domain controller":
443 elif serverrole == "member server":
444 smbconfsuffix = "member"
445 elif serverrole == "standalone":
446 smbconfsuffix = "standalone"
448 if sid_generator is None:
449 sid_generator = "internal"
451 assert domain is not None
452 domain = domain.upper()
454 assert realm is not None
455 realm = realm.upper()
457 default_lp = param.LoadParm()
458 #Load non-existant file
459 if os.path.exists(smbconf):
460 default_lp.load(smbconf)
462 if targetdir is not None:
463 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
464 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
466 default_lp.set("lock dir", os.path.abspath(targetdir))
471 if sid_generator == "internal":
472 sid_generator_line = ""
474 sid_generator_line = "sid generator = " + sid_generator
476 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
477 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
479 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
481 "NETBIOS_NAME": netbiosname,
484 "SERVERROLE": serverrole,
485 "NETLOGONPATH": netlogon,
486 "SYSVOLPATH": sysvol,
487 "SIDGENERATOR_LINE": sid_generator_line,
488 "PRIVATEDIR_LINE": privatedir_line,
489 "LOCKDIR_LINE": lockdir_line
493 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
494 users_gid, wheel_gid):
495 """setup reasonable name mappings for sam names to unix names.
497 :param samdb: SamDB object.
498 :param idmap: IDmap db object.
499 :param sid: The domain sid.
500 :param domaindn: The domain DN.
501 :param root_uid: uid of the UNIX root user.
502 :param nobody_uid: uid of the UNIX nobody user.
503 :param users_gid: gid of the UNIX users group.
504 :param wheel_gid: gid of the UNIX wheel group."""
506 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
507 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
509 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
510 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
512 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
513 provision_backend, names, schema,
516 """Setup the partitions for the SAM database.
518 Alternatively, provision() may call this, and then populate the database.
520 :note: This will wipe the Sam Database!
522 :note: This function always removes the local SAM LDB file. The erase
523 parameter controls whether to erase the existing data, which
524 may not be stored locally but in LDAP.
527 assert session_info is not None
529 old_partitions = None
530 new_partitions = None
532 # We use options=["modules:"] to stop the modules loading - we
533 # just want to wipe and re-initialise the database, not start it up
536 samdb = Ldb(url=samdb_path, session_info=session_info,
537 lp=lp, options=["modules:"])
538 res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*")
541 old_partitions = res[0]["partition"]
545 if old_partitions is not None:
547 for old_partition in old_partitions:
548 new_partition = old_partition
549 if old_partition.endswith(".ldb"):
550 p = old_partition.split(":")[0]
551 dn = ldb.Dn(schema.ldb, p)
552 new_partition = dn.get_casefold()
553 new_partitions.append(new_partition)
556 samdb.erase_except_schema_controlled()
558 os.unlink(samdb_path)
559 samdb = Ldb(url=samdb_path, session_info=session_info,
560 lp=lp, options=["modules:"])
562 samdb.erase_except_schema_controlled()
564 #Add modules to the list to activate them by default
565 #beware often order is important
567 # Some Known ordering constraints:
568 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
569 # - objectclass must be before password_hash, because password_hash checks
570 # that the objectclass is of type person (filled in by objectclass
571 # module when expanding the objectclass list)
572 # - partition must be last
573 # - each partition has its own module list then
574 modules_list = ["resolve_oids",
597 "extended_dn_out_ldb"]
598 modules_list2 = ["show_deleted",
603 ldap_backend_line = "# No LDAP backend"
604 if provision_backend.type is not "ldb":
605 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
607 if provision_backend.ldap_backend_type == "fedora-ds":
608 backend_modules = ["nsuniqueid", "paged_searches"]
609 # We can handle linked attributes here, as we don't have directory-side subtree operations
610 tdb_modules_list = ["extended_dn_out_fds"]
611 elif ldap_backend.ldap_backend_type == "openldap":
612 backend_modules = ["entryuuid", "paged_searches"]
613 # OpenLDAP handles subtree renames, so we don't want to do any of these things
614 tdb_modules_list = ["extended_dn_out_openldap"]
616 elif serverrole == "domain controller":
617 tdb_modules_list.insert(0, "repl_meta_data")
620 backend_modules = ["objectguid"]
622 if tdb_modules_list is None:
623 tdb_modules_list_as_string = ""
625 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
627 samdb.transaction_start()
629 message("Setting up sam.ldb partitions and settings")
630 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
631 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
632 "SCHEMADN_MOD2": ",objectguid",
633 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
634 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
635 "SCHEMADN_MOD": "schema_data",
636 "CONFIGDN_MOD": "naming_fsmo",
637 "DOMAINDN_MOD": "pdc_fsmo",
638 "MODULES_LIST": ",".join(modules_list),
639 "TDB_MODULES_LIST": tdb_modules_list_as_string,
640 "MODULES_LIST2": ",".join(modules_list2),
641 "BACKEND_MOD": ",".join(backend_modules),
642 "LDAP_BACKEND_LINE": ldap_backend_line,
646 if new_partitions is not None:
648 m.dn = ldb.Dn(samdb, "@PARTITION")
650 m["partition"] = ldb.MessageElement(new_partitions, ldb.FLAG_MOD_ADD, "partition")
653 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
655 message("Setting up sam.ldb rootDSE")
656 setup_samdb_rootdse(samdb, setup_path, names)
659 samdb.transaction_cancel()
662 samdb.transaction_commit()
665 def secretsdb_self_join(secretsdb, domain,
666 netbiosname, domainsid, machinepass,
667 realm=None, dnsdomain=None,
669 key_version_number=1,
670 secure_channel_type=SEC_CHAN_WKSTA):
671 """Add domain join-specific bits to a secrets database.
673 :param secretsdb: Ldb Handle to the secrets database
674 :param machinepass: Machine password
676 attrs=["whenChanged",
684 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
685 msg["secureChannelType"] = str(secure_channel_type)
686 msg["flatname"] = [domain]
687 msg["objectClass"] = ["top", "primaryDomain"]
688 if realm is not None:
689 if dnsdomain is None:
690 dnsdomain = realm.lower()
691 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
693 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
694 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
695 msg["privateKeytab"] = ["secrets.keytab"];
698 msg["secret"] = [machinepass]
699 msg["samAccountName"] = ["%s$" % netbiosname]
700 msg["secureChannelType"] = [str(secure_channel_type)]
701 msg["objectSid"] = [ndr_pack(domainsid)]
703 res = secretsdb.search(base="cn=Primary Domains",
705 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
706 scope=SCOPE_ONELEVEL)
709 if del_msg.dn is not msg.dn:
710 secretsdb.delete(del_msg.dn)
712 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
715 msg["priorSecret"] = res[0]["secret"]
716 msg["priorWhenChanged"] = res[0]["whenChanged"]
718 if res["privateKeytab"] is not None:
719 msg["privateKeytab"] = res[0]["privateKeytab"]
721 if res["krb5Keytab"] is not None:
722 msg["krb5Keytab"] = res[0]["krb5Keytab"]
725 el.set_flags(ldb.FLAG_MOD_REPLACE)
726 secretsdb.modify(msg)
731 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
732 dns_keytab_path, dnspass):
733 """Add DNS specific bits to a secrets database.
735 :param secretsdb: Ldb Handle to the secrets database
736 :param setup_path: Setup path function
737 :param machinepass: Machine password
739 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
741 "DNSDOMAIN": dnsdomain,
742 "DNS_KEYTAB": dns_keytab_path,
743 "DNSPASS_B64": b64encode(dnspass),
747 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
748 """Setup the secrets database.
750 :param path: Path to the secrets database.
751 :param setup_path: Get the path to a setup file.
752 :param session_info: Session info.
753 :param credentials: Credentials
754 :param lp: Loadparm context
755 :return: LDB handle for the created secrets database
757 if os.path.exists(path):
759 secrets_ldb = Ldb(path, session_info=session_info,
762 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
763 secrets_ldb = Ldb(path, session_info=session_info,
765 secrets_ldb.transaction_start()
766 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
768 if backend_credentials is not None and backend_credentials.authentication_requested():
769 if backend_credentials.get_bind_dn() is not None:
770 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
771 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
772 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
775 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
776 "LDAPADMINUSER": backend_credentials.get_username(),
777 "LDAPADMINREALM": backend_credentials.get_realm(),
778 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
783 def setup_privileges(path, setup_path, session_info, lp):
784 """Setup the privileges database.
786 :param path: Path to the privileges database.
787 :param setup_path: Get the path to a setup file.
788 :param session_info: Session info.
789 :param credentials: Credentials
790 :param lp: Loadparm context
791 :return: LDB handle for the created secrets database
793 if os.path.exists(path):
795 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
796 privilege_ldb.erase()
797 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
800 def setup_registry(path, setup_path, session_info, lp):
801 """Setup the registry.
803 :param path: Path to the registry database
804 :param setup_path: Function that returns the path to a setup.
805 :param session_info: Session information
806 :param credentials: Credentials
807 :param lp: Loadparm context
809 reg = registry.Registry()
810 hive = registry.open_ldb(path, session_info=session_info,
812 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
813 provision_reg = setup_path("provision.reg")
814 assert os.path.exists(provision_reg)
815 reg.diff_apply(provision_reg)
818 def setup_idmapdb(path, setup_path, session_info, lp):
819 """Setup the idmap database.
821 :param path: path to the idmap database
822 :param setup_path: Function that returns a path to a setup file
823 :param session_info: Session information
824 :param credentials: Credentials
825 :param lp: Loadparm context
827 if os.path.exists(path):
830 idmap_ldb = IDmapDB(path, session_info=session_info,
834 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
838 def setup_samdb_rootdse(samdb, setup_path, names):
839 """Setup the SamDB rootdse.
841 :param samdb: Sam Database handle
842 :param setup_path: Obtain setup path
844 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
845 "SCHEMADN": names.schemadn,
846 "NETBIOSNAME": names.netbiosname,
847 "DNSDOMAIN": names.dnsdomain,
848 "REALM": names.realm,
849 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
850 "DOMAINDN": names.domaindn,
851 "ROOTDN": names.rootdn,
852 "CONFIGDN": names.configdn,
853 "SERVERDN": names.serverdn,
857 def setup_self_join(samdb, names,
858 machinepass, dnspass,
859 domainsid, invocationid, setup_path,
860 policyguid, policyguid_dc, domainControllerFunctionality,
862 """Join a host to its own domain."""
863 assert isinstance(invocationid, str)
864 if ntdsguid is not None:
865 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
868 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
869 "CONFIGDN": names.configdn,
870 "SCHEMADN": names.schemadn,
871 "DOMAINDN": names.domaindn,
872 "SERVERDN": names.serverdn,
873 "INVOCATIONID": invocationid,
874 "NETBIOSNAME": names.netbiosname,
875 "DEFAULTSITE": names.sitename,
876 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
877 "MACHINEPASS_B64": b64encode(machinepass),
878 "DNSPASS_B64": b64encode(dnspass),
879 "REALM": names.realm,
880 "DOMAIN": names.domain,
881 "DNSDOMAIN": names.dnsdomain,
882 "SAMBA_VERSION_STRING": version,
883 "NTDSGUID": ntdsguid_line,
884 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
886 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
887 "POLICYGUID": policyguid,
888 "POLICYGUID_DC": policyguid_dc,
889 "DNSDOMAIN": names.dnsdomain,
890 "DOMAINSID": str(domainsid),
891 "DOMAINDN": names.domaindn})
893 # add the NTDSGUID based SPNs
894 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
895 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
896 expression="", scope=SCOPE_BASE)
897 assert isinstance(names.ntdsguid, str)
899 # Setup fSMORoleOwner entries to point at the newly created DC entry
900 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
901 "DOMAIN": names.domain,
902 "DNSDOMAIN": names.dnsdomain,
903 "DOMAINDN": names.domaindn,
904 "CONFIGDN": names.configdn,
905 "SCHEMADN": names.schemadn,
906 "DEFAULTSITE": names.sitename,
907 "SERVERDN": names.serverdn,
908 "NETBIOSNAME": names.netbiosname,
909 "NTDSGUID": names.ntdsguid
913 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
915 domainsid, domainguid, policyguid, policyguid_dc,
916 fill, adminpass, krbtgtpass,
917 machinepass, invocationid, dnspass, ntdsguid,
918 serverrole, dom_for_fun_level=None,
920 """Setup a complete SAM Database.
922 :note: This will wipe the main SAM database file!
925 # ATTENTION: Do NOT change these default values without discussion with the
926 # team and/or release manager. They have a big impact on the whole program!
927 domainControllerFunctionality = DS_DC_FUNCTION_2008
929 if dom_for_fun_level is None:
930 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
931 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
932 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
934 if dom_for_fun_level > domainControllerFunctionality:
935 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
937 domainFunctionality = dom_for_fun_level
938 forestFunctionality = dom_for_fun_level
940 # Also wipes the database
941 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
942 provision_backend=provision_backend, session_info=session_info,
944 serverrole=serverrole, schema=schema)
947 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
948 sambadn=names.sambadn)
950 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
951 samdb = Ldb(session_info=session_info,
952 credentials=provision_backend.credentials, lp=lp)
954 message("Pre-loading the Samba 4 and AD schema")
956 # Load the schema from the one we computed earlier
957 samdb.set_schema_from_ldb(schema.ldb)
959 # And now we can connect to the DB - the schema won't be loaded from the DB
965 samdb.transaction_start()
967 message("Erasing data from partitions")
968 # Load the schema (again). This time it will force a reindex,
969 # and will therefore make the erase_partitions() below
970 # computationally sane
971 samdb.set_schema_from_ldb(schema.ldb)
972 samdb.erase_partitions()
974 # Set the domain functionality levels onto the database.
975 # Various module (the password_hash module in particular) need
976 # to know what level of AD we are emulating.
978 # These will be fixed into the database via the database
979 # modifictions below, but we need them set from the start.
980 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
981 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
982 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
984 samdb.set_domain_sid(str(domainsid))
985 if serverrole == "domain controller":
986 samdb.set_invocation_id(invocationid)
988 message("Adding DomainDN: %s" % names.domaindn)
990 #impersonate domain admin
991 admin_session_info = admin_session(lp, str(domainsid))
992 samdb.set_session_info(admin_session_info)
993 if domainguid is not None:
994 domainguid_line = "objectGUID: %s\n-" % domainguid
997 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
998 "DOMAINDN": names.domaindn,
999 "DOMAINGUID": domainguid_line
1003 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1004 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1005 "DOMAINSID": str(domainsid),
1006 "SCHEMADN": names.schemadn,
1007 "NETBIOSNAME": names.netbiosname,
1008 "DEFAULTSITE": names.sitename,
1009 "CONFIGDN": names.configdn,
1010 "SERVERDN": names.serverdn,
1011 "POLICYGUID": policyguid,
1012 "DOMAINDN": names.domaindn,
1013 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1014 "SAMBA_VERSION_STRING": version
1017 message("Adding configuration container")
1018 descr = get_config_descriptor(domainsid);
1019 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1020 "CONFIGDN": names.configdn,
1021 "DESCRIPTOR": descr,
1023 message("Modifying configuration container")
1024 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1025 "CONFIGDN": names.configdn,
1026 "SCHEMADN": names.schemadn,
1029 # The LDIF here was created when the Schema object was constructed
1030 message("Setting up sam.ldb schema")
1031 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1032 samdb.modify_ldif(schema.schema_dn_modify)
1033 samdb.write_prefixes_from_schema()
1034 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1035 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1036 {"SCHEMADN": names.schemadn})
1038 message("Setting up sam.ldb configuration data")
1039 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1040 "CONFIGDN": names.configdn,
1041 "NETBIOSNAME": names.netbiosname,
1042 "DEFAULTSITE": names.sitename,
1043 "DNSDOMAIN": names.dnsdomain,
1044 "DOMAIN": names.domain,
1045 "SCHEMADN": names.schemadn,
1046 "DOMAINDN": names.domaindn,
1047 "SERVERDN": names.serverdn,
1048 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1051 message("Setting up display specifiers")
1052 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1053 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1054 check_all_substituted(display_specifiers_ldif)
1055 samdb.add_ldif(display_specifiers_ldif)
1057 message("Adding users container")
1058 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1059 "DOMAINDN": names.domaindn})
1060 message("Modifying users container")
1061 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1062 "DOMAINDN": names.domaindn})
1063 message("Adding computers container")
1064 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1065 "DOMAINDN": names.domaindn})
1066 message("Modifying computers container")
1067 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1068 "DOMAINDN": names.domaindn})
1069 message("Setting up sam.ldb data")
1070 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1071 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1072 "DOMAINDN": names.domaindn,
1073 "NETBIOSNAME": names.netbiosname,
1074 "DEFAULTSITE": names.sitename,
1075 "CONFIGDN": names.configdn,
1076 "SERVERDN": names.serverdn,
1077 "POLICYGUID_DC": policyguid_dc
1080 if fill == FILL_FULL:
1081 message("Setting up sam.ldb users and groups")
1082 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1083 "DOMAINDN": names.domaindn,
1084 "DOMAINSID": str(domainsid),
1085 "CONFIGDN": names.configdn,
1086 "ADMINPASS_B64": b64encode(adminpass),
1087 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1090 if serverrole == "domain controller":
1091 message("Setting up self join")
1092 setup_self_join(samdb, names=names, invocationid=invocationid,
1094 machinepass=machinepass,
1095 domainsid=domainsid, policyguid=policyguid,
1096 policyguid_dc=policyguid_dc,
1097 setup_path=setup_path,
1098 domainControllerFunctionality=domainControllerFunctionality,
1101 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1102 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1103 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1104 assert isinstance(names.ntdsguid, str)
1107 samdb.transaction_cancel()
1110 samdb.transaction_commit()
1115 FILL_NT4SYNC = "NT4SYNC"
1119 def provision(setup_dir, message, session_info,
1120 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1122 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1124 domain=None, hostname=None, hostip=None, hostip6=None,
1125 domainsid=None, adminpass=None, ldapadminpass=None,
1126 krbtgtpass=None, domainguid=None,
1127 policyguid=None, policyguid_dc=None, invocationid=None,
1128 machinepass=None, ntdsguid=None,
1129 dnspass=None, root=None, nobody=None, users=None,
1130 wheel=None, backup=None, aci=None, serverrole=None,
1131 dom_for_fun_level=None,
1132 ldap_backend_extra_port=None, backend_type=None,
1134 ol_mmr_urls=None, ol_olc=None,
1135 setup_ds_path=None, slapd_path=None, nosync=False,
1136 ldap_dryrun_mode=False):
1139 :note: caution, this wipes all existing data!
1142 def setup_path(file):
1143 return os.path.join(setup_dir, file)
1145 if domainsid is None:
1146 domainsid = security.random_sid()
1148 domainsid = security.dom_sid(domainsid)
1150 # create/adapt the group policy GUIDs
1151 if policyguid is None:
1152 policyguid = str(uuid.uuid4())
1153 policyguid = policyguid.upper()
1154 if policyguid_dc is None:
1155 policyguid_dc = str(uuid.uuid4())
1156 policyguid_dc = policyguid_dc.upper()
1158 if adminpass is None:
1159 adminpass = glue.generate_random_str(12)
1160 if krbtgtpass is None:
1161 krbtgtpass = glue.generate_random_str(12)
1162 if machinepass is None:
1163 machinepass = glue.generate_random_str(12)
1165 dnspass = glue.generate_random_str(12)
1166 if ldapadminpass is None:
1167 #Make a new, random password between Samba and it's LDAP server
1168 ldapadminpass=glue.generate_random_str(12)
1170 if backend_type is None:
1171 backend_type = "ldb"
1173 sid_generator = "internal"
1174 if backend_type == "fedora-ds":
1175 sid_generator = "backend"
1177 root_uid = findnss_uid([root or "root"])
1178 nobody_uid = findnss_uid([nobody or "nobody"])
1179 users_gid = findnss_gid([users or "users"])
1181 wheel_gid = findnss_gid(["wheel", "adm"])
1183 wheel_gid = findnss_gid([wheel])
1185 if targetdir is not None:
1186 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1187 os.makedirs(os.path.join(targetdir, "etc"))
1188 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1189 elif smbconf is None:
1190 smbconf = param.default_path()
1192 # only install a new smb.conf if there isn't one there already
1193 if not os.path.exists(smbconf):
1194 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1195 targetdir, sid_generator)
1197 lp = param.LoadParm()
1200 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1201 dnsdomain=realm, serverrole=serverrole,
1202 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1203 serverdn=serverdn, sitename=sitename)
1205 paths = provision_paths_from_lp(lp, names.dnsdomain)
1209 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1210 except socket.gaierror, (socket.EAI_NODATA, msg):
1215 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1216 except socket.gaierror, (socket.EAI_NODATA, msg):
1219 if serverrole is None:
1220 serverrole = lp.get("server role")
1222 assert serverrole in ("domain controller", "member server", "standalone")
1223 if invocationid is None and serverrole == "domain controller":
1224 invocationid = str(uuid.uuid4())
1226 if not os.path.exists(paths.private_dir):
1227 os.mkdir(paths.private_dir)
1229 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1231 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1232 sambadn=names.sambadn)
1234 provision_backend = ProvisionBackend(backend_type,
1235 paths=paths, setup_path=setup_path,
1236 lp=lp, credentials=credentials,
1238 message=message, hostname=hostname,
1239 root=root, schema=schema,
1240 ldapadminpass=ldapadminpass,
1241 ldap_backend_extra_port=ldap_backend_extra_port,
1242 ol_mmr_urls=ol_mmr_urls,
1243 slapd_path=slapd_path,
1244 setup_ds_path=setup_ds_path,
1245 ldap_dryrun_mode=ldap_dryrun_mode,
1246 domainsid=domainsid)
1248 # only install a new shares config db if there is none
1249 if not os.path.exists(paths.shareconf):
1250 message("Setting up share.ldb")
1251 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1253 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1256 message("Setting up secrets.ldb")
1257 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1258 session_info=session_info,
1259 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1261 message("Setting up the registry")
1262 setup_registry(paths.hklm, setup_path, session_info,
1265 message("Setting up the privileges database")
1266 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1268 message("Setting up idmap db")
1269 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1272 message("Setting up SAM db")
1273 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1274 provision_backend, lp, names,
1276 domainsid=domainsid,
1277 schema=schema, domainguid=domainguid,
1278 policyguid=policyguid, policyguid_dc=policyguid_dc,
1280 adminpass=adminpass, krbtgtpass=krbtgtpass,
1281 invocationid=invocationid,
1282 machinepass=machinepass, dnspass=dnspass,
1283 ntdsguid=ntdsguid, serverrole=serverrole,
1284 dom_for_fun_level=dom_for_fun_level)
1286 if serverrole == "domain controller":
1287 if paths.netlogon is None:
1288 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1289 message("Please either remove %s or see the template at %s" %
1290 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1291 assert(paths.netlogon is not None)
1293 if paths.sysvol is None:
1294 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1295 message("Please either remove %s or see the template at %s" %
1296 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1297 assert(paths.sysvol is not None)
1299 # Set up group policies (domain policy and domain controller policy)
1301 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1302 "{" + policyguid + "}")
1303 os.makedirs(policy_path, 0755)
1304 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1305 "[General]\r\nVersion=65543")
1306 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1307 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1309 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1310 "{" + policyguid_dc + "}")
1311 os.makedirs(policy_path_dc, 0755)
1312 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1313 "[General]\r\nVersion=2")
1314 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1315 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1317 if not os.path.isdir(paths.netlogon):
1318 os.makedirs(paths.netlogon, 0755)
1320 if samdb_fill == FILL_FULL:
1321 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1322 root_uid=root_uid, nobody_uid=nobody_uid,
1323 users_gid=users_gid, wheel_gid=wheel_gid)
1325 message("Setting up sam.ldb rootDSE marking as synchronized")
1326 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1328 # Only make a zone file on the first DC, it should be replicated with DNS replication
1329 if serverrole == "domain controller":
1330 secretsdb_self_join(secrets_ldb, domain=domain,
1332 dnsdomain=names.dnsdomain,
1333 netbiosname=names.netbiosname,
1334 domainsid=domainsid,
1335 machinepass=machinepass,
1336 secure_channel_type=SEC_CHAN_BDC)
1338 secretsdb_setup_dns(secrets_ldb, setup_path,
1339 realm=names.realm, dnsdomain=names.dnsdomain,
1340 dns_keytab_path=paths.dns_keytab,
1343 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1344 assert isinstance(domainguid, str)
1346 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1348 hostip6=hostip6, hostname=names.hostname,
1350 domainguid=domainguid, ntdsguid=names.ntdsguid)
1352 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1353 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1355 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1356 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1357 keytab_name=paths.dns_keytab)
1358 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1359 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1361 create_krb5_conf(paths.krb5conf, setup_path,
1362 dnsdomain=names.dnsdomain, hostname=names.hostname,
1364 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1366 if provision_backend.post_setup is not None:
1367 provision_backend.post_setup()
1369 if provision_backend.shutdown is not None:
1370 provision_backend.shutdown()
1372 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1375 #Now commit the secrets.ldb to disk
1376 secrets_ldb.transaction_commit()
1378 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1380 message("Once the above files are installed, your Samba4 server will be ready to use")
1381 message("Server Role: %s" % serverrole)
1382 message("Hostname: %s" % names.hostname)
1383 message("NetBIOS Domain: %s" % names.domain)
1384 message("DNS Domain: %s" % names.dnsdomain)
1385 message("DOMAIN SID: %s" % str(domainsid))
1386 if samdb_fill == FILL_FULL:
1387 message("Admin password: %s" % adminpass)
1388 if provision_backend.type is not "ldb":
1389 if provision_backend.credentials.get_bind_dn() is not None:
1390 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1392 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1394 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1396 if provision_backend.slapd_command_escaped is not None:
1397 # now display slapd_command_file.txt to show how slapd must be started next time
1398 message("Use later the following commandline to start slapd, then Samba:")
1399 message(provision_backend.slapd_command_escaped)
1400 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1403 result = ProvisionResult()
1404 result.domaindn = domaindn
1405 result.paths = paths
1407 result.samdb = samdb
1412 def provision_become_dc(setup_dir=None,
1413 smbconf=None, targetdir=None, realm=None,
1414 rootdn=None, domaindn=None, schemadn=None,
1415 configdn=None, serverdn=None,
1416 domain=None, hostname=None, domainsid=None,
1417 adminpass=None, krbtgtpass=None, domainguid=None,
1418 policyguid=None, policyguid_dc=None, invocationid=None,
1420 dnspass=None, root=None, nobody=None, users=None,
1421 wheel=None, backup=None, serverrole=None,
1422 ldap_backend=None, ldap_backend_type=None,
1423 sitename=None, debuglevel=1):
1426 """print a message if quiet is not set."""
1429 glue.set_debug_level(debuglevel)
1431 return provision(setup_dir, message, system_session(), None,
1432 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1433 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1434 configdn=configdn, serverdn=serverdn, domain=domain,
1435 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1436 machinepass=machinepass, serverrole="domain controller",
1440 def setup_db_config(setup_path, dbdir):
1441 """Setup a Berkeley database.
1443 :param setup_path: Setup path function.
1444 :param dbdir: Database directory."""
1445 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1446 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1447 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1448 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1450 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1451 {"LDAPDBDIR": dbdir})
1453 def ldap_backend_shutdown(self):
1454 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1455 if self.slapd.poll() is None:
1457 if hasattr(self.slapd, "terminate"):
1458 self.slapd.terminate()
1460 # Older python versions don't have .terminate()
1462 os.kill(self.slapd.pid, signal.SIGTERM)
1464 #and now wait for it to die
1465 self.slapd.communicate()
1468 class ProvisionBackend(object):
1469 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
1470 names=None, message=None,
1471 hostname=None, root=None,
1472 schema=None, ldapadminpass=None,
1473 ldap_backend_extra_port=None,
1475 setup_ds_path=None, slapd_path=None,
1476 nosync=False, ldap_dryrun_mode=False,
1478 """Provision an LDAP backend for samba4
1480 This works for OpenLDAP and Fedora DS
1483 self.slapd_command = None
1484 self.slapd_command_escaped = None
1486 self.type = backend_type
1488 # Set a default - the code for "existing" below replaces this
1489 self.ldap_backend_type = backend_type
1491 self.post_setup = None
1492 self.shutdown = None
1494 if self.type is "ldb":
1495 self.credentials = None
1496 self.secrets_credentials = None
1499 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1501 if self.type == "existing":
1502 #Check to see that this 'existing' LDAP backend in fact exists
1503 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1504 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1505 expression="(objectClass=OpenLDAProotDSE)")
1507 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1508 self.credentials = credentials
1509 # This caused them to be set into the long-term database later in the script.
1510 self.secrets_credentials = credentials
1512 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1515 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1516 # if another instance of slapd is already running
1518 ldapi_db = Ldb(self.ldapi_uri)
1519 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1520 expression="(objectClass=OpenLDAProotDSE)");
1522 f = open(paths.slapdpid, "r")
1525 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1529 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1534 # Try to print helpful messages when the user has not specified the path to slapd
1535 if slapd_path is None:
1536 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1537 if not os.path.exists(slapd_path):
1538 message (slapd_path)
1539 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1542 if not os.path.isdir(paths.ldapdir):
1543 os.makedirs(paths.ldapdir, 0700)
1545 # Put the LDIF of the schema into a database so we can search on
1546 # it to generate schema-dependent configurations in Fedora DS and
1548 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1550 os.unlink(schemadb_path)
1554 schema.write_to_tmp_ldb(schemadb_path);
1556 self.credentials = Credentials()
1557 self.credentials.guess(lp)
1558 #Kerberos to an ldapi:// backend makes no sense
1559 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1561 self.secrets_credentials = Credentials()
1562 self.secrets_credentials.guess(lp)
1563 #Kerberos to an ldapi:// backend makes no sense
1564 self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS)
1566 self.shutdown = ldap_backend_shutdown
1568 if self.type == "fedora-ds":
1569 provision_fds_backend(self, setup_path=setup_path,
1570 names=names, message=message,
1572 ldapadminpass=ldapadminpass, root=root,
1574 ldap_backend_extra_port=ldap_backend_extra_port,
1575 setup_ds_path=setup_ds_path,
1576 slapd_path=slapd_path,
1578 ldap_dryrun_mode=ldap_dryrun_mode,
1579 domainsid=domainsid)
1581 elif self.type == "openldap":
1582 provision_openldap_backend(self, setup_path=setup_path,
1583 names=names, message=message,
1585 ldapadminpass=ldapadminpass, root=root,
1587 ldap_backend_extra_port=ldap_backend_extra_port,
1588 ol_mmr_urls=ol_mmr_urls,
1589 slapd_path=slapd_path,
1591 ldap_dryrun_mode=ldap_dryrun_mode)
1593 raise ProvisioningError("Unknown LDAP backend type selected")
1595 self.credentials.set_password(ldapadminpass)
1596 self.secrets_credentials.set_username("samba-admin")
1597 self.secrets_credentials.set_password(ldapadminpass)
1599 self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'"
1600 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1601 "SLAPD_COMMAND" : slapd_command})
1603 # Now start the slapd, so we can provision onto it. We keep the
1604 # subprocess context around, to kill this off at the successful
1606 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1608 while self.slapd.poll() is None:
1609 # Wait until the socket appears
1611 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1612 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1613 expression="(objectClass=OpenLDAProotDSE)")
1614 # If we have got here, then we must have a valid connection to the LDAP server!
1620 raise ProvisioningError("slapd died before we could make a connection to it")
1623 def provision_openldap_backend(result, setup_path=None, names=None,
1625 hostname=None, ldapadminpass=None, root=None,
1627 ldap_backend_extra_port=None,
1629 slapd_path=None, nosync=False,
1630 ldap_dryrun_mode=False):
1632 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1635 nosync_config = "dbnosync"
1637 lnkattr = schema.linked_attributes()
1638 refint_attributes = ""
1639 memberof_config = "# Generated from Samba4 schema\n"
1640 for att in lnkattr.keys():
1641 if lnkattr[att] is not None:
1642 refint_attributes = refint_attributes + " " + att
1644 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1645 { "MEMBER_ATTR" : att ,
1646 "MEMBEROF_ATTR" : lnkattr[att] })
1648 refint_config = read_and_sub_file(setup_path("refint.conf"),
1649 { "LINK_ATTRS" : refint_attributes})
1651 attrs = ["linkID", "lDAPDisplayName"]
1652 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1654 for i in range (0, len(res)):
1655 index_attr = res[i]["lDAPDisplayName"][0]
1656 if index_attr == "objectGUID":
1657 index_attr = "entryUUID"
1659 index_config += "index " + index_attr + " eq\n"
1661 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1663 mmr_replicator_acl = ""
1664 mmr_serverids_config = ""
1665 mmr_syncrepl_schema_config = ""
1666 mmr_syncrepl_config_config = ""
1667 mmr_syncrepl_user_config = ""
1670 if ol_mmr_urls is not None:
1671 # For now, make these equal
1672 mmr_pass = ldapadminpass
1674 url_list=filter(None,ol_mmr_urls.split(' '))
1675 if (len(url_list) == 1):
1676 url_list=filter(None,ol_mmr_urls.split(','))
1679 mmr_on_config = "MirrorMode On"
1680 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1682 for url in url_list:
1684 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1685 { "SERVERID" : str(serverid),
1686 "LDAPSERVER" : url })
1689 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1691 "MMRDN": names.schemadn,
1693 "MMR_PASSWORD": mmr_pass})
1696 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1698 "MMRDN": names.configdn,
1700 "MMR_PASSWORD": mmr_pass})
1703 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1705 "MMRDN": names.domaindn,
1707 "MMR_PASSWORD": mmr_pass })
1708 # OpenLDAP cn=config initialisation
1709 olc_syncrepl_config = ""
1711 # if mmr = yes, generate cn=config-replication directives
1712 # and olc_seed.lif for the other mmr-servers
1713 if ol_mmr_urls is not None:
1715 olc_serverids_config = ""
1716 olc_syncrepl_seed_config = ""
1717 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1719 for url in url_list:
1721 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1722 { "SERVERID" : str(serverid),
1723 "LDAPSERVER" : url })
1726 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1729 "MMR_PASSWORD": mmr_pass})
1731 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1733 "LDAPSERVER" : url})
1735 setup_file(setup_path("olc_seed.ldif"), result.paths.olcseedldif,
1736 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1737 "OLC_PW": ldapadminpass,
1738 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1741 setup_file(setup_path("slapd.conf"), result.paths.slapdconf,
1742 {"DNSDOMAIN": names.dnsdomain,
1743 "LDAPDIR": result.paths.ldapdir,
1744 "DOMAINDN": names.domaindn,
1745 "CONFIGDN": names.configdn,
1746 "SCHEMADN": names.schemadn,
1747 "MEMBEROF_CONFIG": memberof_config,
1748 "MIRRORMODE": mmr_on_config,
1749 "REPLICATOR_ACL": mmr_replicator_acl,
1750 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1751 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1752 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1753 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1754 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1755 "OLC_MMR_CONFIG": olc_mmr_config,
1756 "REFINT_CONFIG": refint_config,
1757 "INDEX_CONFIG": index_config,
1758 "NOSYNC": nosync_config})
1760 setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "user"))
1761 setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "config"))
1762 setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "schema"))
1764 if not os.path.exists(os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba")):
1765 os.makedirs(os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1767 setup_file(setup_path("cn=samba.ldif"),
1768 os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1769 { "UUID": str(uuid.uuid4()),
1770 "LDAPTIME": timestring(int(time.time()))} )
1771 setup_file(setup_path("cn=samba-admin.ldif"),
1772 os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1773 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1774 "UUID": str(uuid.uuid4()),
1775 "LDAPTIME": timestring(int(time.time()))} )
1777 if ol_mmr_urls is not None:
1778 setup_file(setup_path("cn=replicator.ldif"),
1779 os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1780 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1781 "UUID": str(uuid.uuid4()),
1782 "LDAPTIME": timestring(int(time.time()))} )
1785 mapping = "schema-map-openldap-2.3"
1786 backend_schema = "backend-schema.schema"
1788 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1789 assert backend_schema_data is not None
1790 open(os.path.join(result.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1792 # now we generate the needed strings to start slapd automatically,
1793 # first ldapi_uri...
1794 if ldap_backend_extra_port is not None:
1795 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1796 # specified there as part of it's clue as to it's own name,
1797 # and not to replicate to itself
1798 if ol_mmr_urls is None:
1799 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1801 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1803 server_port_string = ""
1805 # Prepare the 'result' information - the commands to return in particular
1806 result.slapd_provision_command = [slapd_path]
1808 result.slapd_provision_command.append("-F" + result.paths.olcdir)
1810 result.slapd_provision_command.append("-h")
1812 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1813 result.slapd_command = list(result.slapd_provision_command)
1815 result.slapd_provision_command.append(result.ldapi_uri)
1816 result.slapd_provision_command.append("-d0")
1818 uris = result.ldapi_uri
1819 if server_port_string is not "":
1820 uris = uris + " " + server_port_string
1822 result.slapd_command.append(uris)
1824 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1825 result.credentials.set_username("samba-admin")
1827 # If we were just looking for crashes up to this point, it's a
1828 # good time to exit before we realise we don't have OpenLDAP on
1830 if ldap_dryrun_mode:
1833 # Finally, convert the configuration into cn=config style!
1834 if not os.path.isdir(result.paths.olcdir):
1835 os.makedirs(result.paths.olcdir, 0770)
1837 retcode = subprocess.call([slapd_path, "-Ttest", "-f", result.paths.slapdconf, "-F", result.paths.olcdir], close_fds=True, shell=False)
1839 # We can't do this, as OpenLDAP is strange. It gives an error
1840 # output to the above, but does the conversion sucessfully...
1843 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1845 if not os.path.exists(os.path.join(result.paths.olcdir, "cn=config.ldif")):
1846 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1848 # Don't confuse the admin by leaving the slapd.conf around
1849 os.remove(result.paths.slapdconf)
1852 def provision_fds_backend(result, setup_path=None, names=None,
1854 hostname=None, ldapadminpass=None, root=None,
1856 ldap_backend_extra_port=None,
1860 ldap_dryrun_mode=False,
1863 if ldap_backend_extra_port is not None:
1864 serverport = "ServerPort=%d" % ldap_backend_extra_port
1868 setup_file(setup_path("fedorads.inf"), result.paths.fedoradsinf,
1870 "HOSTNAME": hostname,
1871 "DNSDOMAIN": names.dnsdomain,
1872 "LDAPDIR": result.paths.ldapdir,
1873 "DOMAINDN": names.domaindn,
1874 "LDAPMANAGERDN": names.ldapmanagerdn,
1875 "LDAPMANAGERPASS": ldapadminpass,
1876 "SERVERPORT": serverport})
1878 setup_file(setup_path("fedorads-partitions.ldif"), result.paths.fedoradspartitions,
1879 {"CONFIGDN": names.configdn,
1880 "SCHEMADN": names.schemadn,
1881 "SAMBADN": names.sambadn,
1884 setup_file(setup_path("fedorads-sasl.ldif"), result.paths.fedoradssasl,
1885 {"SAMBADN": names.sambadn,
1888 setup_file(setup_path("fedorads-dna.ldif"), result.paths.fedoradsdna,
1889 {"DOMAINDN": names.domaindn,
1890 "SAMBADN": names.sambadn,
1891 "DOMAINSID": str(domainsid),
1894 setup_file(setup_path("fedorads-pam.ldif"), result.paths.fedoradspam)
1896 lnkattr = schema.linked_attributes()
1898 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1899 memberof_config = ""
1903 for attr in lnkattr.keys():
1904 if lnkattr[attr] is not None:
1905 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1906 { "ARG_NUMBER" : str(argnum) ,
1907 "LINK_ATTR" : attr })
1908 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1909 { "MEMBER_ATTR" : attr ,
1910 "MEMBEROF_ATTR" : lnkattr[attr] })
1911 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1915 open(result.paths.fedoradsrefint, 'w').write(refint_config)
1916 open(result.paths.fedoradslinkedattributes, 'w').write(memberof_config)
1918 attrs = ["lDAPDisplayName"]
1919 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1921 for i in range (0, len(res)):
1922 attr = res[i]["lDAPDisplayName"][0]
1924 if attr == "objectGUID":
1927 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1930 open(result.paths.fedoradsindex, 'w').write(index_config)
1932 setup_file(setup_path("fedorads-samba.ldif"), result.paths.fedoradssamba,
1933 {"SAMBADN": names.sambadn,
1934 "LDAPADMINPASS": ldapadminpass
1937 mapping = "schema-map-fedora-ds-1.0"
1938 backend_schema = "99_ad.ldif"
1940 # Build a schema file in Fedora DS format
1941 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1942 assert backend_schema_data is not None
1943 open(os.path.join(result.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1945 result.credentials.set_bind_dn(names.ldapmanagerdn)
1947 # Destory the target directory, or else setup-ds.pl will complain
1948 fedora_ds_dir = os.path.join(result.paths.ldapdir, "slapd-samba4")
1949 shutil.rmtree(fedora_ds_dir, True)
1951 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", result.paths.slapdpid];
1952 #In the 'provision' command line, stay in the foreground so we can easily kill it
1953 result.slapd_provision_command.append("-d0")
1955 #the command for the final run is the normal script
1956 result.slapd_command = [os.path.join(result.paths.ldapdir, "slapd-samba4", "start-slapd")]
1958 # If we were just looking for crashes up to this point, it's a
1959 # good time to exit before we realise we don't have Fedora DS on
1960 if ldap_dryrun_mode:
1963 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1964 if setup_ds_path is None:
1965 raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1966 if not os.path.exists(setup_ds_path):
1967 message (setup_ds_path)
1968 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1970 # Run the Fedora DS setup utility
1971 retcode = subprocess.call([setup_ds_path, "--silent", "--file", result.paths.fedoradsinf], close_fds=True, shell=False)
1973 raise ProvisioningError("setup-ds failed")
1976 retcode = subprocess.call([
1977 os.path.join(result.paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", result.paths.fedoradssamba],
1978 close_fds=True, shell=False)
1980 raise("ldib2db failed")
1982 # Leave a hook to do the 'post initilisation' setup
1983 def fds_post_setup(self):
1984 ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
1986 # delete default SASL mappings
1987 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1989 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1990 for i in range (0, len(res)):
1991 dn = str(res[i]["dn"])
1994 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1997 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1999 m.dn = ldb.Dn(1, names.domaindn)
2002 m.dn = ldb.Dn(1, names.configdn)
2005 m.dn = ldb.Dn(1, names.schemadn)
2008 result.post_setup = fds_post_setup
2011 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2012 """Create a PHP LDAP admin configuration file.
2014 :param path: Path to write the configuration to.
2015 :param setup_path: Function to generate setup paths.
2017 setup_file(setup_path("phpldapadmin-config.php"), path,
2018 {"S4_LDAPI_URI": ldapi_uri})
2021 def create_zone_file(path, setup_path, dnsdomain,
2022 hostip, hostip6, hostname, realm, domainguid,
2024 """Write out a DNS zone file, from the info in the current database.
2026 :param path: Path of the new zone file.
2027 :param setup_path: Setup path function.
2028 :param dnsdomain: DNS Domain name
2029 :param domaindn: DN of the Domain
2030 :param hostip: Local IPv4 IP
2031 :param hostip6: Local IPv6 IP
2032 :param hostname: Local hostname
2033 :param realm: Realm name
2034 :param domainguid: GUID of the domain.
2035 :param ntdsguid: GUID of the hosts nTDSDSA record.
2037 assert isinstance(domainguid, str)
2039 if hostip6 is not None:
2040 hostip6_base_line = " IN AAAA " + hostip6
2041 hostip6_host_line = hostname + " IN AAAA " + hostip6
2043 hostip6_base_line = ""
2044 hostip6_host_line = ""
2046 if hostip is not None:
2047 hostip_base_line = " IN A " + hostip
2048 hostip_host_line = hostname + " IN A " + hostip
2050 hostip_base_line = ""
2051 hostip_host_line = ""
2053 setup_file(setup_path("provision.zone"), path, {
2054 "HOSTNAME": hostname,
2055 "DNSDOMAIN": dnsdomain,
2057 "HOSTIP_BASE_LINE": hostip_base_line,
2058 "HOSTIP_HOST_LINE": hostip_host_line,
2059 "DOMAINGUID": domainguid,
2060 "DATESTRING": time.strftime("%Y%m%d%H"),
2061 "DEFAULTSITE": DEFAULTSITE,
2062 "NTDSGUID": ntdsguid,
2063 "HOSTIP6_BASE_LINE": hostip6_base_line,
2064 "HOSTIP6_HOST_LINE": hostip6_host_line,
2068 def create_named_conf(path, setup_path, realm, dnsdomain,
2070 """Write out a file containing zone statements suitable for inclusion in a
2071 named.conf file (including GSS-TSIG configuration).
2073 :param path: Path of the new named.conf file.
2074 :param setup_path: Setup path function.
2075 :param realm: Realm name
2076 :param dnsdomain: DNS Domain name
2077 :param private_dir: Path to private directory
2078 :param keytab_name: File name of DNS keytab file
2081 setup_file(setup_path("named.conf"), path, {
2082 "DNSDOMAIN": dnsdomain,
2084 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2085 "PRIVATE_DIR": private_dir
2088 def create_named_txt(path, setup_path, realm, dnsdomain,
2089 private_dir, keytab_name):
2090 """Write out a file containing zone statements suitable for inclusion in a
2091 named.conf file (including GSS-TSIG configuration).
2093 :param path: Path of the new named.conf file.
2094 :param setup_path: Setup path function.
2095 :param realm: Realm name
2096 :param dnsdomain: DNS Domain name
2097 :param private_dir: Path to private directory
2098 :param keytab_name: File name of DNS keytab file
2101 setup_file(setup_path("named.txt"), path, {
2102 "DNSDOMAIN": dnsdomain,
2104 "DNS_KEYTAB": keytab_name,
2105 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2106 "PRIVATE_DIR": private_dir
2109 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2110 """Write out a file containing zone statements suitable for inclusion in a
2111 named.conf file (including GSS-TSIG configuration).
2113 :param path: Path of the new named.conf file.
2114 :param setup_path: Setup path function.
2115 :param dnsdomain: DNS Domain name
2116 :param hostname: Local hostname
2117 :param realm: Realm name
2120 setup_file(setup_path("krb5.conf"), path, {
2121 "DNSDOMAIN": dnsdomain,
2122 "HOSTNAME": hostname,