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")
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 dnsdomain = dnsdomain.lower()
446 if serverrole == "domain controller":
448 domain = lp.get("workgroup")
450 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
451 if lp.get("workgroup").upper() != domain.upper():
452 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
453 lp.get("workgroup"), domain)
457 domaindn = "CN=" + netbiosname
459 assert domain is not None
460 domain = domain.upper()
461 if not valid_netbios_name(domain):
462 raise InvalidNetbiosName(domain)
464 if netbiosname.upper() == realm.upper():
465 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
467 if hostname.upper() == realm.upper():
468 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
470 if domain.upper() == realm.upper():
471 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
477 configdn = "CN=Configuration," + rootdn
479 schemadn = "CN=Schema," + configdn
486 names = ProvisionNames()
487 names.rootdn = rootdn
488 names.domaindn = domaindn
489 names.configdn = configdn
490 names.schemadn = schemadn
491 names.sambadn = sambadn
492 names.ldapmanagerdn = "CN=Manager," + rootdn
493 names.dnsdomain = dnsdomain
494 names.domain = domain
496 names.netbiosname = netbiosname
497 names.hostname = hostname
498 names.sitename = sitename
499 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
504 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
506 """Create a new smb.conf file based on a couple of basic settings.
508 assert smbconf is not None
510 hostname = socket.gethostname().split(".")[0].lower()
512 if serverrole is None:
513 serverrole = "standalone"
515 assert serverrole in ("domain controller", "member server", "standalone")
516 if serverrole == "domain controller":
518 elif serverrole == "member server":
519 smbconfsuffix = "member"
520 elif serverrole == "standalone":
521 smbconfsuffix = "standalone"
523 assert domain is not None
524 assert realm is not None
526 default_lp = param.LoadParm()
527 #Load non-existant file
528 if os.path.exists(smbconf):
529 default_lp.load(smbconf)
531 if targetdir is not None:
532 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
533 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
535 default_lp.set("lock dir", os.path.abspath(targetdir))
540 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
541 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
543 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
545 "HOSTNAME": hostname,
548 "SERVERROLE": serverrole,
549 "NETLOGONPATH": netlogon,
550 "SYSVOLPATH": sysvol,
551 "PRIVATEDIR_LINE": privatedir_line,
552 "LOCKDIR_LINE": lockdir_line
556 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
557 users_gid, wheel_gid):
558 """setup reasonable name mappings for sam names to unix names.
560 :param samdb: SamDB object.
561 :param idmap: IDmap db object.
562 :param sid: The domain sid.
563 :param domaindn: The domain DN.
564 :param root_uid: uid of the UNIX root user.
565 :param nobody_uid: uid of the UNIX nobody user.
566 :param users_gid: gid of the UNIX users group.
567 :param wheel_gid: gid of the UNIX wheel group."""
569 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
570 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
572 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
573 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
575 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
577 serverrole, ldap_backend=None,
579 """Setup the partitions for the SAM database.
581 Alternatively, provision() may call this, and then populate the database.
583 :note: This will wipe the Sam Database!
585 :note: This function always removes the local SAM LDB file. The erase
586 parameter controls whether to erase the existing data, which
587 may not be stored locally but in LDAP.
589 assert session_info is not None
591 # We use options=["modules:"] to stop the modules loading - we
592 # just want to wipe and re-initialise the database, not start it up
595 samdb = Ldb(url=samdb_path, session_info=session_info,
596 credentials=credentials, lp=lp, options=["modules:"])
598 samdb.erase_except_schema_controlled()
600 os.unlink(samdb_path)
601 samdb = Ldb(url=samdb_path, session_info=session_info,
602 credentials=credentials, lp=lp, options=["modules:"])
604 samdb.erase_except_schema_controlled()
607 #Add modules to the list to activate them by default
608 #beware often order is important
610 # Some Known ordering constraints:
611 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
612 # - objectclass must be before password_hash, because password_hash checks
613 # that the objectclass is of type person (filled in by objectclass
614 # module when expanding the objectclass list)
615 # - partition must be last
616 # - each partition has its own module list then
617 modules_list = ["resolve_oids",
640 "extended_dn_out_ldb"]
641 modules_list2 = ["show_deleted",
644 domaindn_ldb = "users.ldb"
645 configdn_ldb = "configuration.ldb"
646 schemadn_ldb = "schema.ldb"
647 if ldap_backend is not None:
648 domaindn_ldb = ldap_backend.ldapi_uri
649 configdn_ldb = ldap_backend.ldapi_uri
650 schemadn_ldb = ldap_backend.ldapi_uri
652 if ldap_backend.ldap_backend_type == "fedora-ds":
653 backend_modules = ["nsuniqueid", "paged_searches"]
654 # We can handle linked attributes here, as we don't have directory-side subtree operations
655 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
656 elif ldap_backend.ldap_backend_type == "openldap":
657 backend_modules = ["entryuuid", "paged_searches"]
658 # OpenLDAP handles subtree renames, so we don't want to do any of these things
659 tdb_modules_list = ["extended_dn_out_dereference"]
661 elif serverrole == "domain controller":
662 tdb_modules_list.insert(0, "repl_meta_data")
665 backend_modules = ["objectguid"]
667 if tdb_modules_list is None:
668 tdb_modules_list_as_string = ""
670 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
672 samdb.transaction_start()
674 message("Setting up sam.ldb partitions and settings")
675 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
676 "SCHEMADN": names.schemadn,
677 "SCHEMADN_LDB": schemadn_ldb,
678 "SCHEMADN_MOD2": ",objectguid",
679 "CONFIGDN": names.configdn,
680 "CONFIGDN_LDB": configdn_ldb,
681 "DOMAINDN": names.domaindn,
682 "DOMAINDN_LDB": domaindn_ldb,
683 "SCHEMADN_MOD": "schema_fsmo",
684 "CONFIGDN_MOD": "naming_fsmo",
685 "DOMAINDN_MOD": "pdc_fsmo",
686 "MODULES_LIST": ",".join(modules_list),
687 "TDB_MODULES_LIST": tdb_modules_list_as_string,
688 "MODULES_LIST2": ",".join(modules_list2),
689 "BACKEND_MOD": ",".join(backend_modules),
692 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
694 message("Setting up sam.ldb rootDSE")
695 setup_samdb_rootdse(samdb, setup_path, names)
698 samdb.transaction_cancel()
701 samdb.transaction_commit()
703 def secretsdb_self_join(secretsdb, domain,
704 netbiosname, domainsid, machinepass,
705 realm=None, dnsdomain=None,
707 key_version_number=1,
708 secure_channel_type=SEC_CHAN_WKSTA):
709 """Add domain join-specific bits to a secrets database.
711 :param secretsdb: Ldb Handle to the secrets database
712 :param machinepass: Machine password
714 attrs=["whenChanged",
722 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
723 msg["secureChannelType"] = str(secure_channel_type)
724 msg["flatname"] = [domain]
725 msg["objectClass"] = ["top", "primaryDomain"]
726 if realm is not None:
727 if dnsdomain is None:
728 dnsdomain = realm.lower()
729 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
731 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
732 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
733 msg["privateKeytab"] = ["secrets.keytab"];
736 msg["secret"] = [machinepass]
737 msg["samAccountName"] = ["%s$" % netbiosname]
738 msg["secureChannelType"] = [str(secure_channel_type)]
739 msg["objectSid"] = [ndr_pack(domainsid)]
741 res = secretsdb.search(base="cn=Primary Domains",
743 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
744 scope=SCOPE_ONELEVEL)
747 if del_msg.dn is not msg.dn:
748 secretsdb.delete(del_msg.dn)
750 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
753 msg["priorSecret"] = res[0]["secret"]
754 msg["priorWhenChanged"] = res[0]["whenChanged"]
756 if res["privateKeytab"] is not None:
757 msg["privateKeytab"] = res[0]["privateKeytab"]
759 if res["krb5Keytab"] is not None:
760 msg["krb5Keytab"] = res[0]["krb5Keytab"]
763 el.set_flags(ldb.FLAG_MOD_REPLACE)
764 secretsdb.modify(msg)
769 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
770 dns_keytab_path, dnspass):
771 """Add DNS specific bits to a secrets database.
773 :param secretsdb: Ldb Handle to the secrets database
774 :param setup_path: Setup path function
775 :param machinepass: Machine password
777 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
779 "DNSDOMAIN": dnsdomain,
780 "DNS_KEYTAB": dns_keytab_path,
781 "DNSPASS_B64": b64encode(dnspass),
785 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
786 """Setup the secrets database.
788 :param path: Path to the secrets database.
789 :param setup_path: Get the path to a setup file.
790 :param session_info: Session info.
791 :param credentials: Credentials
792 :param lp: Loadparm context
793 :return: LDB handle for the created secrets database
795 if os.path.exists(path):
797 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
800 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
801 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
803 secrets_ldb.transaction_start()
804 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
806 if credentials is not None and credentials.authentication_requested():
807 if credentials.get_bind_dn() is not None:
808 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
809 "LDAPMANAGERDN": credentials.get_bind_dn(),
810 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
813 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
814 "LDAPADMINUSER": credentials.get_username(),
815 "LDAPADMINREALM": credentials.get_realm(),
816 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
821 def setup_registry(path, setup_path, session_info, lp):
822 """Setup the registry.
824 :param path: Path to the registry database
825 :param setup_path: Function that returns the path to a setup.
826 :param session_info: Session information
827 :param credentials: Credentials
828 :param lp: Loadparm context
830 reg = registry.Registry()
831 hive = registry.open_ldb(path, session_info=session_info,
833 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
834 provision_reg = setup_path("provision.reg")
835 assert os.path.exists(provision_reg)
836 reg.diff_apply(provision_reg)
839 def setup_idmapdb(path, setup_path, session_info, lp):
840 """Setup the idmap database.
842 :param path: path to the idmap database
843 :param setup_path: Function that returns a path to a setup file
844 :param session_info: Session information
845 :param credentials: Credentials
846 :param lp: Loadparm context
848 if os.path.exists(path):
851 idmap_ldb = IDmapDB(path, session_info=session_info,
855 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
859 def setup_samdb_rootdse(samdb, setup_path, names):
860 """Setup the SamDB rootdse.
862 :param samdb: Sam Database handle
863 :param setup_path: Obtain setup path
865 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
866 "SCHEMADN": names.schemadn,
867 "NETBIOSNAME": names.netbiosname,
868 "DNSDOMAIN": names.dnsdomain,
869 "REALM": names.realm,
870 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
871 "DOMAINDN": names.domaindn,
872 "ROOTDN": names.rootdn,
873 "CONFIGDN": names.configdn,
874 "SERVERDN": names.serverdn,
878 def setup_self_join(samdb, names,
879 machinepass, dnspass,
880 domainsid, invocationid, setup_path,
881 policyguid, policyguid_dc, domainControllerFunctionality,ntdsguid):
882 """Join a host to its own domain."""
883 assert isinstance(invocationid, str)
884 if ntdsguid is not None:
885 ntdsguid_mod = "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_mod,
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)
1012 if serverrole == "domain controller":
1013 domain_oc = "domainDNS"
1015 domain_oc = "samba4LocalDomain"
1017 #impersonate domain admin
1018 admin_session_info = admin_session(lp, str(domainsid))
1019 samdb.set_session_info(admin_session_info)
1020 if domainguid is not None:
1021 domainguid_mod = "objectGUID: %s\n-" % domainguid
1024 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1025 "DOMAINDN": names.domaindn,
1026 "DOMAIN_OC": domain_oc,
1027 "DOMAINGUID": domainguid_mod
1031 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1032 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1033 "DOMAINSID": str(domainsid),
1034 "SCHEMADN": names.schemadn,
1035 "NETBIOSNAME": names.netbiosname,
1036 "DEFAULTSITE": names.sitename,
1037 "CONFIGDN": names.configdn,
1038 "SERVERDN": names.serverdn,
1039 "POLICYGUID": policyguid,
1040 "DOMAINDN": names.domaindn,
1041 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1042 "SAMBA_VERSION_STRING": version
1045 message("Adding configuration container")
1046 descr = get_config_descriptor(domainsid);
1047 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1048 "CONFIGDN": names.configdn,
1049 "DESCRIPTOR": descr,
1051 message("Modifying configuration container")
1052 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1053 "CONFIGDN": names.configdn,
1054 "SCHEMADN": names.schemadn,
1057 # The LDIF here was created when the Schema object was constructed
1058 message("Setting up sam.ldb schema")
1059 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1060 samdb.modify_ldif(schema.schema_dn_modify)
1061 samdb.write_prefixes_from_schema()
1062 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1063 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1064 {"SCHEMADN": names.schemadn})
1066 message("Setting up sam.ldb configuration data")
1067 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1068 "CONFIGDN": names.configdn,
1069 "NETBIOSNAME": names.netbiosname,
1070 "DEFAULTSITE": names.sitename,
1071 "DNSDOMAIN": names.dnsdomain,
1072 "DOMAIN": names.domain,
1073 "SCHEMADN": names.schemadn,
1074 "DOMAINDN": names.domaindn,
1075 "SERVERDN": names.serverdn,
1076 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1079 message("Setting up display specifiers")
1080 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1081 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1082 check_all_substituted(display_specifiers_ldif)
1083 samdb.add_ldif(display_specifiers_ldif)
1085 message("Adding users container")
1086 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1087 "DOMAINDN": names.domaindn})
1088 message("Modifying users container")
1089 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1090 "DOMAINDN": names.domaindn})
1091 message("Adding computers container")
1092 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1093 "DOMAINDN": names.domaindn})
1094 message("Modifying computers container")
1095 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1096 "DOMAINDN": names.domaindn})
1097 message("Setting up sam.ldb data")
1098 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1099 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1100 "DOMAINDN": names.domaindn,
1101 "NETBIOSNAME": names.netbiosname,
1102 "DEFAULTSITE": names.sitename,
1103 "CONFIGDN": names.configdn,
1104 "SERVERDN": names.serverdn,
1105 "POLICYGUID_DC": policyguid_dc
1108 if fill == FILL_FULL:
1109 message("Setting up sam.ldb users and groups")
1110 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1111 "DOMAINDN": names.domaindn,
1112 "DOMAINSID": str(domainsid),
1113 "CONFIGDN": names.configdn,
1114 "ADMINPASS_B64": b64encode(adminpass),
1115 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1118 if serverrole == "domain controller":
1119 message("Setting up self join")
1120 setup_self_join(samdb, names=names, invocationid=invocationid,
1122 machinepass=machinepass,
1123 domainsid=domainsid, policyguid=policyguid,
1124 policyguid_dc=policyguid_dc,
1125 setup_path=setup_path,
1126 domainControllerFunctionality=domainControllerFunctionality,ntdsguid=ntdsguid)
1128 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1129 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1130 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1131 assert isinstance(names.ntdsguid, str)
1134 samdb.transaction_cancel()
1137 samdb.transaction_commit()
1142 FILL_NT4SYNC = "NT4SYNC"
1146 def provision(setup_dir, message, session_info,
1147 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1149 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1151 domain=None, hostname=None, hostip=None, hostip6=None,
1152 domainsid=None, adminpass=None, ldapadminpass=None,
1153 krbtgtpass=None, domainguid=None,
1154 policyguid=None, policyguid_dc=None, invocationid=None,
1155 machinepass=None,ntdsguid=None,
1156 dnspass=None, root=None, nobody=None, users=None,
1157 wheel=None, backup=None, aci=None, serverrole=None,
1158 dom_for_fun_level=None,
1159 ldap_backend_extra_port=None, ldap_backend_type=None,
1161 ol_mmr_urls=None, ol_olc=None,
1162 setup_ds_path=None, slapd_path=None, nosync=False,
1163 ldap_dryrun_mode=False):
1166 :note: caution, this wipes all existing data!
1169 def setup_path(file):
1170 return os.path.join(setup_dir, file)
1172 if domainsid is None:
1173 domainsid = security.random_sid()
1175 domainsid = security.dom_sid(domainsid)
1177 # create/adapt the group policy GUIDs
1178 if policyguid is None:
1179 policyguid = str(uuid.uuid4())
1180 policyguid = policyguid.upper()
1181 if policyguid_dc is None:
1182 policyguid_dc = str(uuid.uuid4())
1183 policyguid_dc = policyguid_dc.upper()
1185 if adminpass is None:
1186 adminpass = glue.generate_random_str(12)
1187 if krbtgtpass is None:
1188 krbtgtpass = glue.generate_random_str(12)
1189 if machinepass is None:
1190 machinepass = glue.generate_random_str(12)
1192 dnspass = glue.generate_random_str(12)
1193 if ldapadminpass is None:
1194 #Make a new, random password between Samba and it's LDAP server
1195 ldapadminpass=glue.generate_random_str(12)
1198 root_uid = findnss_uid([root or "root"])
1199 nobody_uid = findnss_uid([nobody or "nobody"])
1200 users_gid = findnss_gid([users or "users"])
1202 wheel_gid = findnss_gid(["wheel", "adm"])
1204 wheel_gid = findnss_gid([wheel])
1206 if targetdir is not None:
1207 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1208 os.makedirs(os.path.join(targetdir, "etc"))
1209 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1210 elif smbconf is None:
1211 smbconf = param.default_path()
1213 # only install a new smb.conf if there isn't one there already
1214 if not os.path.exists(smbconf):
1215 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1218 lp = param.LoadParm()
1221 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1222 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1223 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1226 paths = provision_paths_from_lp(lp, names.dnsdomain)
1230 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1231 except socket.gaierror, (socket.EAI_NODATA, msg):
1236 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1237 except socket.gaierror, (socket.EAI_NODATA, msg):
1240 if serverrole is None:
1241 serverrole = lp.get("server role")
1243 assert serverrole in ("domain controller", "member server", "standalone")
1244 if invocationid is None and serverrole == "domain controller":
1245 invocationid = str(uuid.uuid4())
1247 if not os.path.exists(paths.private_dir):
1248 os.mkdir(paths.private_dir)
1250 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1252 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1253 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1255 secrets_credentials = credentials
1256 provision_backend = None
1257 if ldap_backend_type:
1258 # We only support an LDAP backend over ldapi://
1260 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1261 lp=lp, credentials=credentials,
1263 message=message, hostname=hostname,
1264 root=root, schema=schema,
1265 ldap_backend_type=ldap_backend_type,
1266 ldapadminpass=ldapadminpass,
1267 ldap_backend_extra_port=ldap_backend_extra_port,
1268 ol_mmr_urls=ol_mmr_urls,
1269 slapd_path=slapd_path,
1270 setup_ds_path=setup_ds_path,
1271 ldap_dryrun_mode=ldap_dryrun_mode)
1273 # Now use the backend credentials to access the databases
1274 credentials = provision_backend.credentials
1275 secrets_credentials = provision_backend.adminCredentials
1276 ldapi_url = provision_backend.ldapi_uri
1278 # only install a new shares config db if there is none
1279 if not os.path.exists(paths.shareconf):
1280 message("Setting up share.ldb")
1281 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1282 credentials=credentials, lp=lp)
1283 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1286 message("Setting up secrets.ldb")
1287 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1288 session_info=session_info,
1289 credentials=secrets_credentials, lp=lp)
1291 message("Setting up the registry")
1292 setup_registry(paths.hklm, setup_path, session_info,
1295 message("Setting up idmap db")
1296 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1299 message("Setting up SAM db")
1300 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1301 credentials=credentials, lp=lp, names=names,
1303 domainsid=domainsid,
1304 schema=schema, domainguid=domainguid,
1305 policyguid=policyguid, policyguid_dc=policyguid_dc,
1307 adminpass=adminpass, krbtgtpass=krbtgtpass,
1308 invocationid=invocationid,
1309 machinepass=machinepass, dnspass=dnspass,
1311 serverrole=serverrole,
1312 dom_for_fun_level=dom_for_fun_level,
1313 ldap_backend=provision_backend)
1315 if serverrole == "domain controller":
1316 if paths.netlogon is None:
1317 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1318 message("Please either remove %s or see the template at %s" %
1319 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1320 assert(paths.netlogon is not None)
1322 if paths.sysvol is None:
1323 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1324 message("Please either remove %s or see the template at %s" %
1325 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1326 assert(paths.sysvol is not None)
1328 # Set up group policies (domain policy and domain controller policy)
1330 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1331 "{" + policyguid + "}")
1332 os.makedirs(policy_path, 0755)
1333 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1334 "[General]\r\nVersion=65543")
1335 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1336 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1338 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1339 "{" + policyguid_dc + "}")
1340 os.makedirs(policy_path_dc, 0755)
1341 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1342 "[General]\r\nVersion=2")
1343 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1344 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1346 if not os.path.isdir(paths.netlogon):
1347 os.makedirs(paths.netlogon, 0755)
1349 if samdb_fill == FILL_FULL:
1350 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1351 root_uid=root_uid, nobody_uid=nobody_uid,
1352 users_gid=users_gid, wheel_gid=wheel_gid)
1354 message("Setting up sam.ldb rootDSE marking as synchronized")
1355 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1357 # Only make a zone file on the first DC, it should be replicated with DNS replication
1358 if serverrole == "domain controller":
1359 secretsdb_self_join(secrets_ldb, domain=domain,
1361 dnsdomain=names.dnsdomain,
1362 netbiosname=names.netbiosname,
1363 domainsid=domainsid,
1364 machinepass=machinepass,
1365 secure_channel_type=SEC_CHAN_BDC)
1367 secretsdb_setup_dns(secrets_ldb, setup_path,
1368 realm=names.realm, dnsdomain=names.dnsdomain,
1369 dns_keytab_path=paths.dns_keytab,
1372 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1373 assert isinstance(domainguid, str)
1375 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1376 domaindn=names.domaindn, hostip=hostip,
1377 hostip6=hostip6, hostname=names.hostname,
1378 dnspass=dnspass, realm=names.realm,
1379 domainguid=domainguid, ntdsguid=names.ntdsguid)
1381 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1382 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1384 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1385 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1386 keytab_name=paths.dns_keytab)
1387 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1388 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1390 create_krb5_conf(paths.krb5conf, setup_path,
1391 dnsdomain=names.dnsdomain, hostname=names.hostname,
1393 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1395 #Now commit the secrets.ldb to disk
1396 secrets_ldb.transaction_commit()
1398 if provision_backend is not None:
1399 if ldap_backend_type == "fedora-ds":
1400 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1402 # delete default SASL mappings
1403 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1405 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1406 for i in range (0, len(res)):
1407 dn = str(res[i]["dn"])
1410 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1413 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1415 m.dn = ldb.Dn(1, names.domaindn)
1418 m.dn = ldb.Dn(1, names.configdn)
1421 m.dn = ldb.Dn(1, names.schemadn)
1424 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1425 if provision_backend.slapd.poll() is None:
1427 if hasattr(provision_backend.slapd, "terminate"):
1428 provision_backend.slapd.terminate()
1430 # Older python versions don't have .terminate()
1432 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1434 #and now wait for it to die
1435 provision_backend.slapd.communicate()
1437 # now display slapd_command_file.txt to show how slapd must be started next time
1438 message("Use later the following commandline to start slapd, then Samba:")
1439 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1440 message(slapd_command)
1441 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1443 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1444 "SLAPD_COMMAND" : slapd_command})
1447 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1450 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1452 message("Once the above files are installed, your Samba4 server will be ready to use")
1453 message("Server Role: %s" % serverrole)
1454 message("Hostname: %s" % names.hostname)
1455 message("NetBIOS Domain: %s" % names.domain)
1456 message("DNS Domain: %s" % names.dnsdomain)
1457 message("DOMAIN SID: %s" % str(domainsid))
1458 if samdb_fill == FILL_FULL:
1459 message("Admin password: %s" % adminpass)
1460 if provision_backend:
1461 if provision_backend.credentials.get_bind_dn() is not None:
1462 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1464 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1466 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1468 result = ProvisionResult()
1469 result.domaindn = domaindn
1470 result.paths = paths
1472 result.samdb = samdb
1477 def provision_become_dc(setup_dir=None,
1478 smbconf=None, targetdir=None, realm=None,
1479 rootdn=None, domaindn=None, schemadn=None,
1480 configdn=None, serverdn=None,
1481 domain=None, hostname=None, domainsid=None,
1482 adminpass=None, krbtgtpass=None, domainguid=None,
1483 policyguid=None, policyguid_dc=None, invocationid=None,
1485 dnspass=None, root=None, nobody=None, users=None,
1486 wheel=None, backup=None, serverrole=None,
1487 ldap_backend=None, ldap_backend_type=None,
1488 sitename=None, debuglevel=1):
1491 """print a message if quiet is not set."""
1494 glue.set_debug_level(debuglevel)
1496 return provision(setup_dir, message, system_session(), None,
1497 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1498 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1499 configdn=configdn, serverdn=serverdn, domain=domain,
1500 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1501 machinepass=machinepass, serverrole="domain controller",
1505 def setup_db_config(setup_path, dbdir):
1506 """Setup a Berkeley database.
1508 :param setup_path: Setup path function.
1509 :param dbdir: Database directory."""
1510 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1511 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1512 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1513 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1515 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1516 {"LDAPDBDIR": dbdir})
1518 class ProvisionBackend(object):
1519 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1520 names=None, message=None,
1521 hostname=None, root=None,
1522 schema=None, ldapadminpass=None,
1523 ldap_backend_type=None, ldap_backend_extra_port=None,
1525 setup_ds_path=None, slapd_path=None,
1526 nosync=False, ldap_dryrun_mode=False):
1527 """Provision an LDAP backend for samba4
1529 This works for OpenLDAP and Fedora DS
1532 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1534 if not os.path.isdir(paths.ldapdir):
1535 os.makedirs(paths.ldapdir, 0700)
1537 if ldap_backend_type == "existing":
1538 #Check to see that this 'existing' LDAP backend in fact exists
1539 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1540 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1541 expression="(objectClass=OpenLDAProotDSE)")
1543 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1544 # This caused them to be set into the long-term database later in the script.
1545 self.credentials = credentials
1546 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1549 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1550 # if another instance of slapd is already running
1552 ldapi_db = Ldb(self.ldapi_uri)
1553 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1554 expression="(objectClass=OpenLDAProotDSE)");
1556 f = open(paths.slapdpid, "r")
1559 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1563 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1568 # Try to print helpful messages when the user has not specified the path to slapd
1569 if slapd_path is None:
1570 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1571 if not os.path.exists(slapd_path):
1572 message (slapd_path)
1573 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1575 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1577 os.unlink(schemadb_path)
1582 # Put the LDIF of the schema into a database so we can search on
1583 # it to generate schema-dependent configurations in Fedora DS and
1585 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1586 schema.ldb.connect(schemadb_path)
1587 schema.ldb.transaction_start()
1589 # These bits of LDIF are supplied when the Schema object is created
1590 schema.ldb.add_ldif(schema.schema_dn_add)
1591 schema.ldb.modify_ldif(schema.schema_dn_modify)
1592 schema.ldb.add_ldif(schema.schema_data)
1593 schema.ldb.transaction_commit()
1595 self.credentials = Credentials()
1596 self.credentials.guess(lp)
1597 #Kerberos to an ldapi:// backend makes no sense
1598 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1600 self.adminCredentials = Credentials()
1601 self.adminCredentials.guess(lp)
1602 #Kerberos to an ldapi:// backend makes no sense
1603 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1605 self.ldap_backend_type = ldap_backend_type
1607 if ldap_backend_type == "fedora-ds":
1608 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1609 names=names, message=message,
1611 ldapadminpass=ldapadminpass, root=root,
1613 ldap_backend_extra_port=ldap_backend_extra_port,
1614 setup_ds_path=setup_ds_path,
1615 slapd_path=slapd_path,
1617 ldap_dryrun_mode=ldap_dryrun_mode)
1619 elif ldap_backend_type == "openldap":
1620 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1621 names=names, message=message,
1623 ldapadminpass=ldapadminpass, root=root,
1625 ldap_backend_extra_port=ldap_backend_extra_port,
1626 ol_mmr_urls=ol_mmr_urls,
1627 slapd_path=slapd_path,
1629 ldap_dryrun_mode=ldap_dryrun_mode)
1631 raise ProvisioningError("Unknown LDAP backend type selected")
1633 self.credentials.set_password(ldapadminpass)
1634 self.adminCredentials.set_username("samba-admin")
1635 self.adminCredentials.set_password(ldapadminpass)
1637 # Now start the slapd, so we can provision onto it. We keep the
1638 # subprocess context around, to kill this off at the successful
1640 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1642 while self.slapd.poll() is None:
1643 # Wait until the socket appears
1645 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1646 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1647 expression="(objectClass=OpenLDAProotDSE)")
1648 # If we have got here, then we must have a valid connection to the LDAP server!
1654 raise ProvisioningError("slapd died before we could make a connection to it")
1657 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1659 hostname=None, ldapadminpass=None, root=None,
1661 ldap_backend_extra_port=None,
1663 slapd_path=None, nosync=False,
1664 ldap_dryrun_mode=False):
1666 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1669 nosync_config = "dbnosync"
1671 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1672 refint_attributes = ""
1673 memberof_config = "# Generated from Samba4 schema\n"
1674 for att in lnkattr.keys():
1675 if lnkattr[att] is not None:
1676 refint_attributes = refint_attributes + " " + att
1678 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1679 { "MEMBER_ATTR" : att ,
1680 "MEMBEROF_ATTR" : lnkattr[att] })
1682 refint_config = read_and_sub_file(setup_path("refint.conf"),
1683 { "LINK_ATTRS" : refint_attributes})
1685 attrs = ["linkID", "lDAPDisplayName"]
1686 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1688 for i in range (0, len(res)):
1689 index_attr = res[i]["lDAPDisplayName"][0]
1690 if index_attr == "objectGUID":
1691 index_attr = "entryUUID"
1693 index_config += "index " + index_attr + " eq\n"
1695 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1697 mmr_replicator_acl = ""
1698 mmr_serverids_config = ""
1699 mmr_syncrepl_schema_config = ""
1700 mmr_syncrepl_config_config = ""
1701 mmr_syncrepl_user_config = ""
1704 if ol_mmr_urls is not None:
1705 # For now, make these equal
1706 mmr_pass = ldapadminpass
1708 url_list=filter(None,ol_mmr_urls.split(' '))
1709 if (len(url_list) == 1):
1710 url_list=filter(None,ol_mmr_urls.split(','))
1713 mmr_on_config = "MirrorMode On"
1714 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1716 for url in url_list:
1718 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1719 { "SERVERID" : str(serverid),
1720 "LDAPSERVER" : url })
1723 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1725 "MMRDN": names.schemadn,
1727 "MMR_PASSWORD": mmr_pass})
1730 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1732 "MMRDN": names.configdn,
1734 "MMR_PASSWORD": mmr_pass})
1737 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1739 "MMRDN": names.domaindn,
1741 "MMR_PASSWORD": mmr_pass })
1742 # OpenLDAP cn=config initialisation
1743 olc_syncrepl_config = ""
1745 # if mmr = yes, generate cn=config-replication directives
1746 # and olc_seed.lif for the other mmr-servers
1747 if ol_mmr_urls is not None:
1749 olc_serverids_config = ""
1750 olc_syncrepl_seed_config = ""
1751 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1753 for url in url_list:
1755 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1756 { "SERVERID" : str(serverid),
1757 "LDAPSERVER" : url })
1760 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1763 "MMR_PASSWORD": mmr_pass})
1765 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1767 "LDAPSERVER" : url})
1769 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1770 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1771 "OLC_PW": ldapadminpass,
1772 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1775 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1776 {"DNSDOMAIN": names.dnsdomain,
1777 "LDAPDIR": paths.ldapdir,
1778 "DOMAINDN": names.domaindn,
1779 "CONFIGDN": names.configdn,
1780 "SCHEMADN": names.schemadn,
1781 "MEMBEROF_CONFIG": memberof_config,
1782 "MIRRORMODE": mmr_on_config,
1783 "REPLICATOR_ACL": mmr_replicator_acl,
1784 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1785 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1786 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1787 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1788 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1789 "OLC_MMR_CONFIG": olc_mmr_config,
1790 "REFINT_CONFIG": refint_config,
1791 "INDEX_CONFIG": index_config,
1792 "NOSYNC": nosync_config})
1794 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1795 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1796 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1798 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1799 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1801 setup_file(setup_path("cn=samba.ldif"),
1802 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1803 { "UUID": str(uuid.uuid4()),
1804 "LDAPTIME": timestring(int(time.time()))} )
1805 setup_file(setup_path("cn=samba-admin.ldif"),
1806 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1807 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1808 "UUID": str(uuid.uuid4()),
1809 "LDAPTIME": timestring(int(time.time()))} )
1811 if ol_mmr_urls is not None:
1812 setup_file(setup_path("cn=replicator.ldif"),
1813 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1814 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1815 "UUID": str(uuid.uuid4()),
1816 "LDAPTIME": timestring(int(time.time()))} )
1819 mapping = "schema-map-openldap-2.3"
1820 backend_schema = "backend-schema.schema"
1822 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1823 assert backend_schema_data is not None
1824 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1826 # now we generate the needed strings to start slapd automatically,
1827 # first ldapi_uri...
1828 if ldap_backend_extra_port is not None:
1829 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1830 # specified there as part of it's clue as to it's own name,
1831 # and not to replicate to itself
1832 if ol_mmr_urls is None:
1833 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1835 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1837 server_port_string = ""
1839 # Prepare the 'result' information - the commands to return in particular
1840 result.slapd_provision_command = [slapd_path]
1842 result.slapd_provision_command.append("-F" + paths.olcdir)
1844 result.slapd_provision_command.append("-h")
1846 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1847 result.slapd_command = list(result.slapd_provision_command)
1849 result.slapd_provision_command.append(result.ldapi_uri)
1850 result.slapd_provision_command.append("-d0")
1852 uris = result.ldapi_uri
1853 if server_port_string is not "":
1854 uris = uris + " " + server_port_string
1856 result.slapd_command.append(uris)
1858 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1859 result.credentials.set_username("samba-admin")
1861 # If we were just looking for crashes up to this point, it's a
1862 # good time to exit before we realise we don't have OpenLDAP on
1864 if ldap_dryrun_mode:
1867 # Finally, convert the configuration into cn=config style!
1868 if not os.path.isdir(paths.olcdir):
1869 os.makedirs(paths.olcdir, 0770)
1871 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1873 # We can't do this, as OpenLDAP is strange. It gives an error
1874 # output to the above, but does the conversion sucessfully...
1877 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1879 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1880 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1882 # Don't confuse the admin by leaving the slapd.conf around
1883 os.remove(paths.slapdconf)
1886 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1888 hostname=None, ldapadminpass=None, root=None,
1890 ldap_backend_extra_port=None,
1894 ldap_dryrun_mode=False):
1896 if ldap_backend_extra_port is not None:
1897 serverport = "ServerPort=%d" % ldap_backend_extra_port
1901 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1903 "HOSTNAME": hostname,
1904 "DNSDOMAIN": names.dnsdomain,
1905 "LDAPDIR": paths.ldapdir,
1906 "DOMAINDN": names.domaindn,
1907 "LDAPMANAGERDN": names.ldapmanagerdn,
1908 "LDAPMANAGERPASS": ldapadminpass,
1909 "SERVERPORT": serverport})
1911 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1912 {"CONFIGDN": names.configdn,
1913 "SCHEMADN": names.schemadn,
1914 "SAMBADN": names.sambadn,
1917 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1918 {"SAMBADN": names.sambadn,
1921 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1922 {"SAMBADN": names.sambadn,
1923 "LDAPADMINPASS": ldapadminpass
1926 mapping = "schema-map-fedora-ds-1.0"
1927 backend_schema = "99_ad.ldif"
1929 # Build a schema file in Fedora DS format
1930 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1931 assert backend_schema_data is not None
1932 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1934 result.credentials.set_bind_dn(names.ldapmanagerdn)
1936 # Destory the target directory, or else setup-ds.pl will complain
1937 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1938 shutil.rmtree(fedora_ds_dir, True)
1940 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1941 #In the 'provision' command line, stay in the foreground so we can easily kill it
1942 result.slapd_provision_command.append("-d0")
1944 #the command for the final run is the normal script
1945 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1947 # If we were just looking for crashes up to this point, it's a
1948 # good time to exit before we realise we don't have Fedora DS on
1949 if ldap_dryrun_mode:
1952 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1953 if setup_ds_path is None:
1954 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\"!")
1955 if not os.path.exists(setup_ds_path):
1956 message (setup_ds_path)
1957 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1959 # Run the Fedora DS setup utility
1960 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1962 raise ProvisioningError("setup-ds failed")
1965 retcode = subprocess.call([
1966 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1967 close_fds=True, shell=False)
1969 raise("ldib2db failed")
1971 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1972 """Create a PHP LDAP admin configuration file.
1974 :param path: Path to write the configuration to.
1975 :param setup_path: Function to generate setup paths.
1977 setup_file(setup_path("phpldapadmin-config.php"), path,
1978 {"S4_LDAPI_URI": ldapi_uri})
1981 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1982 hostip, hostip6, hostname, dnspass, realm, domainguid,
1984 """Write out a DNS zone file, from the info in the current database.
1986 :param path: Path of the new zone file.
1987 :param setup_path: Setup path function.
1988 :param dnsdomain: DNS Domain name
1989 :param domaindn: DN of the Domain
1990 :param hostip: Local IPv4 IP
1991 :param hostip6: Local IPv6 IP
1992 :param hostname: Local hostname
1993 :param dnspass: Password for DNS
1994 :param realm: Realm name
1995 :param domainguid: GUID of the domain.
1996 :param ntdsguid: GUID of the hosts nTDSDSA record.
1998 assert isinstance(domainguid, str)
2000 if hostip6 is not None:
2001 hostip6_base_line = " IN AAAA " + hostip6
2002 hostip6_host_line = hostname + " IN AAAA " + hostip6
2004 hostip6_base_line = ""
2005 hostip6_host_line = ""
2007 if hostip is not None:
2008 hostip_base_line = " IN A " + hostip
2009 hostip_host_line = hostname + " IN A " + hostip
2011 hostip_base_line = ""
2012 hostip_host_line = ""
2014 setup_file(setup_path("provision.zone"), path, {
2015 "DNSPASS_B64": b64encode(dnspass),
2016 "HOSTNAME": hostname,
2017 "DNSDOMAIN": dnsdomain,
2019 "HOSTIP_BASE_LINE": hostip_base_line,
2020 "HOSTIP_HOST_LINE": hostip_host_line,
2021 "DOMAINGUID": domainguid,
2022 "DATESTRING": time.strftime("%Y%m%d%H"),
2023 "DEFAULTSITE": DEFAULTSITE,
2024 "NTDSGUID": ntdsguid,
2025 "HOSTIP6_BASE_LINE": hostip6_base_line,
2026 "HOSTIP6_HOST_LINE": hostip6_host_line,
2030 def create_named_conf(path, setup_path, realm, dnsdomain,
2032 """Write out a file containing zone statements suitable for inclusion in a
2033 named.conf file (including GSS-TSIG configuration).
2035 :param path: Path of the new named.conf file.
2036 :param setup_path: Setup path function.
2037 :param realm: Realm name
2038 :param dnsdomain: DNS Domain name
2039 :param private_dir: Path to private directory
2040 :param keytab_name: File name of DNS keytab file
2043 setup_file(setup_path("named.conf"), path, {
2044 "DNSDOMAIN": dnsdomain,
2046 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2047 "PRIVATE_DIR": private_dir
2050 def create_named_txt(path, setup_path, realm, dnsdomain,
2051 private_dir, keytab_name):
2052 """Write out a file containing zone statements suitable for inclusion in a
2053 named.conf file (including GSS-TSIG configuration).
2055 :param path: Path of the new named.conf file.
2056 :param setup_path: Setup path function.
2057 :param realm: Realm name
2058 :param dnsdomain: DNS Domain name
2059 :param private_dir: Path to private directory
2060 :param keytab_name: File name of DNS keytab file
2063 setup_file(setup_path("named.txt"), path, {
2064 "DNSDOMAIN": dnsdomain,
2066 "DNS_KEYTAB": keytab_name,
2067 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2068 "PRIVATE_DIR": private_dir
2071 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2072 """Write out a file containing zone statements suitable for inclusion in a
2073 named.conf file (including GSS-TSIG configuration).
2075 :param path: Path of the new named.conf file.
2076 :param setup_path: Setup path function.
2077 :param dnsdomain: DNS Domain name
2078 :param hostname: Local hostname
2079 :param realm: Realm name
2082 setup_file(setup_path("krb5.conf"), path, {
2083 "DNSDOMAIN": dnsdomain,
2084 "HOSTNAME": hostname,