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)
213 # We don't actually add this ldif, just parse it
214 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
215 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
218 # Return a hash with the forward attribute as a key and the back as the value
219 def get_linked_attributes(schemadn,schemaldb):
220 attrs = ["linkID", "lDAPDisplayName"]
221 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)
223 for i in range (0, len(res)):
224 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
225 target = schemaldb.searchone(basedn=schemadn,
226 expression=expression,
227 attribute="lDAPDisplayName",
229 if target is not None:
230 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
234 def get_dnsyntax_attributes(schemadn,schemaldb):
235 attrs = ["linkID", "lDAPDisplayName"]
236 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
238 for i in range (0, len(res)):
239 attributes.append(str(res[i]["lDAPDisplayName"]))
244 def check_install(lp, session_info, credentials):
245 """Check whether the current install seems ok.
247 :param lp: Loadparm context
248 :param session_info: Session information
249 :param credentials: Credentials
251 if lp.get("realm") == "":
252 raise Exception("Realm empty")
253 ldb = Ldb(lp.get("sam database"), session_info=session_info,
254 credentials=credentials, lp=lp)
255 if len(ldb.search("(cn=Administrator)")) != 1:
256 raise ProvisioningError("No administrator account found")
259 def findnss(nssfn, names):
260 """Find a user or group from a list of possibilities.
262 :param nssfn: NSS Function to try (should raise KeyError if not found)
263 :param names: Names to check.
264 :return: Value return by first names list.
271 raise KeyError("Unable to find user/group %r" % names)
274 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
275 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
278 def read_and_sub_file(file, subst_vars):
279 """Read a file and sub in variables found in it
281 :param file: File to be read (typically from setup directory)
282 param subst_vars: Optional variables to subsitute in the file.
284 data = open(file, 'r').read()
285 if subst_vars is not None:
286 data = substitute_var(data, subst_vars)
287 check_all_substituted(data)
291 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
292 """Setup a ldb in the private dir.
294 :param ldb: LDB file to import data into
295 :param ldif_path: Path of the LDIF file to load
296 :param subst_vars: Optional variables to subsitute in LDIF.
298 assert isinstance(ldif_path, str)
300 data = read_and_sub_file(ldif_path, subst_vars)
304 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
305 """Modify a ldb in the private dir.
307 :param ldb: LDB object.
308 :param ldif_path: LDIF file path.
309 :param subst_vars: Optional dictionary with substitution variables.
311 data = read_and_sub_file(ldif_path, subst_vars)
313 ldb.modify_ldif(data)
316 def setup_ldb(ldb, ldif_path, subst_vars):
317 """Import a LDIF a file into a LDB handle, optionally substituting variables.
319 :note: Either all LDIF data will be added or none (using transactions).
321 :param ldb: LDB file to import into.
322 :param ldif_path: Path to the LDIF file.
323 :param subst_vars: Dictionary with substitution variables.
325 assert ldb is not None
326 ldb.transaction_start()
328 setup_add_ldif(ldb, ldif_path, subst_vars)
330 ldb.transaction_cancel()
332 ldb.transaction_commit()
335 def setup_file(template, fname, subst_vars):
336 """Setup a file in the private dir.
338 :param template: Path of the template file.
339 :param fname: Path of the file to create.
340 :param subst_vars: Substitution variables.
344 if os.path.exists(f):
347 data = read_and_sub_file(template, subst_vars)
348 open(f, 'w').write(data)
351 def provision_paths_from_lp(lp, dnsdomain):
352 """Set the default paths for provisioning.
354 :param lp: Loadparm context.
355 :param dnsdomain: DNS Domain name
357 paths = ProvisionPaths()
358 paths.private_dir = lp.get("private dir")
359 paths.dns_keytab = "dns.keytab"
361 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
362 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
363 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
364 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
365 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
366 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
367 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
368 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
369 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
370 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
371 paths.phpldapadminconfig = os.path.join(paths.private_dir,
372 "phpldapadmin-config.php")
373 paths.ldapdir = os.path.join(paths.private_dir,
375 paths.slapdconf = os.path.join(paths.ldapdir,
377 paths.slapdpid = os.path.join(paths.ldapdir,
379 paths.modulesconf = os.path.join(paths.ldapdir,
381 paths.memberofconf = os.path.join(paths.ldapdir,
383 paths.fedoradsinf = os.path.join(paths.ldapdir,
385 paths.fedoradspartitions = os.path.join(paths.ldapdir,
386 "fedorads-partitions.ldif")
387 paths.fedoradssasl = os.path.join(paths.ldapdir,
388 "fedorads-sasl.ldif")
389 paths.fedoradssamba = os.path.join(paths.ldapdir,
390 "fedorads-samba.ldif")
391 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
392 "mmr_serverids.conf")
393 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
395 paths.olcdir = os.path.join(paths.ldapdir,
397 paths.olcseedldif = os.path.join(paths.ldapdir,
399 paths.hklm = "hklm.ldb"
400 paths.hkcr = "hkcr.ldb"
401 paths.hkcu = "hkcu.ldb"
402 paths.hku = "hku.ldb"
403 paths.hkpd = "hkpd.ldb"
404 paths.hkpt = "hkpt.ldb"
406 paths.sysvol = lp.get("path", "sysvol")
408 paths.netlogon = lp.get("path", "netlogon")
410 paths.smbconf = lp.configfile
415 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
416 serverrole=None, rootdn=None, domaindn=None, configdn=None,
417 schemadn=None, serverdn=None, sitename=None, sambadn=None):
418 """Guess configuration settings to use."""
421 hostname = socket.gethostname().split(".")[0].lower()
423 netbiosname = hostname.upper()
424 if not valid_netbios_name(netbiosname):
425 raise InvalidNetbiosName(netbiosname)
427 hostname = hostname.lower()
429 if dnsdomain is None:
430 dnsdomain = lp.get("realm")
432 if serverrole is None:
433 serverrole = lp.get("server role")
435 assert dnsdomain is not None
436 realm = dnsdomain.upper()
438 if lp.get("realm").upper() != realm:
439 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
440 (lp.get("realm"), lp.configfile, realm))
442 dnsdomain = dnsdomain.lower()
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 = "CN=" + netbiosname
457 assert domain is not None
458 domain = domain.upper()
459 if not valid_netbios_name(domain):
460 raise InvalidNetbiosName(domain)
462 if netbiosname.upper() == realm.upper():
463 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
465 if hostname.upper() == realm.upper():
466 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
468 if domain.upper() == realm.upper():
469 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
475 configdn = "CN=Configuration," + rootdn
477 schemadn = "CN=Schema," + configdn
484 names = ProvisionNames()
485 names.rootdn = rootdn
486 names.domaindn = domaindn
487 names.configdn = configdn
488 names.schemadn = schemadn
489 names.sambadn = sambadn
490 names.ldapmanagerdn = "CN=Manager," + rootdn
491 names.dnsdomain = dnsdomain
492 names.domain = domain
494 names.netbiosname = netbiosname
495 names.hostname = hostname
496 names.sitename = sitename
497 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
502 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
504 """Create a new smb.conf file based on a couple of basic settings.
506 assert smbconf is not None
508 hostname = socket.gethostname().split(".")[0].lower()
510 if serverrole is None:
511 serverrole = "standalone"
513 assert serverrole in ("domain controller", "member server", "standalone")
514 if serverrole == "domain controller":
516 elif serverrole == "member server":
517 smbconfsuffix = "member"
518 elif serverrole == "standalone":
519 smbconfsuffix = "standalone"
521 assert domain is not None
522 assert realm is not None
524 default_lp = param.LoadParm()
525 #Load non-existant file
526 if os.path.exists(smbconf):
527 default_lp.load(smbconf)
529 if targetdir is not None:
530 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
531 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
533 default_lp.set("lock dir", os.path.abspath(targetdir))
538 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
539 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
541 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
543 "HOSTNAME": hostname,
546 "SERVERROLE": serverrole,
547 "NETLOGONPATH": netlogon,
548 "SYSVOLPATH": sysvol,
549 "PRIVATEDIR_LINE": privatedir_line,
550 "LOCKDIR_LINE": lockdir_line
554 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
555 users_gid, wheel_gid):
556 """setup reasonable name mappings for sam names to unix names.
558 :param samdb: SamDB object.
559 :param idmap: IDmap db object.
560 :param sid: The domain sid.
561 :param domaindn: The domain DN.
562 :param root_uid: uid of the UNIX root user.
563 :param nobody_uid: uid of the UNIX nobody user.
564 :param users_gid: gid of the UNIX users group.
565 :param wheel_gid: gid of the UNIX wheel group."""
567 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
568 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
570 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
571 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
573 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
575 serverrole, ldap_backend=None,
577 """Setup the partitions for the SAM database.
579 Alternatively, provision() may call this, and then populate the database.
581 :note: This will wipe the Sam Database!
583 :note: This function always removes the local SAM LDB file. The erase
584 parameter controls whether to erase the existing data, which
585 may not be stored locally but in LDAP.
587 assert session_info is not None
589 # We use options=["modules:"] to stop the modules loading - we
590 # just want to wipe and re-initialise the database, not start it up
593 samdb = Ldb(url=samdb_path, session_info=session_info,
594 credentials=credentials, lp=lp, options=["modules:"])
596 samdb.erase_except_schema_controlled()
598 os.unlink(samdb_path)
599 samdb = Ldb(url=samdb_path, session_info=session_info,
600 credentials=credentials, lp=lp, options=["modules:"])
602 samdb.erase_except_schema_controlled()
605 #Add modules to the list to activate them by default
606 #beware often order is important
608 # Some Known ordering constraints:
609 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
610 # - objectclass must be before password_hash, because password_hash checks
611 # that the objectclass is of type person (filled in by objectclass
612 # module when expanding the objectclass list)
613 # - partition must be last
614 # - each partition has its own module list then
615 modules_list = ["resolve_oids",
636 "extended_dn_out_ldb"]
637 modules_list2 = ["show_deleted",
640 domaindn_ldb = "users.ldb"
641 configdn_ldb = "configuration.ldb"
642 schemadn_ldb = "schema.ldb"
643 if ldap_backend is not None:
644 domaindn_ldb = ldap_backend.ldapi_uri
645 configdn_ldb = ldap_backend.ldapi_uri
646 schemadn_ldb = ldap_backend.ldapi_uri
648 if ldap_backend.ldap_backend_type == "fedora-ds":
649 backend_modules = ["nsuniqueid", "paged_searches"]
650 # We can handle linked attributes here, as we don't have directory-side subtree operations
651 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
652 elif ldap_backend.ldap_backend_type == "openldap":
653 backend_modules = ["entryuuid", "paged_searches"]
654 # OpenLDAP handles subtree renames, so we don't want to do any of these things
655 tdb_modules_list = ["extended_dn_out_dereference"]
657 elif serverrole == "domain controller":
658 tdb_modules_list.insert(0, "repl_meta_data")
661 backend_modules = ["objectguid"]
663 if tdb_modules_list is None:
664 tdb_modules_list_as_string = ""
666 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
668 samdb.transaction_start()
670 message("Setting up sam.ldb partitions and settings")
671 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
672 "SCHEMADN": names.schemadn,
673 "SCHEMADN_LDB": schemadn_ldb,
674 "SCHEMADN_MOD2": ",objectguid",
675 "CONFIGDN": names.configdn,
676 "CONFIGDN_LDB": configdn_ldb,
677 "DOMAINDN": names.domaindn,
678 "DOMAINDN_LDB": domaindn_ldb,
679 "SCHEMADN_MOD": "schema_fsmo,instancetype",
680 "CONFIGDN_MOD": "naming_fsmo,instancetype",
681 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
682 "MODULES_LIST": ",".join(modules_list),
683 "TDB_MODULES_LIST": tdb_modules_list_as_string,
684 "MODULES_LIST2": ",".join(modules_list2),
685 "BACKEND_MOD": ",".join(backend_modules),
688 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
690 message("Setting up sam.ldb rootDSE")
691 setup_samdb_rootdse(samdb, setup_path, names)
694 samdb.transaction_cancel()
697 samdb.transaction_commit()
699 def secretsdb_self_join(secretsdb, domain,
700 netbiosname, domainsid, machinepass,
701 realm=None, dnsdomain=None,
703 key_version_number=1,
704 secure_channel_type=SEC_CHAN_WKSTA):
705 """Add domain join-specific bits to a secrets database.
707 :param secretsdb: Ldb Handle to the secrets database
708 :param machinepass: Machine password
710 attrs=["whenChanged",
718 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
719 msg["secureChannelType"] = str(secure_channel_type)
720 msg["flatname"] = [domain]
721 msg["objectClass"] = ["top", "primaryDomain"]
722 if realm is not None:
723 if dnsdomain is None:
724 dnsdomain = realm.lower()
725 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
727 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
728 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
729 msg["privateKeytab"] = ["secrets.keytab"];
732 msg["secret"] = [machinepass]
733 msg["samAccountName"] = ["%s$" % netbiosname]
734 msg["secureChannelType"] = [str(secure_channel_type)]
735 msg["objectSid"] = [ndr_pack(domainsid)]
737 res = secretsdb.search(base="cn=Primary Domains",
739 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
740 scope=SCOPE_ONELEVEL)
743 if del_msg.dn is not msg.dn:
744 secretsdb.delete(del_msg.dn)
746 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
749 msg["priorSecret"] = res[0]["secret"]
750 msg["priorWhenChanged"] = res[0]["whenChanged"]
752 if res["privateKeytab"] is not None:
753 msg["privateKeytab"] = res[0]["privateKeytab"]
755 if res["krb5Keytab"] is not None:
756 msg["krb5Keytab"] = res[0]["krb5Keytab"]
759 el.set_flags(ldb.FLAG_MOD_REPLACE)
760 secretsdb.modify(msg)
765 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
766 dns_keytab_path, dnspass):
767 """Add DNS specific bits to a secrets database.
769 :param secretsdb: Ldb Handle to the secrets database
770 :param setup_path: Setup path function
771 :param machinepass: Machine password
773 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
775 "DNSDOMAIN": dnsdomain,
776 "DNS_KEYTAB": dns_keytab_path,
777 "DNSPASS_B64": b64encode(dnspass),
781 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
782 """Setup the secrets database.
784 :param path: Path to the secrets database.
785 :param setup_path: Get the path to a setup file.
786 :param session_info: Session info.
787 :param credentials: Credentials
788 :param lp: Loadparm context
789 :return: LDB handle for the created secrets database
791 if os.path.exists(path):
793 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
796 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
797 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
799 secrets_ldb.transaction_start()
800 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
802 if credentials is not None and credentials.authentication_requested():
803 if credentials.get_bind_dn() is not None:
804 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
805 "LDAPMANAGERDN": credentials.get_bind_dn(),
806 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
809 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
810 "LDAPADMINUSER": credentials.get_username(),
811 "LDAPADMINREALM": credentials.get_realm(),
812 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
817 def setup_registry(path, setup_path, session_info, lp):
818 """Setup the registry.
820 :param path: Path to the registry database
821 :param setup_path: Function that returns the path to a setup.
822 :param session_info: Session information
823 :param credentials: Credentials
824 :param lp: Loadparm context
826 reg = registry.Registry()
827 hive = registry.open_ldb(path, session_info=session_info,
829 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
830 provision_reg = setup_path("provision.reg")
831 assert os.path.exists(provision_reg)
832 reg.diff_apply(provision_reg)
835 def setup_idmapdb(path, setup_path, session_info, lp):
836 """Setup the idmap database.
838 :param path: path to the idmap database
839 :param setup_path: Function that returns a path to a setup file
840 :param session_info: Session information
841 :param credentials: Credentials
842 :param lp: Loadparm context
844 if os.path.exists(path):
847 idmap_ldb = IDmapDB(path, session_info=session_info,
851 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
855 def setup_samdb_rootdse(samdb, setup_path, names):
856 """Setup the SamDB rootdse.
858 :param samdb: Sam Database handle
859 :param setup_path: Obtain setup path
861 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
862 "SCHEMADN": names.schemadn,
863 "NETBIOSNAME": names.netbiosname,
864 "DNSDOMAIN": names.dnsdomain,
865 "REALM": names.realm,
866 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
867 "DOMAINDN": names.domaindn,
868 "ROOTDN": names.rootdn,
869 "CONFIGDN": names.configdn,
870 "SERVERDN": names.serverdn,
874 def setup_self_join(samdb, names,
875 machinepass, dnspass,
876 domainsid, invocationid, setup_path,
877 policyguid, policyguid_dc, domainControllerFunctionality):
878 """Join a host to its own domain."""
879 assert isinstance(invocationid, str)
880 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
881 "CONFIGDN": names.configdn,
882 "SCHEMADN": names.schemadn,
883 "DOMAINDN": names.domaindn,
884 "SERVERDN": names.serverdn,
885 "INVOCATIONID": invocationid,
886 "NETBIOSNAME": names.netbiosname,
887 "DEFAULTSITE": names.sitename,
888 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
889 "MACHINEPASS_B64": b64encode(machinepass),
890 "DNSPASS_B64": b64encode(dnspass),
891 "REALM": names.realm,
892 "DOMAIN": names.domain,
893 "DNSDOMAIN": names.dnsdomain,
894 "SAMBA_VERSION_STRING": version,
895 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
897 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
898 "POLICYGUID": policyguid,
899 "POLICYGUID_DC": policyguid_dc,
900 "DNSDOMAIN": names.dnsdomain,
901 "DOMAINSID": str(domainsid),
902 "DOMAINDN": names.domaindn})
904 # add the NTDSGUID based SPNs
905 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
906 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
907 expression="", scope=SCOPE_BASE)
908 assert isinstance(names.ntdsguid, str)
910 # Setup fSMORoleOwner entries to point at the newly created DC entry
911 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
912 "DOMAIN": names.domain,
913 "DNSDOMAIN": names.dnsdomain,
914 "DOMAINDN": names.domaindn,
915 "CONFIGDN": names.configdn,
916 "SCHEMADN": names.schemadn,
917 "DEFAULTSITE": names.sitename,
918 "SERVERDN": names.serverdn,
919 "NETBIOSNAME": names.netbiosname,
920 "NTDSGUID": names.ntdsguid
924 def setup_samdb(path, setup_path, session_info, credentials, lp,
926 domainsid, domainguid, policyguid, policyguid_dc,
927 fill, adminpass, krbtgtpass,
928 machinepass, invocationid, dnspass,
929 serverrole, dom_for_fun_level=None,
930 schema=None, ldap_backend=None):
931 """Setup a complete SAM Database.
933 :note: This will wipe the main SAM database file!
936 # ATTENTION: Do NOT change these default values without discussion with the
937 # team and/or release manager. They have a big impact on the whole program!
938 domainControllerFunctionality = DS_DC_FUNCTION_2008
940 if dom_for_fun_level is None:
941 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
942 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
943 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
945 if dom_for_fun_level > domainControllerFunctionality:
946 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!")
948 domainFunctionality = dom_for_fun_level
949 forestFunctionality = dom_for_fun_level
951 # Also wipes the database
952 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
953 credentials=credentials, session_info=session_info,
954 names=names, ldap_backend=ldap_backend,
955 serverrole=serverrole)
958 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
959 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
961 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
962 samdb = Ldb(session_info=session_info,
963 credentials=credentials, lp=lp)
965 message("Pre-loading the Samba 4 and AD schema")
967 # Load the schema from the one we computed earlier
968 samdb.set_schema_from_ldb(schema.ldb)
970 # And now we can connect to the DB - the schema won't be loaded from the DB
974 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
979 samdb.transaction_start()
981 message("Erasing data from partitions")
982 # Load the schema (again). This time it will force a reindex,
983 # and will therefore make the erase_partitions() below
984 # computationally sane
985 samdb.set_schema_from_ldb(schema.ldb)
986 samdb.erase_partitions()
988 # Set the domain functionality levels onto the database.
989 # Various module (the password_hash module in particular) need
990 # to know what level of AD we are emulating.
992 # These will be fixed into the database via the database
993 # modifictions below, but we need them set from the start.
994 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
995 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
996 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
998 samdb.set_domain_sid(str(domainsid))
999 if serverrole == "domain controller":
1000 samdb.set_invocation_id(invocationid)
1002 message("Adding DomainDN: %s" % names.domaindn)
1003 if serverrole == "domain controller":
1004 domain_oc = "domainDNS"
1006 domain_oc = "samba4LocalDomain"
1008 #impersonate domain admin
1009 admin_session_info = admin_session(lp, str(domainsid))
1010 samdb.set_session_info(admin_session_info)
1012 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1013 "DOMAINDN": names.domaindn,
1014 "DOMAIN_OC": domain_oc
1017 message("Modifying DomainDN: " + names.domaindn + "")
1018 if domainguid is not None:
1019 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
1023 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1024 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1025 "DOMAINSID": str(domainsid),
1026 "SCHEMADN": names.schemadn,
1027 "NETBIOSNAME": names.netbiosname,
1028 "DEFAULTSITE": names.sitename,
1029 "CONFIGDN": names.configdn,
1030 "SERVERDN": names.serverdn,
1031 "POLICYGUID": policyguid,
1032 "DOMAINDN": names.domaindn,
1033 "DOMAINGUID_MOD": domainguid_mod,
1034 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1035 "SAMBA_VERSION_STRING": version
1038 message("Adding configuration container")
1039 descr = get_config_descriptor(domainsid);
1040 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1041 "CONFIGDN": names.configdn,
1042 "DESCRIPTOR": descr,
1044 message("Modifying configuration container")
1045 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1046 "CONFIGDN": names.configdn,
1047 "SCHEMADN": names.schemadn,
1050 # The LDIF here was created when the Schema object was constructed
1051 message("Setting up sam.ldb schema")
1052 samdb.add_ldif(schema.schema_dn_add)
1053 samdb.modify_ldif(schema.schema_dn_modify)
1054 samdb.write_prefixes_from_schema()
1055 samdb.add_ldif(schema.schema_data)
1056 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1057 {"SCHEMADN": names.schemadn})
1059 message("Setting up sam.ldb configuration data")
1060 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1061 "CONFIGDN": names.configdn,
1062 "NETBIOSNAME": names.netbiosname,
1063 "DEFAULTSITE": names.sitename,
1064 "DNSDOMAIN": names.dnsdomain,
1065 "DOMAIN": names.domain,
1066 "SCHEMADN": names.schemadn,
1067 "DOMAINDN": names.domaindn,
1068 "SERVERDN": names.serverdn,
1069 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1072 message("Setting up display specifiers")
1073 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1074 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1075 check_all_substituted(display_specifiers_ldif)
1076 samdb.add_ldif(display_specifiers_ldif)
1078 message("Adding users container")
1079 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1080 "DOMAINDN": names.domaindn})
1081 message("Modifying users container")
1082 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1083 "DOMAINDN": names.domaindn})
1084 message("Adding computers container")
1085 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1086 "DOMAINDN": names.domaindn})
1087 message("Modifying computers container")
1088 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1089 "DOMAINDN": names.domaindn})
1090 message("Setting up sam.ldb data")
1091 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1092 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1093 "DOMAINDN": names.domaindn,
1094 "NETBIOSNAME": names.netbiosname,
1095 "DEFAULTSITE": names.sitename,
1096 "CONFIGDN": names.configdn,
1097 "SERVERDN": names.serverdn,
1098 "POLICYGUID_DC": policyguid_dc
1101 if fill == FILL_FULL:
1102 message("Setting up sam.ldb users and groups")
1103 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1104 "DOMAINDN": names.domaindn,
1105 "DOMAINSID": str(domainsid),
1106 "CONFIGDN": names.configdn,
1107 "ADMINPASS_B64": b64encode(adminpass),
1108 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1111 if serverrole == "domain controller":
1112 message("Setting up self join")
1113 setup_self_join(samdb, names=names, invocationid=invocationid,
1115 machinepass=machinepass,
1116 domainsid=domainsid, policyguid=policyguid,
1117 policyguid_dc=policyguid_dc,
1118 setup_path=setup_path,
1119 domainControllerFunctionality=domainControllerFunctionality)
1121 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1122 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1123 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1124 assert isinstance(names.ntdsguid, str)
1127 samdb.transaction_cancel()
1130 samdb.transaction_commit()
1135 FILL_NT4SYNC = "NT4SYNC"
1139 def provision(setup_dir, message, session_info,
1140 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1142 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1144 domain=None, hostname=None, hostip=None, hostip6=None,
1145 domainsid=None, adminpass=None, ldapadminpass=None,
1146 krbtgtpass=None, domainguid=None,
1147 policyguid=None, policyguid_dc=None, invocationid=None,
1149 dnspass=None, root=None, nobody=None, users=None,
1150 wheel=None, backup=None, aci=None, serverrole=None,
1151 dom_for_fun_level=None,
1152 ldap_backend_extra_port=None, ldap_backend_type=None,
1154 ol_mmr_urls=None, ol_olc=None,
1155 setup_ds_path=None, slapd_path=None, nosync=False,
1156 ldap_dryrun_mode=False):
1159 :note: caution, this wipes all existing data!
1162 def setup_path(file):
1163 return os.path.join(setup_dir, file)
1165 if domainsid is None:
1166 domainsid = security.random_sid()
1168 domainsid = security.dom_sid(domainsid)
1170 # create/adapt the group policy GUIDs
1171 if policyguid is None:
1172 policyguid = str(uuid.uuid4())
1173 policyguid = policyguid.upper()
1174 if policyguid_dc is None:
1175 policyguid_dc = str(uuid.uuid4())
1176 policyguid_dc = policyguid_dc.upper()
1178 if adminpass is None:
1179 adminpass = glue.generate_random_str(12)
1180 if krbtgtpass is None:
1181 krbtgtpass = glue.generate_random_str(12)
1182 if machinepass is None:
1183 machinepass = glue.generate_random_str(12)
1185 dnspass = glue.generate_random_str(12)
1186 if ldapadminpass is None:
1187 #Make a new, random password between Samba and it's LDAP server
1188 ldapadminpass=glue.generate_random_str(12)
1191 root_uid = findnss_uid([root or "root"])
1192 nobody_uid = findnss_uid([nobody or "nobody"])
1193 users_gid = findnss_gid([users or "users"])
1195 wheel_gid = findnss_gid(["wheel", "adm"])
1197 wheel_gid = findnss_gid([wheel])
1199 if targetdir is not None:
1200 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1201 os.makedirs(os.path.join(targetdir, "etc"))
1202 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1203 elif smbconf is None:
1204 smbconf = param.default_path()
1206 # only install a new smb.conf if there isn't one there already
1207 if not os.path.exists(smbconf):
1208 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1211 lp = param.LoadParm()
1214 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1215 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1216 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1219 paths = provision_paths_from_lp(lp, names.dnsdomain)
1223 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1224 except socket.gaierror, (socket.EAI_NODATA, msg):
1229 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1230 except socket.gaierror, (socket.EAI_NODATA, msg):
1233 if serverrole is None:
1234 serverrole = lp.get("server role")
1236 assert serverrole in ("domain controller", "member server", "standalone")
1237 if invocationid is None and serverrole == "domain controller":
1238 invocationid = str(uuid.uuid4())
1240 if not os.path.exists(paths.private_dir):
1241 os.mkdir(paths.private_dir)
1243 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1245 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1246 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1248 secrets_credentials = credentials
1249 provision_backend = None
1250 if ldap_backend_type:
1251 # We only support an LDAP backend over ldapi://
1253 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1254 lp=lp, credentials=credentials,
1256 message=message, hostname=hostname,
1257 root=root, schema=schema,
1258 ldap_backend_type=ldap_backend_type,
1259 ldapadminpass=ldapadminpass,
1260 ldap_backend_extra_port=ldap_backend_extra_port,
1261 ol_mmr_urls=ol_mmr_urls,
1262 slapd_path=slapd_path,
1263 setup_ds_path=setup_ds_path,
1264 ldap_dryrun_mode=ldap_dryrun_mode)
1266 # Now use the backend credentials to access the databases
1267 credentials = provision_backend.credentials
1268 secrets_credentials = provision_backend.adminCredentials
1269 ldapi_url = provision_backend.ldapi_uri
1271 # only install a new shares config db if there is none
1272 if not os.path.exists(paths.shareconf):
1273 message("Setting up share.ldb")
1274 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1275 credentials=credentials, lp=lp)
1276 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1279 message("Setting up secrets.ldb")
1280 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1281 session_info=session_info,
1282 credentials=secrets_credentials, lp=lp)
1284 message("Setting up the registry")
1285 setup_registry(paths.hklm, setup_path, session_info,
1288 message("Setting up idmap db")
1289 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1292 message("Setting up SAM db")
1293 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1294 credentials=credentials, lp=lp, names=names,
1296 domainsid=domainsid,
1297 schema=schema, domainguid=domainguid,
1298 policyguid=policyguid, policyguid_dc=policyguid_dc,
1300 adminpass=adminpass, krbtgtpass=krbtgtpass,
1301 invocationid=invocationid,
1302 machinepass=machinepass, dnspass=dnspass,
1303 serverrole=serverrole,
1304 dom_for_fun_level=dom_for_fun_level,
1305 ldap_backend=provision_backend)
1307 if serverrole == "domain controller":
1308 if paths.netlogon is None:
1309 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1310 message("Please either remove %s or see the template at %s" %
1311 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1312 assert(paths.netlogon is not None)
1314 if paths.sysvol is None:
1315 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1316 message("Please either remove %s or see the template at %s" %
1317 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1318 assert(paths.sysvol is not None)
1320 # Set up group policies (domain policy and domain controller policy)
1322 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1323 "{" + policyguid + "}")
1324 os.makedirs(policy_path, 0755)
1325 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1326 "[General]\r\nVersion=65543")
1327 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1328 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1330 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1331 "{" + policyguid_dc + "}")
1332 os.makedirs(policy_path_dc, 0755)
1333 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1334 "[General]\r\nVersion=2")
1335 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1336 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1338 if not os.path.isdir(paths.netlogon):
1339 os.makedirs(paths.netlogon, 0755)
1341 if samdb_fill == FILL_FULL:
1342 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1343 root_uid=root_uid, nobody_uid=nobody_uid,
1344 users_gid=users_gid, wheel_gid=wheel_gid)
1346 message("Setting up sam.ldb rootDSE marking as synchronized")
1347 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1349 # Only make a zone file on the first DC, it should be replicated with DNS replication
1350 if serverrole == "domain controller":
1351 secretsdb_self_join(secrets_ldb, domain=domain,
1353 dnsdomain=names.dnsdomain,
1354 netbiosname=names.netbiosname,
1355 domainsid=domainsid,
1356 machinepass=machinepass,
1357 secure_channel_type=SEC_CHAN_BDC)
1359 secretsdb_setup_dns(secrets_ldb, setup_path,
1360 realm=names.realm, dnsdomain=names.dnsdomain,
1361 dns_keytab_path=paths.dns_keytab,
1364 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1365 assert isinstance(domainguid, str)
1367 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1368 domaindn=names.domaindn, hostip=hostip,
1369 hostip6=hostip6, hostname=names.hostname,
1370 dnspass=dnspass, realm=names.realm,
1371 domainguid=domainguid, ntdsguid=names.ntdsguid)
1373 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1374 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1376 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1377 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1378 keytab_name=paths.dns_keytab)
1379 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1380 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1382 create_krb5_conf(paths.krb5conf, setup_path,
1383 dnsdomain=names.dnsdomain, hostname=names.hostname,
1385 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1387 #Now commit the secrets.ldb to disk
1388 secrets_ldb.transaction_commit()
1390 if provision_backend is not None:
1391 if ldap_backend_type == "fedora-ds":
1392 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1394 # delete default SASL mappings
1395 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1397 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1398 for i in range (0, len(res)):
1399 dn = str(res[i]["dn"])
1402 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1405 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1407 m.dn = ldb.Dn(1, names.domaindn)
1410 m.dn = ldb.Dn(1, names.configdn)
1413 m.dn = ldb.Dn(1, names.schemadn)
1416 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1417 if provision_backend.slapd.poll() is None:
1419 if hasattr(provision_backend.slapd, "terminate"):
1420 provision_backend.slapd.terminate()
1422 # Older python versions don't have .terminate()
1424 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1426 #and now wait for it to die
1427 provision_backend.slapd.communicate()
1429 # now display slapd_command_file.txt to show how slapd must be started next time
1430 message("Use later the following commandline to start slapd, then Samba:")
1431 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1432 message(slapd_command)
1433 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1435 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1436 "SLAPD_COMMAND" : slapd_command})
1439 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1442 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1444 message("Once the above files are installed, your Samba4 server will be ready to use")
1445 message("Server Role: %s" % serverrole)
1446 message("Hostname: %s" % names.hostname)
1447 message("NetBIOS Domain: %s" % names.domain)
1448 message("DNS Domain: %s" % names.dnsdomain)
1449 message("DOMAIN SID: %s" % str(domainsid))
1450 if samdb_fill == FILL_FULL:
1451 message("Admin password: %s" % adminpass)
1452 if provision_backend:
1453 if provision_backend.credentials.get_bind_dn() is not None:
1454 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1456 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1458 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1460 result = ProvisionResult()
1461 result.domaindn = domaindn
1462 result.paths = paths
1464 result.samdb = samdb
1469 def provision_become_dc(setup_dir=None,
1470 smbconf=None, targetdir=None, realm=None,
1471 rootdn=None, domaindn=None, schemadn=None,
1472 configdn=None, serverdn=None,
1473 domain=None, hostname=None, domainsid=None,
1474 adminpass=None, krbtgtpass=None, domainguid=None,
1475 policyguid=None, policyguid_dc=None, invocationid=None,
1477 dnspass=None, root=None, nobody=None, users=None,
1478 wheel=None, backup=None, serverrole=None,
1479 ldap_backend=None, ldap_backend_type=None,
1480 sitename=None, debuglevel=1):
1483 """print a message if quiet is not set."""
1486 glue.set_debug_level(debuglevel)
1488 return provision(setup_dir, message, system_session(), None,
1489 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1490 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1491 configdn=configdn, serverdn=serverdn, domain=domain,
1492 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1493 machinepass=machinepass, serverrole="domain controller",
1497 def setup_db_config(setup_path, dbdir):
1498 """Setup a Berkeley database.
1500 :param setup_path: Setup path function.
1501 :param dbdir: Database directory."""
1502 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1503 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1504 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1505 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1507 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1508 {"LDAPDBDIR": dbdir})
1510 class ProvisionBackend(object):
1511 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1512 names=None, message=None,
1513 hostname=None, root=None,
1514 schema=None, ldapadminpass=None,
1515 ldap_backend_type=None, ldap_backend_extra_port=None,
1517 setup_ds_path=None, slapd_path=None,
1518 nosync=False, ldap_dryrun_mode=False):
1519 """Provision an LDAP backend for samba4
1521 This works for OpenLDAP and Fedora DS
1524 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1526 if not os.path.isdir(paths.ldapdir):
1527 os.makedirs(paths.ldapdir, 0700)
1529 if ldap_backend_type == "existing":
1530 #Check to see that this 'existing' LDAP backend in fact exists
1531 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1532 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1533 expression="(objectClass=OpenLDAProotDSE)")
1535 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1536 # This caused them to be set into the long-term database later in the script.
1537 self.credentials = credentials
1538 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1541 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1542 # if another instance of slapd is already running
1544 ldapi_db = Ldb(self.ldapi_uri)
1545 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1546 expression="(objectClass=OpenLDAProotDSE)");
1548 f = open(paths.slapdpid, "r")
1551 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1555 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1560 # Try to print helpful messages when the user has not specified the path to slapd
1561 if slapd_path is None:
1562 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1563 if not os.path.exists(slapd_path):
1564 message (slapd_path)
1565 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1567 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1569 os.unlink(schemadb_path)
1574 # Put the LDIF of the schema into a database so we can search on
1575 # it to generate schema-dependent configurations in Fedora DS and
1577 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1578 schema.ldb.connect(schemadb_path)
1579 schema.ldb.transaction_start()
1581 # These bits of LDIF are supplied when the Schema object is created
1582 schema.ldb.add_ldif(schema.schema_dn_add)
1583 schema.ldb.modify_ldif(schema.schema_dn_modify)
1584 schema.ldb.add_ldif(schema.schema_data)
1585 schema.ldb.transaction_commit()
1587 self.credentials = Credentials()
1588 self.credentials.guess(lp)
1589 #Kerberos to an ldapi:// backend makes no sense
1590 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1592 self.adminCredentials = Credentials()
1593 self.adminCredentials.guess(lp)
1594 #Kerberos to an ldapi:// backend makes no sense
1595 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1597 self.ldap_backend_type = ldap_backend_type
1599 if ldap_backend_type == "fedora-ds":
1600 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1601 names=names, message=message,
1603 ldapadminpass=ldapadminpass, root=root,
1605 ldap_backend_extra_port=ldap_backend_extra_port,
1606 setup_ds_path=setup_ds_path,
1607 slapd_path=slapd_path,
1609 ldap_dryrun_mode=ldap_dryrun_mode)
1611 elif ldap_backend_type == "openldap":
1612 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1613 names=names, message=message,
1615 ldapadminpass=ldapadminpass, root=root,
1617 ldap_backend_extra_port=ldap_backend_extra_port,
1618 ol_mmr_urls=ol_mmr_urls,
1619 slapd_path=slapd_path,
1621 ldap_dryrun_mode=ldap_dryrun_mode)
1623 raise ProvisioningError("Unknown LDAP backend type selected")
1625 self.credentials.set_password(ldapadminpass)
1626 self.adminCredentials.set_username("samba-admin")
1627 self.adminCredentials.set_password(ldapadminpass)
1629 # Now start the slapd, so we can provision onto it. We keep the
1630 # subprocess context around, to kill this off at the successful
1632 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1634 while self.slapd.poll() is None:
1635 # Wait until the socket appears
1637 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1638 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1639 expression="(objectClass=OpenLDAProotDSE)")
1640 # If we have got here, then we must have a valid connection to the LDAP server!
1646 raise ProvisioningError("slapd died before we could make a connection to it")
1649 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1651 hostname=None, ldapadminpass=None, root=None,
1653 ldap_backend_extra_port=None,
1655 slapd_path=None, nosync=False,
1656 ldap_dryrun_mode=False):
1658 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1661 nosync_config = "dbnosync"
1663 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1664 refint_attributes = ""
1665 memberof_config = "# Generated from Samba4 schema\n"
1666 for att in lnkattr.keys():
1667 if lnkattr[att] is not None:
1668 refint_attributes = refint_attributes + " " + att
1670 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1671 { "MEMBER_ATTR" : att ,
1672 "MEMBEROF_ATTR" : lnkattr[att] })
1674 refint_config = read_and_sub_file(setup_path("refint.conf"),
1675 { "LINK_ATTRS" : refint_attributes})
1677 attrs = ["linkID", "lDAPDisplayName"]
1678 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1680 for i in range (0, len(res)):
1681 index_attr = res[i]["lDAPDisplayName"][0]
1682 if index_attr == "objectGUID":
1683 index_attr = "entryUUID"
1685 index_config += "index " + index_attr + " eq\n"
1687 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1689 mmr_replicator_acl = ""
1690 mmr_serverids_config = ""
1691 mmr_syncrepl_schema_config = ""
1692 mmr_syncrepl_config_config = ""
1693 mmr_syncrepl_user_config = ""
1696 if ol_mmr_urls is not None:
1697 # For now, make these equal
1698 mmr_pass = ldapadminpass
1700 url_list=filter(None,ol_mmr_urls.split(' '))
1701 if (len(url_list) == 1):
1702 url_list=filter(None,ol_mmr_urls.split(','))
1705 mmr_on_config = "MirrorMode On"
1706 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1708 for url in url_list:
1710 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1711 { "SERVERID" : str(serverid),
1712 "LDAPSERVER" : url })
1715 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1717 "MMRDN": names.schemadn,
1719 "MMR_PASSWORD": mmr_pass})
1722 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1724 "MMRDN": names.configdn,
1726 "MMR_PASSWORD": mmr_pass})
1729 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1731 "MMRDN": names.domaindn,
1733 "MMR_PASSWORD": mmr_pass })
1734 # OpenLDAP cn=config initialisation
1735 olc_syncrepl_config = ""
1737 # if mmr = yes, generate cn=config-replication directives
1738 # and olc_seed.lif for the other mmr-servers
1739 if ol_mmr_urls is not None:
1741 olc_serverids_config = ""
1742 olc_syncrepl_seed_config = ""
1743 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1745 for url in url_list:
1747 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1748 { "SERVERID" : str(serverid),
1749 "LDAPSERVER" : url })
1752 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1755 "MMR_PASSWORD": mmr_pass})
1757 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1759 "LDAPSERVER" : url})
1761 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1762 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1763 "OLC_PW": ldapadminpass,
1764 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1767 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1768 {"DNSDOMAIN": names.dnsdomain,
1769 "LDAPDIR": paths.ldapdir,
1770 "DOMAINDN": names.domaindn,
1771 "CONFIGDN": names.configdn,
1772 "SCHEMADN": names.schemadn,
1773 "MEMBEROF_CONFIG": memberof_config,
1774 "MIRRORMODE": mmr_on_config,
1775 "REPLICATOR_ACL": mmr_replicator_acl,
1776 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1777 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1778 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1779 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1780 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1781 "OLC_MMR_CONFIG": olc_mmr_config,
1782 "REFINT_CONFIG": refint_config,
1783 "INDEX_CONFIG": index_config,
1784 "NOSYNC": nosync_config})
1786 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1787 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1788 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1790 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1791 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1793 setup_file(setup_path("cn=samba.ldif"),
1794 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1795 { "UUID": str(uuid.uuid4()),
1796 "LDAPTIME": timestring(int(time.time()))} )
1797 setup_file(setup_path("cn=samba-admin.ldif"),
1798 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1799 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1800 "UUID": str(uuid.uuid4()),
1801 "LDAPTIME": timestring(int(time.time()))} )
1803 if ol_mmr_urls is not None:
1804 setup_file(setup_path("cn=replicator.ldif"),
1805 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1806 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1807 "UUID": str(uuid.uuid4()),
1808 "LDAPTIME": timestring(int(time.time()))} )
1811 mapping = "schema-map-openldap-2.3"
1812 backend_schema = "backend-schema.schema"
1814 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1815 assert backend_schema_data is not None
1816 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1818 # now we generate the needed strings to start slapd automatically,
1819 # first ldapi_uri...
1820 if ldap_backend_extra_port is not None:
1821 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1822 # specified there as part of it's clue as to it's own name,
1823 # and not to replicate to itself
1824 if ol_mmr_urls is None:
1825 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1827 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1829 server_port_string = ""
1831 # Prepare the 'result' information - the commands to return in particular
1832 result.slapd_provision_command = [slapd_path]
1834 result.slapd_provision_command.append("-F" + paths.olcdir)
1836 result.slapd_provision_command.append("-h")
1838 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1839 result.slapd_command = list(result.slapd_provision_command)
1841 result.slapd_provision_command.append(result.ldapi_uri)
1842 result.slapd_provision_command.append("-d0")
1844 uris = result.ldapi_uri
1845 if server_port_string is not "":
1846 uris = uris + " " + server_port_string
1848 result.slapd_command.append(uris)
1850 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1851 result.credentials.set_username("samba-admin")
1853 # If we were just looking for crashes up to this point, it's a
1854 # good time to exit before we realise we don't have OpenLDAP on
1856 if ldap_dryrun_mode:
1859 # Finally, convert the configuration into cn=config style!
1860 if not os.path.isdir(paths.olcdir):
1861 os.makedirs(paths.olcdir, 0770)
1863 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1865 # We can't do this, as OpenLDAP is strange. It gives an error
1866 # output to the above, but does the conversion sucessfully...
1869 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1871 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1872 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1874 # Don't confuse the admin by leaving the slapd.conf around
1875 os.remove(paths.slapdconf)
1878 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1880 hostname=None, ldapadminpass=None, root=None,
1882 ldap_backend_extra_port=None,
1886 ldap_dryrun_mode=False):
1888 if ldap_backend_extra_port is not None:
1889 serverport = "ServerPort=%d" % ldap_backend_extra_port
1893 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1895 "HOSTNAME": hostname,
1896 "DNSDOMAIN": names.dnsdomain,
1897 "LDAPDIR": paths.ldapdir,
1898 "DOMAINDN": names.domaindn,
1899 "LDAPMANAGERDN": names.ldapmanagerdn,
1900 "LDAPMANAGERPASS": ldapadminpass,
1901 "SERVERPORT": serverport})
1903 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1904 {"CONFIGDN": names.configdn,
1905 "SCHEMADN": names.schemadn,
1906 "SAMBADN": names.sambadn,
1909 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1910 {"SAMBADN": names.sambadn,
1913 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1914 {"SAMBADN": names.sambadn,
1915 "LDAPADMINPASS": ldapadminpass
1918 mapping = "schema-map-fedora-ds-1.0"
1919 backend_schema = "99_ad.ldif"
1921 # Build a schema file in Fedora DS format
1922 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1923 assert backend_schema_data is not None
1924 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1926 result.credentials.set_bind_dn(names.ldapmanagerdn)
1928 # Destory the target directory, or else setup-ds.pl will complain
1929 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1930 shutil.rmtree(fedora_ds_dir, True)
1932 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1933 #In the 'provision' command line, stay in the foreground so we can easily kill it
1934 result.slapd_provision_command.append("-d0")
1936 #the command for the final run is the normal script
1937 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1939 # If we were just looking for crashes up to this point, it's a
1940 # good time to exit before we realise we don't have Fedora DS on
1941 if ldap_dryrun_mode:
1944 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1945 if setup_ds_path is None:
1946 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\"!")
1947 if not os.path.exists(setup_ds_path):
1948 message (setup_ds_path)
1949 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1951 # Run the Fedora DS setup utility
1952 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1954 raise ProvisioningError("setup-ds failed")
1957 retcode = subprocess.call([
1958 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1959 close_fds=True, shell=False)
1961 raise("ldib2db failed")
1963 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1964 """Create a PHP LDAP admin configuration file.
1966 :param path: Path to write the configuration to.
1967 :param setup_path: Function to generate setup paths.
1969 setup_file(setup_path("phpldapadmin-config.php"), path,
1970 {"S4_LDAPI_URI": ldapi_uri})
1973 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1974 hostip, hostip6, hostname, dnspass, realm, domainguid,
1976 """Write out a DNS zone file, from the info in the current database.
1978 :param path: Path of the new zone file.
1979 :param setup_path: Setup path function.
1980 :param dnsdomain: DNS Domain name
1981 :param domaindn: DN of the Domain
1982 :param hostip: Local IPv4 IP
1983 :param hostip6: Local IPv6 IP
1984 :param hostname: Local hostname
1985 :param dnspass: Password for DNS
1986 :param realm: Realm name
1987 :param domainguid: GUID of the domain.
1988 :param ntdsguid: GUID of the hosts nTDSDSA record.
1990 assert isinstance(domainguid, str)
1992 if hostip6 is not None:
1993 hostip6_base_line = " IN AAAA " + hostip6
1994 hostip6_host_line = hostname + " IN AAAA " + hostip6
1996 hostip6_base_line = ""
1997 hostip6_host_line = ""
1999 if hostip is not None:
2000 hostip_base_line = " IN A " + hostip
2001 hostip_host_line = hostname + " IN A " + hostip
2003 hostip_base_line = ""
2004 hostip_host_line = ""
2006 setup_file(setup_path("provision.zone"), path, {
2007 "DNSPASS_B64": b64encode(dnspass),
2008 "HOSTNAME": hostname,
2009 "DNSDOMAIN": dnsdomain,
2011 "HOSTIP_BASE_LINE": hostip_base_line,
2012 "HOSTIP_HOST_LINE": hostip_host_line,
2013 "DOMAINGUID": domainguid,
2014 "DATESTRING": time.strftime("%Y%m%d%H"),
2015 "DEFAULTSITE": DEFAULTSITE,
2016 "NTDSGUID": ntdsguid,
2017 "HOSTIP6_BASE_LINE": hostip6_base_line,
2018 "HOSTIP6_HOST_LINE": hostip6_host_line,
2022 def create_named_conf(path, setup_path, realm, dnsdomain,
2024 """Write out a file containing zone statements suitable for inclusion in a
2025 named.conf file (including GSS-TSIG configuration).
2027 :param path: Path of the new named.conf file.
2028 :param setup_path: Setup path function.
2029 :param realm: Realm name
2030 :param dnsdomain: DNS Domain name
2031 :param private_dir: Path to private directory
2032 :param keytab_name: File name of DNS keytab file
2035 setup_file(setup_path("named.conf"), path, {
2036 "DNSDOMAIN": dnsdomain,
2038 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2039 "PRIVATE_DIR": private_dir
2042 def create_named_txt(path, setup_path, realm, dnsdomain,
2043 private_dir, keytab_name):
2044 """Write out a file containing zone statements suitable for inclusion in a
2045 named.conf file (including GSS-TSIG configuration).
2047 :param path: Path of the new named.conf file.
2048 :param setup_path: Setup path function.
2049 :param realm: Realm name
2050 :param dnsdomain: DNS Domain name
2051 :param private_dir: Path to private directory
2052 :param keytab_name: File name of DNS keytab file
2055 setup_file(setup_path("named.txt"), path, {
2056 "DNSDOMAIN": dnsdomain,
2058 "DNS_KEYTAB": keytab_name,
2059 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2060 "PRIVATE_DIR": private_dir
2063 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2064 """Write out a file containing zone statements suitable for inclusion in a
2065 named.conf file (including GSS-TSIG configuration).
2067 :param path: Path of the new named.conf file.
2068 :param setup_path: Setup path function.
2069 :param dnsdomain: DNS Domain name
2070 :param hostname: Local hostname
2071 :param realm: Realm name
2074 setup_file(setup_path("krb5.conf"), path, {
2075 "DNSDOMAIN": dnsdomain,
2076 "HOSTNAME": hostname,