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
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_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
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_schema_descriptor(domain_sid):
77 sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
78 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
79 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
81 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
83 "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
84 "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
85 "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
86 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
87 sec = security.descriptor.from_sddl(sddl, domain_sid)
88 return b64encode(ndr_pack(sec))
90 def get_config_descriptor(domain_sid):
91 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
97 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
98 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
99 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
101 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
103 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
104 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
105 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
106 sec = security.descriptor.from_sddl(sddl, domain_sid)
107 return b64encode(ndr_pack(sec))
110 DEFAULTSITE = "Default-First-Site-Name"
114 class ProvisioningError(Exception):
115 """A generic provision error."""
117 class InvalidNetbiosName(Exception):
118 """A specified name was not a valid NetBIOS name."""
119 def __init__(self, name):
120 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
123 class ProvisionPaths(object):
125 self.shareconf = None
136 self.dns_keytab = None
139 self.private_dir = None
141 self.slapdconf = None
142 self.modulesconf = None
143 self.memberofconf = None
144 self.fedoradsinf = None
145 self.fedoradspartitions = None
146 self.fedoradssasl = None
148 self.olmmrserveridsconf = None
149 self.olmmrsyncreplconf = None
152 self.olcseedldif = None
155 class ProvisionNames(object):
162 self.ldapmanagerdn = None
163 self.dnsdomain = None
165 self.netbiosname = None
172 class ProvisionResult(object):
179 class Schema(object):
180 def __init__(self, setup_path, domain_sid, schemadn=None,
181 serverdn=None, sambadn=None, ldap_backend_type=None):
182 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
184 :param samdb: Load a schema into a SamDB.
185 :param setup_path: Setup path function.
186 :param schemadn: DN of the schema
187 :param serverdn: DN of the server
189 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
193 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
194 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
195 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
196 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
197 check_all_substituted(self.schema_data)
199 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
200 {"SCHEMADN": schemadn,
201 "SERVERDN": serverdn,
204 descr = get_schema_descriptor(domain_sid)
205 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
206 {"SCHEMADN": schemadn,
210 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
211 prefixmap = b64encode(prefixmap)
215 # We don't actually add this ldif, just parse it
216 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
217 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
220 # Return a hash with the forward attribute as a key and the back as the value
221 def get_linked_attributes(schemadn,schemaldb):
222 attrs = ["linkID", "lDAPDisplayName"]
223 res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
225 for i in range (0, len(res)):
226 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
227 target = schemaldb.searchone(basedn=schemadn,
228 expression=expression,
229 attribute="lDAPDisplayName",
231 if target is not None:
232 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
236 def get_dnsyntax_attributes(schemadn,schemaldb):
237 attrs = ["linkID", "lDAPDisplayName"]
238 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
240 for i in range (0, len(res)):
241 attributes.append(str(res[i]["lDAPDisplayName"]))
246 def check_install(lp, session_info, credentials):
247 """Check whether the current install seems ok.
249 :param lp: Loadparm context
250 :param session_info: Session information
251 :param credentials: Credentials
253 if lp.get("realm") == "":
254 raise Exception("Realm empty")
255 ldb = Ldb(lp.get("sam database"), session_info=session_info,
256 credentials=credentials, lp=lp)
257 if len(ldb.search("(cn=Administrator)")) != 1:
258 raise ProvisioningError("No administrator account found")
261 def findnss(nssfn, names):
262 """Find a user or group from a list of possibilities.
264 :param nssfn: NSS Function to try (should raise KeyError if not found)
265 :param names: Names to check.
266 :return: Value return by first names list.
273 raise KeyError("Unable to find user/group %r" % names)
276 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
277 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
280 def read_and_sub_file(file, subst_vars):
281 """Read a file and sub in variables found in it
283 :param file: File to be read (typically from setup directory)
284 param subst_vars: Optional variables to subsitute in the file.
286 data = open(file, 'r').read()
287 if subst_vars is not None:
288 data = substitute_var(data, subst_vars)
289 check_all_substituted(data)
293 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
294 """Setup a ldb in the private dir.
296 :param ldb: LDB file to import data into
297 :param ldif_path: Path of the LDIF file to load
298 :param subst_vars: Optional variables to subsitute in LDIF.
299 :param nocontrols: Optional list of controls, can be None for no controls
301 assert isinstance(ldif_path, str)
302 data = read_and_sub_file(ldif_path, subst_vars)
303 ldb.add_ldif(data,controls)
306 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
307 """Modify a ldb in the private dir.
309 :param ldb: LDB object.
310 :param ldif_path: LDIF file path.
311 :param subst_vars: Optional dictionary with substitution variables.
313 data = read_and_sub_file(ldif_path, subst_vars)
315 ldb.modify_ldif(data)
318 def setup_ldb(ldb, ldif_path, subst_vars):
319 """Import a LDIF a file into a LDB handle, optionally substituting variables.
321 :note: Either all LDIF data will be added or none (using transactions).
323 :param ldb: LDB file to import into.
324 :param ldif_path: Path to the LDIF file.
325 :param subst_vars: Dictionary with substitution variables.
327 assert ldb is not None
328 ldb.transaction_start()
330 setup_add_ldif(ldb, ldif_path, subst_vars)
332 ldb.transaction_cancel()
334 ldb.transaction_commit()
337 def setup_file(template, fname, subst_vars):
338 """Setup a file in the private dir.
340 :param template: Path of the template file.
341 :param fname: Path of the file to create.
342 :param subst_vars: Substitution variables.
346 if os.path.exists(f):
349 data = read_and_sub_file(template, subst_vars)
350 open(f, 'w').write(data)
353 def provision_paths_from_lp(lp, dnsdomain):
354 """Set the default paths for provisioning.
356 :param lp: Loadparm context.
357 :param dnsdomain: DNS Domain name
359 paths = ProvisionPaths()
360 paths.private_dir = lp.get("private dir")
361 paths.dns_keytab = "dns.keytab"
363 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
364 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
365 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
366 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
367 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
368 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
369 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
370 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
371 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
372 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
373 paths.phpldapadminconfig = os.path.join(paths.private_dir,
374 "phpldapadmin-config.php")
375 paths.ldapdir = os.path.join(paths.private_dir,
377 paths.slapdconf = os.path.join(paths.ldapdir,
379 paths.slapdpid = os.path.join(paths.ldapdir,
381 paths.modulesconf = os.path.join(paths.ldapdir,
383 paths.memberofconf = os.path.join(paths.ldapdir,
385 paths.fedoradsinf = os.path.join(paths.ldapdir,
387 paths.fedoradspartitions = os.path.join(paths.ldapdir,
388 "fedorads-partitions.ldif")
389 paths.fedoradssasl = os.path.join(paths.ldapdir,
390 "fedorads-sasl.ldif")
391 paths.fedoradssamba = os.path.join(paths.ldapdir,
392 "fedorads-samba.ldif")
393 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
394 "mmr_serverids.conf")
395 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
397 paths.olcdir = os.path.join(paths.ldapdir,
399 paths.olcseedldif = os.path.join(paths.ldapdir,
401 paths.hklm = "hklm.ldb"
402 paths.hkcr = "hkcr.ldb"
403 paths.hkcu = "hkcu.ldb"
404 paths.hku = "hku.ldb"
405 paths.hkpd = "hkpd.ldb"
406 paths.hkpt = "hkpt.ldb"
408 paths.sysvol = lp.get("path", "sysvol")
410 paths.netlogon = lp.get("path", "netlogon")
412 paths.smbconf = lp.configfile
417 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
418 serverrole=None, rootdn=None, domaindn=None, configdn=None,
419 schemadn=None, serverdn=None, sitename=None, sambadn=None):
420 """Guess configuration settings to use."""
423 hostname = socket.gethostname().split(".")[0].lower()
425 netbiosname = hostname.upper()
426 if not valid_netbios_name(netbiosname):
427 raise InvalidNetbiosName(netbiosname)
429 hostname = hostname.lower()
431 if dnsdomain is None:
432 dnsdomain = lp.get("realm").lower()
434 if serverrole is None:
435 serverrole = lp.get("server role")
437 assert dnsdomain is not None
438 realm = dnsdomain.upper()
440 if lp.get("realm").upper() != realm:
441 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
442 (lp.get("realm"), lp.configfile, realm))
444 if serverrole == "domain controller":
446 domain = lp.get("workgroup")
448 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
449 if lp.get("workgroup").upper() != domain.upper():
450 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
451 lp.get("workgroup"), domain)
455 domaindn = "DC=" + netbiosname
457 assert domain is not None
458 domain = domain.upper()
460 if not valid_netbios_name(domain):
461 raise InvalidNetbiosName(domain)
463 if netbiosname.upper() == realm:
464 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
466 if hostname.upper() == realm:
467 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
469 if domain.upper() == realm:
470 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
476 configdn = "CN=Configuration," + rootdn
478 schemadn = "CN=Schema," + configdn
485 names = ProvisionNames()
486 names.rootdn = rootdn
487 names.domaindn = domaindn
488 names.configdn = configdn
489 names.schemadn = schemadn
490 names.sambadn = sambadn
491 names.ldapmanagerdn = "CN=Manager," + rootdn
492 names.dnsdomain = dnsdomain
493 names.domain = domain
495 names.netbiosname = netbiosname
496 names.hostname = hostname
497 names.sitename = sitename
498 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
503 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
505 """Create a new smb.conf file based on a couple of basic settings.
507 assert smbconf is not None
509 hostname = socket.gethostname().split(".")[0].lower()
511 if serverrole is None:
512 serverrole = "standalone"
514 assert serverrole in ("domain controller", "member server", "standalone")
515 if serverrole == "domain controller":
517 elif serverrole == "member server":
518 smbconfsuffix = "member"
519 elif serverrole == "standalone":
520 smbconfsuffix = "standalone"
522 assert domain is not None
523 assert realm is not None
525 default_lp = param.LoadParm()
526 #Load non-existant file
527 if os.path.exists(smbconf):
528 default_lp.load(smbconf)
530 if targetdir is not None:
531 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
532 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
534 default_lp.set("lock dir", os.path.abspath(targetdir))
539 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
540 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
542 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
544 "HOSTNAME": hostname,
547 "SERVERROLE": serverrole,
548 "NETLOGONPATH": netlogon,
549 "SYSVOLPATH": sysvol,
550 "PRIVATEDIR_LINE": privatedir_line,
551 "LOCKDIR_LINE": lockdir_line
555 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
556 users_gid, wheel_gid):
557 """setup reasonable name mappings for sam names to unix names.
559 :param samdb: SamDB object.
560 :param idmap: IDmap db object.
561 :param sid: The domain sid.
562 :param domaindn: The domain DN.
563 :param root_uid: uid of the UNIX root user.
564 :param nobody_uid: uid of the UNIX nobody user.
565 :param users_gid: gid of the UNIX users group.
566 :param wheel_gid: gid of the UNIX wheel group."""
568 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
569 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
571 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
572 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
574 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
576 serverrole, ldap_backend=None,
578 """Setup the partitions for the SAM database.
580 Alternatively, provision() may call this, and then populate the database.
582 :note: This will wipe the Sam Database!
584 :note: This function always removes the local SAM LDB file. The erase
585 parameter controls whether to erase the existing data, which
586 may not be stored locally but in LDAP.
588 assert session_info is not None
590 # We use options=["modules:"] to stop the modules loading - we
591 # just want to wipe and re-initialise the database, not start it up
594 samdb = Ldb(url=samdb_path, session_info=session_info,
595 credentials=credentials, lp=lp, options=["modules:"])
597 samdb.erase_except_schema_controlled()
599 os.unlink(samdb_path)
600 samdb = Ldb(url=samdb_path, session_info=session_info,
601 credentials=credentials, lp=lp, options=["modules:"])
603 samdb.erase_except_schema_controlled()
606 #Add modules to the list to activate them by default
607 #beware often order is important
609 # Some Known ordering constraints:
610 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
611 # - objectclass must be before password_hash, because password_hash checks
612 # that the objectclass is of type person (filled in by objectclass
613 # module when expanding the objectclass list)
614 # - partition must be last
615 # - each partition has its own module list then
616 modules_list = ["resolve_oids",
639 "extended_dn_out_ldb"]
640 modules_list2 = ["show_deleted",
643 domaindn_ldb = "users.ldb"
644 configdn_ldb = "configuration.ldb"
645 schemadn_ldb = "schema.ldb"
646 if ldap_backend is not None:
647 domaindn_ldb = ldap_backend.ldapi_uri
648 configdn_ldb = ldap_backend.ldapi_uri
649 schemadn_ldb = ldap_backend.ldapi_uri
651 if ldap_backend.ldap_backend_type == "fedora-ds":
652 backend_modules = ["nsuniqueid", "paged_searches"]
653 # We can handle linked attributes here, as we don't have directory-side subtree operations
654 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
655 elif ldap_backend.ldap_backend_type == "openldap":
656 backend_modules = ["entryuuid", "paged_searches"]
657 # OpenLDAP handles subtree renames, so we don't want to do any of these things
658 tdb_modules_list = ["extended_dn_out_dereference"]
660 elif serverrole == "domain controller":
661 tdb_modules_list.insert(0, "repl_meta_data")
664 backend_modules = ["objectguid"]
666 if tdb_modules_list is None:
667 tdb_modules_list_as_string = ""
669 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
671 samdb.transaction_start()
673 message("Setting up sam.ldb partitions and settings")
674 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
675 "SCHEMADN": names.schemadn,
676 "SCHEMADN_LDB": schemadn_ldb,
677 "SCHEMADN_MOD2": ",objectguid",
678 "CONFIGDN": names.configdn,
679 "CONFIGDN_LDB": configdn_ldb,
680 "DOMAINDN": names.domaindn,
681 "DOMAINDN_LDB": domaindn_ldb,
682 "SCHEMADN_MOD": "schema_fsmo",
683 "CONFIGDN_MOD": "naming_fsmo",
684 "DOMAINDN_MOD": "pdc_fsmo",
685 "MODULES_LIST": ",".join(modules_list),
686 "TDB_MODULES_LIST": tdb_modules_list_as_string,
687 "MODULES_LIST2": ",".join(modules_list2),
688 "BACKEND_MOD": ",".join(backend_modules),
691 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
693 message("Setting up sam.ldb rootDSE")
694 setup_samdb_rootdse(samdb, setup_path, names)
697 samdb.transaction_cancel()
700 samdb.transaction_commit()
702 def secretsdb_self_join(secretsdb, domain,
703 netbiosname, domainsid, machinepass,
704 realm=None, dnsdomain=None,
706 key_version_number=1,
707 secure_channel_type=SEC_CHAN_WKSTA):
708 """Add domain join-specific bits to a secrets database.
710 :param secretsdb: Ldb Handle to the secrets database
711 :param machinepass: Machine password
713 attrs=["whenChanged",
721 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
722 msg["secureChannelType"] = str(secure_channel_type)
723 msg["flatname"] = [domain]
724 msg["objectClass"] = ["top", "primaryDomain"]
725 if realm is not None:
726 if dnsdomain is None:
727 dnsdomain = realm.lower()
728 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
730 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
731 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
732 msg["privateKeytab"] = ["secrets.keytab"];
735 msg["secret"] = [machinepass]
736 msg["samAccountName"] = ["%s$" % netbiosname]
737 msg["secureChannelType"] = [str(secure_channel_type)]
738 msg["objectSid"] = [ndr_pack(domainsid)]
740 res = secretsdb.search(base="cn=Primary Domains",
742 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
743 scope=SCOPE_ONELEVEL)
746 if del_msg.dn is not msg.dn:
747 secretsdb.delete(del_msg.dn)
749 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
752 msg["priorSecret"] = res[0]["secret"]
753 msg["priorWhenChanged"] = res[0]["whenChanged"]
755 if res["privateKeytab"] is not None:
756 msg["privateKeytab"] = res[0]["privateKeytab"]
758 if res["krb5Keytab"] is not None:
759 msg["krb5Keytab"] = res[0]["krb5Keytab"]
762 el.set_flags(ldb.FLAG_MOD_REPLACE)
763 secretsdb.modify(msg)
768 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
769 dns_keytab_path, dnspass):
770 """Add DNS specific bits to a secrets database.
772 :param secretsdb: Ldb Handle to the secrets database
773 :param setup_path: Setup path function
774 :param machinepass: Machine password
776 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
778 "DNSDOMAIN": dnsdomain,
779 "DNS_KEYTAB": dns_keytab_path,
780 "DNSPASS_B64": b64encode(dnspass),
784 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
785 """Setup the secrets database.
787 :param path: Path to the secrets database.
788 :param setup_path: Get the path to a setup file.
789 :param session_info: Session info.
790 :param credentials: Credentials
791 :param lp: Loadparm context
792 :return: LDB handle for the created secrets database
794 if os.path.exists(path):
796 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
799 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
800 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
802 secrets_ldb.transaction_start()
803 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
805 if credentials is not None and credentials.authentication_requested():
806 if credentials.get_bind_dn() is not None:
807 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
808 "LDAPMANAGERDN": credentials.get_bind_dn(),
809 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
812 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
813 "LDAPADMINUSER": credentials.get_username(),
814 "LDAPADMINREALM": credentials.get_realm(),
815 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
820 def setup_registry(path, setup_path, session_info, lp):
821 """Setup the registry.
823 :param path: Path to the registry database
824 :param setup_path: Function that returns the path to a setup.
825 :param session_info: Session information
826 :param credentials: Credentials
827 :param lp: Loadparm context
829 reg = registry.Registry()
830 hive = registry.open_ldb(path, session_info=session_info,
832 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
833 provision_reg = setup_path("provision.reg")
834 assert os.path.exists(provision_reg)
835 reg.diff_apply(provision_reg)
838 def setup_idmapdb(path, setup_path, session_info, lp):
839 """Setup the idmap database.
841 :param path: path to the idmap database
842 :param setup_path: Function that returns a path to a setup file
843 :param session_info: Session information
844 :param credentials: Credentials
845 :param lp: Loadparm context
847 if os.path.exists(path):
850 idmap_ldb = IDmapDB(path, session_info=session_info,
854 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
858 def setup_samdb_rootdse(samdb, setup_path, names):
859 """Setup the SamDB rootdse.
861 :param samdb: Sam Database handle
862 :param setup_path: Obtain setup path
864 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
865 "SCHEMADN": names.schemadn,
866 "NETBIOSNAME": names.netbiosname,
867 "DNSDOMAIN": names.dnsdomain,
868 "REALM": names.realm,
869 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
870 "DOMAINDN": names.domaindn,
871 "ROOTDN": names.rootdn,
872 "CONFIGDN": names.configdn,
873 "SERVERDN": names.serverdn,
877 def setup_self_join(samdb, names,
878 machinepass, dnspass,
879 domainsid, invocationid, setup_path,
880 policyguid, policyguid_dc, domainControllerFunctionality,
882 """Join a host to its own domain."""
883 assert isinstance(invocationid, str)
884 if ntdsguid is not None:
885 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
888 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
889 "CONFIGDN": names.configdn,
890 "SCHEMADN": names.schemadn,
891 "DOMAINDN": names.domaindn,
892 "SERVERDN": names.serverdn,
893 "INVOCATIONID": invocationid,
894 "NETBIOSNAME": names.netbiosname,
895 "DEFAULTSITE": names.sitename,
896 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
897 "MACHINEPASS_B64": b64encode(machinepass),
898 "DNSPASS_B64": b64encode(dnspass),
899 "REALM": names.realm,
900 "DOMAIN": names.domain,
901 "DNSDOMAIN": names.dnsdomain,
902 "SAMBA_VERSION_STRING": version,
903 "NTDSGUID": ntdsguid_line,
904 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
906 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
907 "POLICYGUID": policyguid,
908 "POLICYGUID_DC": policyguid_dc,
909 "DNSDOMAIN": names.dnsdomain,
910 "DOMAINSID": str(domainsid),
911 "DOMAINDN": names.domaindn})
913 # add the NTDSGUID based SPNs
914 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
915 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
916 expression="", scope=SCOPE_BASE)
917 assert isinstance(names.ntdsguid, str)
919 # Setup fSMORoleOwner entries to point at the newly created DC entry
920 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
921 "DOMAIN": names.domain,
922 "DNSDOMAIN": names.dnsdomain,
923 "DOMAINDN": names.domaindn,
924 "CONFIGDN": names.configdn,
925 "SCHEMADN": names.schemadn,
926 "DEFAULTSITE": names.sitename,
927 "SERVERDN": names.serverdn,
928 "NETBIOSNAME": names.netbiosname,
929 "NTDSGUID": names.ntdsguid
933 def setup_samdb(path, setup_path, session_info, credentials, lp,
935 domainsid, domainguid, policyguid, policyguid_dc,
936 fill, adminpass, krbtgtpass,
937 machinepass, invocationid, dnspass, ntdsguid,
938 serverrole, dom_for_fun_level=None,
939 schema=None, ldap_backend=None):
940 """Setup a complete SAM Database.
942 :note: This will wipe the main SAM database file!
945 # ATTENTION: Do NOT change these default values without discussion with the
946 # team and/or release manager. They have a big impact on the whole program!
947 domainControllerFunctionality = DS_DC_FUNCTION_2008
949 if dom_for_fun_level is None:
950 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
951 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
952 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
954 if dom_for_fun_level > domainControllerFunctionality:
955 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!")
957 domainFunctionality = dom_for_fun_level
958 forestFunctionality = dom_for_fun_level
960 # Also wipes the database
961 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
962 credentials=credentials, session_info=session_info,
963 names=names, ldap_backend=ldap_backend,
964 serverrole=serverrole)
967 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
968 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
970 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
971 samdb = Ldb(session_info=session_info,
972 credentials=credentials, lp=lp)
974 message("Pre-loading the Samba 4 and AD schema")
976 # Load the schema from the one we computed earlier
977 samdb.set_schema_from_ldb(schema.ldb)
979 # And now we can connect to the DB - the schema won't be loaded from the DB
983 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
988 samdb.transaction_start()
990 message("Erasing data from partitions")
991 # Load the schema (again). This time it will force a reindex,
992 # and will therefore make the erase_partitions() below
993 # computationally sane
994 samdb.set_schema_from_ldb(schema.ldb)
995 samdb.erase_partitions()
997 # Set the domain functionality levels onto the database.
998 # Various module (the password_hash module in particular) need
999 # to know what level of AD we are emulating.
1001 # These will be fixed into the database via the database
1002 # modifictions below, but we need them set from the start.
1003 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1004 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1005 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1007 samdb.set_domain_sid(str(domainsid))
1008 if serverrole == "domain controller":
1009 samdb.set_invocation_id(invocationid)
1011 message("Adding DomainDN: %s" % names.domaindn)
1013 #impersonate domain admin
1014 admin_session_info = admin_session(lp, str(domainsid))
1015 samdb.set_session_info(admin_session_info)
1016 if domainguid is not None:
1017 domainguid_line = "objectGUID: %s\n-" % domainguid
1019 domainguid_line = ""
1020 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1021 "DOMAINDN": names.domaindn,
1022 "DOMAINGUID": domainguid_line
1026 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1027 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1028 "DOMAINSID": str(domainsid),
1029 "SCHEMADN": names.schemadn,
1030 "NETBIOSNAME": names.netbiosname,
1031 "DEFAULTSITE": names.sitename,
1032 "CONFIGDN": names.configdn,
1033 "SERVERDN": names.serverdn,
1034 "POLICYGUID": policyguid,
1035 "DOMAINDN": names.domaindn,
1036 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1037 "SAMBA_VERSION_STRING": version
1040 message("Adding configuration container")
1041 descr = get_config_descriptor(domainsid);
1042 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1043 "CONFIGDN": names.configdn,
1044 "DESCRIPTOR": descr,
1046 message("Modifying configuration container")
1047 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1048 "CONFIGDN": names.configdn,
1049 "SCHEMADN": names.schemadn,
1052 # The LDIF here was created when the Schema object was constructed
1053 message("Setting up sam.ldb schema")
1054 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1055 samdb.modify_ldif(schema.schema_dn_modify)
1056 samdb.write_prefixes_from_schema()
1057 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1058 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1059 {"SCHEMADN": names.schemadn})
1061 message("Setting up sam.ldb configuration data")
1062 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1063 "CONFIGDN": names.configdn,
1064 "NETBIOSNAME": names.netbiosname,
1065 "DEFAULTSITE": names.sitename,
1066 "DNSDOMAIN": names.dnsdomain,
1067 "DOMAIN": names.domain,
1068 "SCHEMADN": names.schemadn,
1069 "DOMAINDN": names.domaindn,
1070 "SERVERDN": names.serverdn,
1071 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1074 message("Setting up display specifiers")
1075 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1076 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1077 check_all_substituted(display_specifiers_ldif)
1078 samdb.add_ldif(display_specifiers_ldif)
1080 message("Adding users container")
1081 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1082 "DOMAINDN": names.domaindn})
1083 message("Modifying users container")
1084 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1085 "DOMAINDN": names.domaindn})
1086 message("Adding computers container")
1087 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1088 "DOMAINDN": names.domaindn})
1089 message("Modifying computers container")
1090 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1091 "DOMAINDN": names.domaindn})
1092 message("Setting up sam.ldb data")
1093 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1094 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1095 "DOMAINDN": names.domaindn,
1096 "NETBIOSNAME": names.netbiosname,
1097 "DEFAULTSITE": names.sitename,
1098 "CONFIGDN": names.configdn,
1099 "SERVERDN": names.serverdn,
1100 "POLICYGUID_DC": policyguid_dc
1103 if fill == FILL_FULL:
1104 message("Setting up sam.ldb users and groups")
1105 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1106 "DOMAINDN": names.domaindn,
1107 "DOMAINSID": str(domainsid),
1108 "CONFIGDN": names.configdn,
1109 "ADMINPASS_B64": b64encode(adminpass),
1110 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1113 if serverrole == "domain controller":
1114 message("Setting up self join")
1115 setup_self_join(samdb, names=names, invocationid=invocationid,
1117 machinepass=machinepass,
1118 domainsid=domainsid, policyguid=policyguid,
1119 policyguid_dc=policyguid_dc,
1120 setup_path=setup_path,
1121 domainControllerFunctionality=domainControllerFunctionality,
1124 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1125 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1126 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1127 assert isinstance(names.ntdsguid, str)
1130 samdb.transaction_cancel()
1133 samdb.transaction_commit()
1138 FILL_NT4SYNC = "NT4SYNC"
1142 def provision(setup_dir, message, session_info,
1143 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1145 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1147 domain=None, hostname=None, hostip=None, hostip6=None,
1148 domainsid=None, adminpass=None, ldapadminpass=None,
1149 krbtgtpass=None, domainguid=None,
1150 policyguid=None, policyguid_dc=None, invocationid=None,
1151 machinepass=None, ntdsguid=None,
1152 dnspass=None, root=None, nobody=None, users=None,
1153 wheel=None, backup=None, aci=None, serverrole=None,
1154 dom_for_fun_level=None,
1155 ldap_backend_extra_port=None, ldap_backend_type=None,
1157 ol_mmr_urls=None, ol_olc=None,
1158 setup_ds_path=None, slapd_path=None, nosync=False,
1159 ldap_dryrun_mode=False):
1162 :note: caution, this wipes all existing data!
1165 def setup_path(file):
1166 return os.path.join(setup_dir, file)
1168 if domainsid is None:
1169 domainsid = security.random_sid()
1171 domainsid = security.dom_sid(domainsid)
1173 # create/adapt the group policy GUIDs
1174 if policyguid is None:
1175 policyguid = str(uuid.uuid4())
1176 policyguid = policyguid.upper()
1177 if policyguid_dc is None:
1178 policyguid_dc = str(uuid.uuid4())
1179 policyguid_dc = policyguid_dc.upper()
1181 if adminpass is None:
1182 adminpass = glue.generate_random_str(12)
1183 if krbtgtpass is None:
1184 krbtgtpass = glue.generate_random_str(12)
1185 if machinepass is None:
1186 machinepass = glue.generate_random_str(12)
1188 dnspass = glue.generate_random_str(12)
1189 if ldapadminpass is None:
1190 #Make a new, random password between Samba and it's LDAP server
1191 ldapadminpass=glue.generate_random_str(12)
1194 root_uid = findnss_uid([root or "root"])
1195 nobody_uid = findnss_uid([nobody or "nobody"])
1196 users_gid = findnss_gid([users or "users"])
1198 wheel_gid = findnss_gid(["wheel", "adm"])
1200 wheel_gid = findnss_gid([wheel])
1202 if targetdir is not None:
1203 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1204 os.makedirs(os.path.join(targetdir, "etc"))
1205 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1206 elif smbconf is None:
1207 smbconf = param.default_path()
1209 # only install a new smb.conf if there isn't one there already
1210 if not os.path.exists(smbconf):
1211 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1214 lp = param.LoadParm()
1217 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1218 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1219 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1222 paths = provision_paths_from_lp(lp, names.dnsdomain)
1226 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1227 except socket.gaierror, (socket.EAI_NODATA, msg):
1232 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1233 except socket.gaierror, (socket.EAI_NODATA, msg):
1236 if serverrole is None:
1237 serverrole = lp.get("server role")
1239 assert serverrole in ("domain controller", "member server", "standalone")
1240 if invocationid is None and serverrole == "domain controller":
1241 invocationid = str(uuid.uuid4())
1243 if not os.path.exists(paths.private_dir):
1244 os.mkdir(paths.private_dir)
1246 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1248 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1249 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1251 secrets_credentials = credentials
1252 provision_backend = None
1253 if ldap_backend_type:
1254 # We only support an LDAP backend over ldapi://
1256 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1257 lp=lp, credentials=credentials,
1259 message=message, hostname=hostname,
1260 root=root, schema=schema,
1261 ldap_backend_type=ldap_backend_type,
1262 ldapadminpass=ldapadminpass,
1263 ldap_backend_extra_port=ldap_backend_extra_port,
1264 ol_mmr_urls=ol_mmr_urls,
1265 slapd_path=slapd_path,
1266 setup_ds_path=setup_ds_path,
1267 ldap_dryrun_mode=ldap_dryrun_mode)
1269 # Now use the backend credentials to access the databases
1270 credentials = provision_backend.credentials
1271 secrets_credentials = provision_backend.adminCredentials
1272 ldapi_url = provision_backend.ldapi_uri
1274 # only install a new shares config db if there is none
1275 if not os.path.exists(paths.shareconf):
1276 message("Setting up share.ldb")
1277 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1278 credentials=credentials, lp=lp)
1279 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1282 message("Setting up secrets.ldb")
1283 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1284 session_info=session_info,
1285 credentials=secrets_credentials, lp=lp)
1287 message("Setting up the registry")
1288 setup_registry(paths.hklm, setup_path, session_info,
1291 message("Setting up idmap db")
1292 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1295 message("Setting up SAM db")
1296 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1297 credentials=credentials, lp=lp, names=names,
1299 domainsid=domainsid,
1300 schema=schema, domainguid=domainguid,
1301 policyguid=policyguid, policyguid_dc=policyguid_dc,
1303 adminpass=adminpass, krbtgtpass=krbtgtpass,
1304 invocationid=invocationid,
1305 machinepass=machinepass, dnspass=dnspass,
1306 ntdsguid=ntdsguid, serverrole=serverrole,
1307 dom_for_fun_level=dom_for_fun_level,
1308 ldap_backend=provision_backend)
1310 if serverrole == "domain controller":
1311 if paths.netlogon is None:
1312 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1313 message("Please either remove %s or see the template at %s" %
1314 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1315 assert(paths.netlogon is not None)
1317 if paths.sysvol is None:
1318 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1319 message("Please either remove %s or see the template at %s" %
1320 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1321 assert(paths.sysvol is not None)
1323 # Set up group policies (domain policy and domain controller policy)
1325 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1326 "{" + policyguid + "}")
1327 os.makedirs(policy_path, 0755)
1328 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1329 "[General]\r\nVersion=65543")
1330 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1331 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1333 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1334 "{" + policyguid_dc + "}")
1335 os.makedirs(policy_path_dc, 0755)
1336 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1337 "[General]\r\nVersion=2")
1338 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1339 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1341 if not os.path.isdir(paths.netlogon):
1342 os.makedirs(paths.netlogon, 0755)
1344 if samdb_fill == FILL_FULL:
1345 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1346 root_uid=root_uid, nobody_uid=nobody_uid,
1347 users_gid=users_gid, wheel_gid=wheel_gid)
1349 message("Setting up sam.ldb rootDSE marking as synchronized")
1350 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1352 # Only make a zone file on the first DC, it should be replicated with DNS replication
1353 if serverrole == "domain controller":
1354 secretsdb_self_join(secrets_ldb, domain=domain,
1356 dnsdomain=names.dnsdomain,
1357 netbiosname=names.netbiosname,
1358 domainsid=domainsid,
1359 machinepass=machinepass,
1360 secure_channel_type=SEC_CHAN_BDC)
1362 secretsdb_setup_dns(secrets_ldb, setup_path,
1363 realm=names.realm, dnsdomain=names.dnsdomain,
1364 dns_keytab_path=paths.dns_keytab,
1367 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1368 assert isinstance(domainguid, str)
1370 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1372 hostip6=hostip6, hostname=names.hostname,
1374 domainguid=domainguid, ntdsguid=names.ntdsguid)
1376 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1377 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1379 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1380 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1381 keytab_name=paths.dns_keytab)
1382 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1383 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1385 create_krb5_conf(paths.krb5conf, setup_path,
1386 dnsdomain=names.dnsdomain, hostname=names.hostname,
1388 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1390 #Now commit the secrets.ldb to disk
1391 secrets_ldb.transaction_commit()
1393 if provision_backend is not None:
1394 if ldap_backend_type == "fedora-ds":
1395 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1397 # delete default SASL mappings
1398 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1400 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1401 for i in range (0, len(res)):
1402 dn = str(res[i]["dn"])
1405 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1408 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1410 m.dn = ldb.Dn(1, names.domaindn)
1413 m.dn = ldb.Dn(1, names.configdn)
1416 m.dn = ldb.Dn(1, names.schemadn)
1419 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1420 if provision_backend.slapd.poll() is None:
1422 if hasattr(provision_backend.slapd, "terminate"):
1423 provision_backend.slapd.terminate()
1425 # Older python versions don't have .terminate()
1427 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1429 #and now wait for it to die
1430 provision_backend.slapd.communicate()
1432 # now display slapd_command_file.txt to show how slapd must be started next time
1433 message("Use later the following commandline to start slapd, then Samba:")
1434 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1435 message(slapd_command)
1436 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1438 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1439 "SLAPD_COMMAND" : slapd_command})
1442 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1445 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1447 message("Once the above files are installed, your Samba4 server will be ready to use")
1448 message("Server Role: %s" % serverrole)
1449 message("Hostname: %s" % names.hostname)
1450 message("NetBIOS Domain: %s" % names.domain)
1451 message("DNS Domain: %s" % names.dnsdomain)
1452 message("DOMAIN SID: %s" % str(domainsid))
1453 if samdb_fill == FILL_FULL:
1454 message("Admin password: %s" % adminpass)
1455 if provision_backend:
1456 if provision_backend.credentials.get_bind_dn() is not None:
1457 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1459 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1461 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1463 result = ProvisionResult()
1464 result.domaindn = domaindn
1465 result.paths = paths
1467 result.samdb = samdb
1472 def provision_become_dc(setup_dir=None,
1473 smbconf=None, targetdir=None, realm=None,
1474 rootdn=None, domaindn=None, schemadn=None,
1475 configdn=None, serverdn=None,
1476 domain=None, hostname=None, domainsid=None,
1477 adminpass=None, krbtgtpass=None, domainguid=None,
1478 policyguid=None, policyguid_dc=None, invocationid=None,
1480 dnspass=None, root=None, nobody=None, users=None,
1481 wheel=None, backup=None, serverrole=None,
1482 ldap_backend=None, ldap_backend_type=None,
1483 sitename=None, debuglevel=1):
1486 """print a message if quiet is not set."""
1489 glue.set_debug_level(debuglevel)
1491 return provision(setup_dir, message, system_session(), None,
1492 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1493 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1494 configdn=configdn, serverdn=serverdn, domain=domain,
1495 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1496 machinepass=machinepass, serverrole="domain controller",
1500 def setup_db_config(setup_path, dbdir):
1501 """Setup a Berkeley database.
1503 :param setup_path: Setup path function.
1504 :param dbdir: Database directory."""
1505 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1506 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1507 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1508 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1510 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1511 {"LDAPDBDIR": dbdir})
1513 class ProvisionBackend(object):
1514 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1515 names=None, message=None,
1516 hostname=None, root=None,
1517 schema=None, ldapadminpass=None,
1518 ldap_backend_type=None, ldap_backend_extra_port=None,
1520 setup_ds_path=None, slapd_path=None,
1521 nosync=False, ldap_dryrun_mode=False):
1522 """Provision an LDAP backend for samba4
1524 This works for OpenLDAP and Fedora DS
1527 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1529 if not os.path.isdir(paths.ldapdir):
1530 os.makedirs(paths.ldapdir, 0700)
1532 if ldap_backend_type == "existing":
1533 #Check to see that this 'existing' LDAP backend in fact exists
1534 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1535 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1536 expression="(objectClass=OpenLDAProotDSE)")
1538 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1539 # This caused them to be set into the long-term database later in the script.
1540 self.credentials = credentials
1541 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1544 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1545 # if another instance of slapd is already running
1547 ldapi_db = Ldb(self.ldapi_uri)
1548 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1549 expression="(objectClass=OpenLDAProotDSE)");
1551 f = open(paths.slapdpid, "r")
1554 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1558 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1563 # Try to print helpful messages when the user has not specified the path to slapd
1564 if slapd_path is None:
1565 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1566 if not os.path.exists(slapd_path):
1567 message (slapd_path)
1568 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1570 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1572 os.unlink(schemadb_path)
1577 # Put the LDIF of the schema into a database so we can search on
1578 # it to generate schema-dependent configurations in Fedora DS and
1580 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1581 schema.ldb.connect(schemadb_path)
1582 schema.ldb.transaction_start()
1584 # These bits of LDIF are supplied when the Schema object is created
1585 schema.ldb.add_ldif(schema.schema_dn_add)
1586 schema.ldb.modify_ldif(schema.schema_dn_modify)
1587 schema.ldb.add_ldif(schema.schema_data)
1588 schema.ldb.transaction_commit()
1590 self.credentials = Credentials()
1591 self.credentials.guess(lp)
1592 #Kerberos to an ldapi:// backend makes no sense
1593 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1595 self.adminCredentials = Credentials()
1596 self.adminCredentials.guess(lp)
1597 #Kerberos to an ldapi:// backend makes no sense
1598 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1600 self.ldap_backend_type = ldap_backend_type
1602 if ldap_backend_type == "fedora-ds":
1603 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1604 names=names, message=message,
1606 ldapadminpass=ldapadminpass, root=root,
1608 ldap_backend_extra_port=ldap_backend_extra_port,
1609 setup_ds_path=setup_ds_path,
1610 slapd_path=slapd_path,
1612 ldap_dryrun_mode=ldap_dryrun_mode)
1614 elif ldap_backend_type == "openldap":
1615 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1616 names=names, message=message,
1618 ldapadminpass=ldapadminpass, root=root,
1620 ldap_backend_extra_port=ldap_backend_extra_port,
1621 ol_mmr_urls=ol_mmr_urls,
1622 slapd_path=slapd_path,
1624 ldap_dryrun_mode=ldap_dryrun_mode)
1626 raise ProvisioningError("Unknown LDAP backend type selected")
1628 self.credentials.set_password(ldapadminpass)
1629 self.adminCredentials.set_username("samba-admin")
1630 self.adminCredentials.set_password(ldapadminpass)
1632 # Now start the slapd, so we can provision onto it. We keep the
1633 # subprocess context around, to kill this off at the successful
1635 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1637 while self.slapd.poll() is None:
1638 # Wait until the socket appears
1640 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1641 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1642 expression="(objectClass=OpenLDAProotDSE)")
1643 # If we have got here, then we must have a valid connection to the LDAP server!
1649 raise ProvisioningError("slapd died before we could make a connection to it")
1652 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1654 hostname=None, ldapadminpass=None, root=None,
1656 ldap_backend_extra_port=None,
1658 slapd_path=None, nosync=False,
1659 ldap_dryrun_mode=False):
1661 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1664 nosync_config = "dbnosync"
1666 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1667 refint_attributes = ""
1668 memberof_config = "# Generated from Samba4 schema\n"
1669 for att in lnkattr.keys():
1670 if lnkattr[att] is not None:
1671 refint_attributes = refint_attributes + " " + att
1673 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1674 { "MEMBER_ATTR" : att ,
1675 "MEMBEROF_ATTR" : lnkattr[att] })
1677 refint_config = read_and_sub_file(setup_path("refint.conf"),
1678 { "LINK_ATTRS" : refint_attributes})
1680 attrs = ["linkID", "lDAPDisplayName"]
1681 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1683 for i in range (0, len(res)):
1684 index_attr = res[i]["lDAPDisplayName"][0]
1685 if index_attr == "objectGUID":
1686 index_attr = "entryUUID"
1688 index_config += "index " + index_attr + " eq\n"
1690 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1692 mmr_replicator_acl = ""
1693 mmr_serverids_config = ""
1694 mmr_syncrepl_schema_config = ""
1695 mmr_syncrepl_config_config = ""
1696 mmr_syncrepl_user_config = ""
1699 if ol_mmr_urls is not None:
1700 # For now, make these equal
1701 mmr_pass = ldapadminpass
1703 url_list=filter(None,ol_mmr_urls.split(' '))
1704 if (len(url_list) == 1):
1705 url_list=filter(None,ol_mmr_urls.split(','))
1708 mmr_on_config = "MirrorMode On"
1709 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1711 for url in url_list:
1713 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1714 { "SERVERID" : str(serverid),
1715 "LDAPSERVER" : url })
1718 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1720 "MMRDN": names.schemadn,
1722 "MMR_PASSWORD": mmr_pass})
1725 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1727 "MMRDN": names.configdn,
1729 "MMR_PASSWORD": mmr_pass})
1732 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1734 "MMRDN": names.domaindn,
1736 "MMR_PASSWORD": mmr_pass })
1737 # OpenLDAP cn=config initialisation
1738 olc_syncrepl_config = ""
1740 # if mmr = yes, generate cn=config-replication directives
1741 # and olc_seed.lif for the other mmr-servers
1742 if ol_mmr_urls is not None:
1744 olc_serverids_config = ""
1745 olc_syncrepl_seed_config = ""
1746 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1748 for url in url_list:
1750 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1751 { "SERVERID" : str(serverid),
1752 "LDAPSERVER" : url })
1755 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1758 "MMR_PASSWORD": mmr_pass})
1760 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1762 "LDAPSERVER" : url})
1764 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1765 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1766 "OLC_PW": ldapadminpass,
1767 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1770 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1771 {"DNSDOMAIN": names.dnsdomain,
1772 "LDAPDIR": paths.ldapdir,
1773 "DOMAINDN": names.domaindn,
1774 "CONFIGDN": names.configdn,
1775 "SCHEMADN": names.schemadn,
1776 "MEMBEROF_CONFIG": memberof_config,
1777 "MIRRORMODE": mmr_on_config,
1778 "REPLICATOR_ACL": mmr_replicator_acl,
1779 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1780 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1781 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1782 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1783 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1784 "OLC_MMR_CONFIG": olc_mmr_config,
1785 "REFINT_CONFIG": refint_config,
1786 "INDEX_CONFIG": index_config,
1787 "NOSYNC": nosync_config})
1789 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1790 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1791 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1793 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1794 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1796 setup_file(setup_path("cn=samba.ldif"),
1797 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1798 { "UUID": str(uuid.uuid4()),
1799 "LDAPTIME": timestring(int(time.time()))} )
1800 setup_file(setup_path("cn=samba-admin.ldif"),
1801 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1802 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1803 "UUID": str(uuid.uuid4()),
1804 "LDAPTIME": timestring(int(time.time()))} )
1806 if ol_mmr_urls is not None:
1807 setup_file(setup_path("cn=replicator.ldif"),
1808 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1809 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1810 "UUID": str(uuid.uuid4()),
1811 "LDAPTIME": timestring(int(time.time()))} )
1814 mapping = "schema-map-openldap-2.3"
1815 backend_schema = "backend-schema.schema"
1817 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1818 assert backend_schema_data is not None
1819 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1821 # now we generate the needed strings to start slapd automatically,
1822 # first ldapi_uri...
1823 if ldap_backend_extra_port is not None:
1824 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1825 # specified there as part of it's clue as to it's own name,
1826 # and not to replicate to itself
1827 if ol_mmr_urls is None:
1828 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1830 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1832 server_port_string = ""
1834 # Prepare the 'result' information - the commands to return in particular
1835 result.slapd_provision_command = [slapd_path]
1837 result.slapd_provision_command.append("-F" + paths.olcdir)
1839 result.slapd_provision_command.append("-h")
1841 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1842 result.slapd_command = list(result.slapd_provision_command)
1844 result.slapd_provision_command.append(result.ldapi_uri)
1845 result.slapd_provision_command.append("-d0")
1847 uris = result.ldapi_uri
1848 if server_port_string is not "":
1849 uris = uris + " " + server_port_string
1851 result.slapd_command.append(uris)
1853 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1854 result.credentials.set_username("samba-admin")
1856 # If we were just looking for crashes up to this point, it's a
1857 # good time to exit before we realise we don't have OpenLDAP on
1859 if ldap_dryrun_mode:
1862 # Finally, convert the configuration into cn=config style!
1863 if not os.path.isdir(paths.olcdir):
1864 os.makedirs(paths.olcdir, 0770)
1866 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1868 # We can't do this, as OpenLDAP is strange. It gives an error
1869 # output to the above, but does the conversion sucessfully...
1872 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1874 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1875 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1877 # Don't confuse the admin by leaving the slapd.conf around
1878 os.remove(paths.slapdconf)
1881 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1883 hostname=None, ldapadminpass=None, root=None,
1885 ldap_backend_extra_port=None,
1889 ldap_dryrun_mode=False):
1891 if ldap_backend_extra_port is not None:
1892 serverport = "ServerPort=%d" % ldap_backend_extra_port
1896 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1898 "HOSTNAME": hostname,
1899 "DNSDOMAIN": names.dnsdomain,
1900 "LDAPDIR": paths.ldapdir,
1901 "DOMAINDN": names.domaindn,
1902 "LDAPMANAGERDN": names.ldapmanagerdn,
1903 "LDAPMANAGERPASS": ldapadminpass,
1904 "SERVERPORT": serverport})
1906 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1907 {"CONFIGDN": names.configdn,
1908 "SCHEMADN": names.schemadn,
1909 "SAMBADN": names.sambadn,
1912 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1913 {"SAMBADN": names.sambadn,
1916 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1917 {"SAMBADN": names.sambadn,
1918 "LDAPADMINPASS": ldapadminpass
1921 mapping = "schema-map-fedora-ds-1.0"
1922 backend_schema = "99_ad.ldif"
1924 # Build a schema file in Fedora DS format
1925 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1926 assert backend_schema_data is not None
1927 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1929 result.credentials.set_bind_dn(names.ldapmanagerdn)
1931 # Destory the target directory, or else setup-ds.pl will complain
1932 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1933 shutil.rmtree(fedora_ds_dir, True)
1935 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1936 #In the 'provision' command line, stay in the foreground so we can easily kill it
1937 result.slapd_provision_command.append("-d0")
1939 #the command for the final run is the normal script
1940 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1942 # If we were just looking for crashes up to this point, it's a
1943 # good time to exit before we realise we don't have Fedora DS on
1944 if ldap_dryrun_mode:
1947 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1948 if setup_ds_path is None:
1949 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\"!")
1950 if not os.path.exists(setup_ds_path):
1951 message (setup_ds_path)
1952 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1954 # Run the Fedora DS setup utility
1955 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1957 raise ProvisioningError("setup-ds failed")
1960 retcode = subprocess.call([
1961 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1962 close_fds=True, shell=False)
1964 raise("ldib2db failed")
1966 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1967 """Create a PHP LDAP admin configuration file.
1969 :param path: Path to write the configuration to.
1970 :param setup_path: Function to generate setup paths.
1972 setup_file(setup_path("phpldapadmin-config.php"), path,
1973 {"S4_LDAPI_URI": ldapi_uri})
1976 def create_zone_file(path, setup_path, dnsdomain,
1977 hostip, hostip6, hostname, realm, domainguid,
1979 """Write out a DNS zone file, from the info in the current database.
1981 :param path: Path of the new zone file.
1982 :param setup_path: Setup path function.
1983 :param dnsdomain: DNS Domain name
1984 :param domaindn: DN of the Domain
1985 :param hostip: Local IPv4 IP
1986 :param hostip6: Local IPv6 IP
1987 :param hostname: Local hostname
1988 :param realm: Realm name
1989 :param domainguid: GUID of the domain.
1990 :param ntdsguid: GUID of the hosts nTDSDSA record.
1992 assert isinstance(domainguid, str)
1994 if hostip6 is not None:
1995 hostip6_base_line = " IN AAAA " + hostip6
1996 hostip6_host_line = hostname + " IN AAAA " + hostip6
1998 hostip6_base_line = ""
1999 hostip6_host_line = ""
2001 if hostip is not None:
2002 hostip_base_line = " IN A " + hostip
2003 hostip_host_line = hostname + " IN A " + hostip
2005 hostip_base_line = ""
2006 hostip_host_line = ""
2008 setup_file(setup_path("provision.zone"), path, {
2009 "HOSTNAME": hostname,
2010 "DNSDOMAIN": dnsdomain,
2012 "HOSTIP_BASE_LINE": hostip_base_line,
2013 "HOSTIP_HOST_LINE": hostip_host_line,
2014 "DOMAINGUID": domainguid,
2015 "DATESTRING": time.strftime("%Y%m%d%H"),
2016 "DEFAULTSITE": DEFAULTSITE,
2017 "NTDSGUID": ntdsguid,
2018 "HOSTIP6_BASE_LINE": hostip6_base_line,
2019 "HOSTIP6_HOST_LINE": hostip6_host_line,
2023 def create_named_conf(path, setup_path, realm, dnsdomain,
2025 """Write out a file containing zone statements suitable for inclusion in a
2026 named.conf file (including GSS-TSIG configuration).
2028 :param path: Path of the new named.conf file.
2029 :param setup_path: Setup path function.
2030 :param realm: Realm name
2031 :param dnsdomain: DNS Domain name
2032 :param private_dir: Path to private directory
2033 :param keytab_name: File name of DNS keytab file
2036 setup_file(setup_path("named.conf"), path, {
2037 "DNSDOMAIN": dnsdomain,
2039 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2040 "PRIVATE_DIR": private_dir
2043 def create_named_txt(path, setup_path, realm, dnsdomain,
2044 private_dir, keytab_name):
2045 """Write out a file containing zone statements suitable for inclusion in a
2046 named.conf file (including GSS-TSIG configuration).
2048 :param path: Path of the new named.conf file.
2049 :param setup_path: Setup path function.
2050 :param realm: Realm name
2051 :param dnsdomain: DNS Domain name
2052 :param private_dir: Path to private directory
2053 :param keytab_name: File name of DNS keytab file
2056 setup_file(setup_path("named.txt"), path, {
2057 "DNSDOMAIN": dnsdomain,
2059 "DNS_KEYTAB": keytab_name,
2060 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2061 "PRIVATE_DIR": private_dir
2064 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2065 """Write out a file containing zone statements suitable for inclusion in a
2066 named.conf file (including GSS-TSIG configuration).
2068 :param path: Path of the new named.conf file.
2069 :param setup_path: Setup path function.
2070 :param dnsdomain: DNS Domain name
2071 :param hostname: Local hostname
2072 :param realm: Realm name
2075 setup_file(setup_path("krb5.conf"), path, {
2076 "DNSDOMAIN": dnsdomain,
2077 "HOSTNAME": hostname,