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
147 self.fedoradspam = None
148 self.fedoradsrefint = None
149 self.fedoradslinkedattributes = None
150 self.fedoradsindex = None
151 self.fedoradssamba = None
153 self.olmmrserveridsconf = None
154 self.olmmrsyncreplconf = None
157 self.olcseedldif = None
160 class ProvisionNames(object):
167 self.ldapmanagerdn = None
168 self.dnsdomain = None
170 self.netbiosname = None
177 class ProvisionResult(object):
184 class Schema(object):
185 def __init__(self, setup_path, domain_sid, schemadn=None,
186 serverdn=None, sambadn=None, ldap_backend_type=None):
187 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
189 :param samdb: Load a schema into a SamDB.
190 :param setup_path: Setup path function.
191 :param schemadn: DN of the schema
192 :param serverdn: DN of the server
194 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
198 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
199 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
200 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
201 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
202 check_all_substituted(self.schema_data)
204 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
205 {"SCHEMADN": schemadn,
206 "SERVERDN": serverdn,
209 descr = get_schema_descriptor(domain_sid)
210 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
211 {"SCHEMADN": schemadn,
215 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
216 prefixmap = b64encode(prefixmap)
220 # We don't actually add this ldif, just parse it
221 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
222 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
225 # Return a hash with the forward attribute as a key and the back as the value
226 def get_linked_attributes(schemadn,schemaldb):
227 attrs = ["linkID", "lDAPDisplayName"]
228 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)
230 for i in range (0, len(res)):
231 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
232 target = schemaldb.searchone(basedn=schemadn,
233 expression=expression,
234 attribute="lDAPDisplayName",
236 if target is not None:
237 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
241 def get_dnsyntax_attributes(schemadn,schemaldb):
242 attrs = ["linkID", "lDAPDisplayName"]
243 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
245 for i in range (0, len(res)):
246 attributes.append(str(res[i]["lDAPDisplayName"]))
251 def check_install(lp, session_info, credentials):
252 """Check whether the current install seems ok.
254 :param lp: Loadparm context
255 :param session_info: Session information
256 :param credentials: Credentials
258 if lp.get("realm") == "":
259 raise Exception("Realm empty")
260 ldb = Ldb(lp.get("sam database"), session_info=session_info,
261 credentials=credentials, lp=lp)
262 if len(ldb.search("(cn=Administrator)")) != 1:
263 raise ProvisioningError("No administrator account found")
266 def findnss(nssfn, names):
267 """Find a user or group from a list of possibilities.
269 :param nssfn: NSS Function to try (should raise KeyError if not found)
270 :param names: Names to check.
271 :return: Value return by first names list.
278 raise KeyError("Unable to find user/group %r" % names)
281 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
282 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
285 def read_and_sub_file(file, subst_vars):
286 """Read a file and sub in variables found in it
288 :param file: File to be read (typically from setup directory)
289 param subst_vars: Optional variables to subsitute in the file.
291 data = open(file, 'r').read()
292 if subst_vars is not None:
293 data = substitute_var(data, subst_vars)
294 check_all_substituted(data)
298 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
299 """Setup a ldb in the private dir.
301 :param ldb: LDB file to import data into
302 :param ldif_path: Path of the LDIF file to load
303 :param subst_vars: Optional variables to subsitute in LDIF.
304 :param nocontrols: Optional list of controls, can be None for no controls
306 assert isinstance(ldif_path, str)
307 data = read_and_sub_file(ldif_path, subst_vars)
308 ldb.add_ldif(data,controls)
311 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
312 """Modify a ldb in the private dir.
314 :param ldb: LDB object.
315 :param ldif_path: LDIF file path.
316 :param subst_vars: Optional dictionary with substitution variables.
318 data = read_and_sub_file(ldif_path, subst_vars)
320 ldb.modify_ldif(data)
323 def setup_ldb(ldb, ldif_path, subst_vars):
324 """Import a LDIF a file into a LDB handle, optionally substituting variables.
326 :note: Either all LDIF data will be added or none (using transactions).
328 :param ldb: LDB file to import into.
329 :param ldif_path: Path to the LDIF file.
330 :param subst_vars: Dictionary with substitution variables.
332 assert ldb is not None
333 ldb.transaction_start()
335 setup_add_ldif(ldb, ldif_path, subst_vars)
337 ldb.transaction_cancel()
339 ldb.transaction_commit()
342 def setup_file(template, fname, subst_vars=None):
343 """Setup a file in the private dir.
345 :param template: Path of the template file.
346 :param fname: Path of the file to create.
347 :param subst_vars: Substitution variables.
351 if os.path.exists(f):
354 data = read_and_sub_file(template, subst_vars)
355 open(f, 'w').write(data)
358 def provision_paths_from_lp(lp, dnsdomain):
359 """Set the default paths for provisioning.
361 :param lp: Loadparm context.
362 :param dnsdomain: DNS Domain name
364 paths = ProvisionPaths()
365 paths.private_dir = lp.get("private dir")
366 paths.dns_keytab = "dns.keytab"
368 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
369 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
370 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
371 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
372 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
373 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
374 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
375 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
376 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
377 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
378 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
379 paths.phpldapadminconfig = os.path.join(paths.private_dir,
380 "phpldapadmin-config.php")
381 paths.ldapdir = os.path.join(paths.private_dir,
383 paths.slapdconf = os.path.join(paths.ldapdir,
385 paths.slapdpid = os.path.join(paths.ldapdir,
387 paths.modulesconf = os.path.join(paths.ldapdir,
389 paths.memberofconf = os.path.join(paths.ldapdir,
391 paths.fedoradsinf = os.path.join(paths.ldapdir,
393 paths.fedoradspartitions = os.path.join(paths.ldapdir,
394 "fedorads-partitions.ldif")
395 paths.fedoradssasl = os.path.join(paths.ldapdir,
396 "fedorads-sasl.ldif")
397 paths.fedoradspam = os.path.join(paths.ldapdir,
399 paths.fedoradsrefint = os.path.join(paths.ldapdir,
400 "fedorads-refint.ldif")
401 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
402 "fedorads-linked-attributes.ldif")
403 paths.fedoradsindex = os.path.join(paths.ldapdir,
404 "fedorads-index.ldif")
405 paths.fedoradssamba = os.path.join(paths.ldapdir,
406 "fedorads-samba.ldif")
407 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
408 "mmr_serverids.conf")
409 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
411 paths.olcdir = os.path.join(paths.ldapdir,
413 paths.olcseedldif = os.path.join(paths.ldapdir,
415 paths.hklm = "hklm.ldb"
416 paths.hkcr = "hkcr.ldb"
417 paths.hkcu = "hkcu.ldb"
418 paths.hku = "hku.ldb"
419 paths.hkpd = "hkpd.ldb"
420 paths.hkpt = "hkpt.ldb"
422 paths.sysvol = lp.get("path", "sysvol")
424 paths.netlogon = lp.get("path", "netlogon")
426 paths.smbconf = lp.configfile
431 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
432 serverrole=None, rootdn=None, domaindn=None, configdn=None,
433 schemadn=None, serverdn=None, sitename=None, sambadn=None):
434 """Guess configuration settings to use."""
437 hostname = socket.gethostname().split(".")[0].lower()
439 netbiosname = hostname.upper()
440 if not valid_netbios_name(netbiosname):
441 raise InvalidNetbiosName(netbiosname)
443 hostname = hostname.lower()
445 if dnsdomain is None:
446 dnsdomain = lp.get("realm").lower()
448 if serverrole is None:
449 serverrole = lp.get("server role")
451 assert dnsdomain is not None
452 realm = dnsdomain.upper()
454 if lp.get("realm").upper() != realm:
455 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
456 (lp.get("realm"), lp.configfile, realm))
458 if serverrole == "domain controller":
460 domain = lp.get("workgroup")
462 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
463 if lp.get("workgroup").upper() != domain.upper():
464 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
465 lp.get("workgroup"), domain)
469 domaindn = "DC=" + netbiosname
471 assert domain is not None
472 domain = domain.upper()
474 if not valid_netbios_name(domain):
475 raise InvalidNetbiosName(domain)
477 if netbiosname.upper() == realm:
478 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
480 if hostname.upper() == realm:
481 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
483 if domain.upper() == realm:
484 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
490 configdn = "CN=Configuration," + rootdn
492 schemadn = "CN=Schema," + configdn
499 names = ProvisionNames()
500 names.rootdn = rootdn
501 names.domaindn = domaindn
502 names.configdn = configdn
503 names.schemadn = schemadn
504 names.sambadn = sambadn
505 names.ldapmanagerdn = "CN=Manager," + rootdn
506 names.dnsdomain = dnsdomain
507 names.domain = domain
509 names.netbiosname = netbiosname
510 names.hostname = hostname
511 names.sitename = sitename
512 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
517 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
519 """Create a new smb.conf file based on a couple of basic settings.
521 assert smbconf is not None
523 hostname = socket.gethostname().split(".")[0].lower()
525 if serverrole is None:
526 serverrole = "standalone"
528 assert serverrole in ("domain controller", "member server", "standalone")
529 if serverrole == "domain controller":
531 elif serverrole == "member server":
532 smbconfsuffix = "member"
533 elif serverrole == "standalone":
534 smbconfsuffix = "standalone"
536 assert domain is not None
537 assert realm is not None
539 default_lp = param.LoadParm()
540 #Load non-existant file
541 if os.path.exists(smbconf):
542 default_lp.load(smbconf)
544 if targetdir is not None:
545 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
546 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
548 default_lp.set("lock dir", os.path.abspath(targetdir))
553 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
554 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
556 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
558 "HOSTNAME": hostname,
561 "SERVERROLE": serverrole,
562 "NETLOGONPATH": netlogon,
563 "SYSVOLPATH": sysvol,
564 "PRIVATEDIR_LINE": privatedir_line,
565 "LOCKDIR_LINE": lockdir_line
569 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
570 users_gid, wheel_gid):
571 """setup reasonable name mappings for sam names to unix names.
573 :param samdb: SamDB object.
574 :param idmap: IDmap db object.
575 :param sid: The domain sid.
576 :param domaindn: The domain DN.
577 :param root_uid: uid of the UNIX root user.
578 :param nobody_uid: uid of the UNIX nobody user.
579 :param users_gid: gid of the UNIX users group.
580 :param wheel_gid: gid of the UNIX wheel group."""
582 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
583 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
585 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
586 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
588 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
590 serverrole, ldap_backend=None,
592 """Setup the partitions for the SAM database.
594 Alternatively, provision() may call this, and then populate the database.
596 :note: This will wipe the Sam Database!
598 :note: This function always removes the local SAM LDB file. The erase
599 parameter controls whether to erase the existing data, which
600 may not be stored locally but in LDAP.
602 assert session_info is not None
604 # We use options=["modules:"] to stop the modules loading - we
605 # just want to wipe and re-initialise the database, not start it up
608 samdb = Ldb(url=samdb_path, session_info=session_info,
609 credentials=credentials, lp=lp, options=["modules:"])
611 samdb.erase_except_schema_controlled()
613 os.unlink(samdb_path)
614 samdb = Ldb(url=samdb_path, session_info=session_info,
615 credentials=credentials, lp=lp, options=["modules:"])
617 samdb.erase_except_schema_controlled()
620 #Add modules to the list to activate them by default
621 #beware often order is important
623 # Some Known ordering constraints:
624 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
625 # - objectclass must be before password_hash, because password_hash checks
626 # that the objectclass is of type person (filled in by objectclass
627 # module when expanding the objectclass list)
628 # - partition must be last
629 # - each partition has its own module list then
630 modules_list = ["resolve_oids",
653 "extended_dn_out_ldb"]
654 modules_list2 = ["show_deleted",
657 domaindn_ldb = "users.ldb"
658 configdn_ldb = "configuration.ldb"
659 schemadn_ldb = "schema.ldb"
660 if ldap_backend is not None:
661 domaindn_ldb = ldap_backend.ldapi_uri
662 configdn_ldb = ldap_backend.ldapi_uri
663 schemadn_ldb = ldap_backend.ldapi_uri
665 if ldap_backend.ldap_backend_type == "fedora-ds":
666 backend_modules = ["nsuniqueid", "paged_searches"]
667 # We can handle linked attributes here, as we don't have directory-side subtree operations
668 tdb_modules_list = ["extended_dn_out_dereference"]
669 elif ldap_backend.ldap_backend_type == "openldap":
670 backend_modules = ["entryuuid", "paged_searches"]
671 # OpenLDAP handles subtree renames, so we don't want to do any of these things
672 tdb_modules_list = ["extended_dn_out_dereference"]
674 elif serverrole == "domain controller":
675 tdb_modules_list.insert(0, "repl_meta_data")
678 backend_modules = ["objectguid"]
680 if tdb_modules_list is None:
681 tdb_modules_list_as_string = ""
683 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
685 samdb.transaction_start()
687 message("Setting up sam.ldb partitions and settings")
688 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
689 "SCHEMADN": names.schemadn,
690 "SCHEMADN_LDB": schemadn_ldb,
691 "SCHEMADN_MOD2": ",objectguid",
692 "CONFIGDN": names.configdn,
693 "CONFIGDN_LDB": configdn_ldb,
694 "DOMAINDN": names.domaindn,
695 "DOMAINDN_LDB": domaindn_ldb,
696 "SCHEMADN_MOD": "schema_fsmo",
697 "CONFIGDN_MOD": "naming_fsmo",
698 "DOMAINDN_MOD": "pdc_fsmo",
699 "MODULES_LIST": ",".join(modules_list),
700 "TDB_MODULES_LIST": tdb_modules_list_as_string,
701 "MODULES_LIST2": ",".join(modules_list2),
702 "BACKEND_MOD": ",".join(backend_modules),
705 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
707 message("Setting up sam.ldb rootDSE")
708 setup_samdb_rootdse(samdb, setup_path, names)
711 samdb.transaction_cancel()
714 samdb.transaction_commit()
716 def secretsdb_self_join(secretsdb, domain,
717 netbiosname, domainsid, machinepass,
718 realm=None, dnsdomain=None,
720 key_version_number=1,
721 secure_channel_type=SEC_CHAN_WKSTA):
722 """Add domain join-specific bits to a secrets database.
724 :param secretsdb: Ldb Handle to the secrets database
725 :param machinepass: Machine password
727 attrs=["whenChanged",
735 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
736 msg["secureChannelType"] = str(secure_channel_type)
737 msg["flatname"] = [domain]
738 msg["objectClass"] = ["top", "primaryDomain"]
739 if realm is not None:
740 if dnsdomain is None:
741 dnsdomain = realm.lower()
742 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
744 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
745 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
746 msg["privateKeytab"] = ["secrets.keytab"];
749 msg["secret"] = [machinepass]
750 msg["samAccountName"] = ["%s$" % netbiosname]
751 msg["secureChannelType"] = [str(secure_channel_type)]
752 msg["objectSid"] = [ndr_pack(domainsid)]
754 res = secretsdb.search(base="cn=Primary Domains",
756 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
757 scope=SCOPE_ONELEVEL)
760 if del_msg.dn is not msg.dn:
761 secretsdb.delete(del_msg.dn)
763 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
766 msg["priorSecret"] = res[0]["secret"]
767 msg["priorWhenChanged"] = res[0]["whenChanged"]
769 if res["privateKeytab"] is not None:
770 msg["privateKeytab"] = res[0]["privateKeytab"]
772 if res["krb5Keytab"] is not None:
773 msg["krb5Keytab"] = res[0]["krb5Keytab"]
776 el.set_flags(ldb.FLAG_MOD_REPLACE)
777 secretsdb.modify(msg)
782 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
783 dns_keytab_path, dnspass):
784 """Add DNS specific bits to a secrets database.
786 :param secretsdb: Ldb Handle to the secrets database
787 :param setup_path: Setup path function
788 :param machinepass: Machine password
790 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
792 "DNSDOMAIN": dnsdomain,
793 "DNS_KEYTAB": dns_keytab_path,
794 "DNSPASS_B64": b64encode(dnspass),
798 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
799 """Setup the secrets database.
801 :param path: Path to the secrets database.
802 :param setup_path: Get the path to a setup file.
803 :param session_info: Session info.
804 :param credentials: Credentials
805 :param lp: Loadparm context
806 :return: LDB handle for the created secrets database
808 if os.path.exists(path):
810 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
813 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
814 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
816 secrets_ldb.transaction_start()
817 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
819 if credentials is not None and credentials.authentication_requested():
820 if credentials.get_bind_dn() is not None:
821 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
822 "LDAPMANAGERDN": credentials.get_bind_dn(),
823 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
826 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
827 "LDAPADMINUSER": credentials.get_username(),
828 "LDAPADMINREALM": credentials.get_realm(),
829 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
834 def setup_privileges(path, setup_path, session_info, lp):
835 """Setup the privileges database.
837 :param path: Path to the privileges database.
838 :param setup_path: Get the path to a setup file.
839 :param session_info: Session info.
840 :param credentials: Credentials
841 :param lp: Loadparm context
842 :return: LDB handle for the created secrets database
844 if os.path.exists(path):
846 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
847 privilege_ldb.erase()
848 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
851 def setup_registry(path, setup_path, session_info, lp):
852 """Setup the registry.
854 :param path: Path to the registry database
855 :param setup_path: Function that returns the path to a setup.
856 :param session_info: Session information
857 :param credentials: Credentials
858 :param lp: Loadparm context
860 reg = registry.Registry()
861 hive = registry.open_ldb(path, session_info=session_info,
863 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
864 provision_reg = setup_path("provision.reg")
865 assert os.path.exists(provision_reg)
866 reg.diff_apply(provision_reg)
869 def setup_idmapdb(path, setup_path, session_info, lp):
870 """Setup the idmap database.
872 :param path: path to the idmap database
873 :param setup_path: Function that returns a path to a setup file
874 :param session_info: Session information
875 :param credentials: Credentials
876 :param lp: Loadparm context
878 if os.path.exists(path):
881 idmap_ldb = IDmapDB(path, session_info=session_info,
885 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
889 def setup_samdb_rootdse(samdb, setup_path, names):
890 """Setup the SamDB rootdse.
892 :param samdb: Sam Database handle
893 :param setup_path: Obtain setup path
895 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
896 "SCHEMADN": names.schemadn,
897 "NETBIOSNAME": names.netbiosname,
898 "DNSDOMAIN": names.dnsdomain,
899 "REALM": names.realm,
900 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
901 "DOMAINDN": names.domaindn,
902 "ROOTDN": names.rootdn,
903 "CONFIGDN": names.configdn,
904 "SERVERDN": names.serverdn,
908 def setup_self_join(samdb, names,
909 machinepass, dnspass,
910 domainsid, invocationid, setup_path,
911 policyguid, policyguid_dc, domainControllerFunctionality,
913 """Join a host to its own domain."""
914 assert isinstance(invocationid, str)
915 if ntdsguid is not None:
916 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
919 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
920 "CONFIGDN": names.configdn,
921 "SCHEMADN": names.schemadn,
922 "DOMAINDN": names.domaindn,
923 "SERVERDN": names.serverdn,
924 "INVOCATIONID": invocationid,
925 "NETBIOSNAME": names.netbiosname,
926 "DEFAULTSITE": names.sitename,
927 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
928 "MACHINEPASS_B64": b64encode(machinepass),
929 "DNSPASS_B64": b64encode(dnspass),
930 "REALM": names.realm,
931 "DOMAIN": names.domain,
932 "DNSDOMAIN": names.dnsdomain,
933 "SAMBA_VERSION_STRING": version,
934 "NTDSGUID": ntdsguid_line,
935 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
937 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
938 "POLICYGUID": policyguid,
939 "POLICYGUID_DC": policyguid_dc,
940 "DNSDOMAIN": names.dnsdomain,
941 "DOMAINSID": str(domainsid),
942 "DOMAINDN": names.domaindn})
944 # add the NTDSGUID based SPNs
945 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
946 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
947 expression="", scope=SCOPE_BASE)
948 assert isinstance(names.ntdsguid, str)
950 # Setup fSMORoleOwner entries to point at the newly created DC entry
951 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
952 "DOMAIN": names.domain,
953 "DNSDOMAIN": names.dnsdomain,
954 "DOMAINDN": names.domaindn,
955 "CONFIGDN": names.configdn,
956 "SCHEMADN": names.schemadn,
957 "DEFAULTSITE": names.sitename,
958 "SERVERDN": names.serverdn,
959 "NETBIOSNAME": names.netbiosname,
960 "NTDSGUID": names.ntdsguid
964 def setup_samdb(path, setup_path, session_info, credentials, lp,
966 domainsid, domainguid, policyguid, policyguid_dc,
967 fill, adminpass, krbtgtpass,
968 machinepass, invocationid, dnspass, ntdsguid,
969 serverrole, dom_for_fun_level=None,
970 schema=None, ldap_backend=None):
971 """Setup a complete SAM Database.
973 :note: This will wipe the main SAM database file!
976 # ATTENTION: Do NOT change these default values without discussion with the
977 # team and/or release manager. They have a big impact on the whole program!
978 domainControllerFunctionality = DS_DC_FUNCTION_2008
980 if dom_for_fun_level is None:
981 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
982 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
983 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
985 if dom_for_fun_level > domainControllerFunctionality:
986 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!")
988 domainFunctionality = dom_for_fun_level
989 forestFunctionality = dom_for_fun_level
991 # Also wipes the database
992 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
993 credentials=credentials, session_info=session_info,
994 names=names, ldap_backend=ldap_backend,
995 serverrole=serverrole)
998 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
999 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
1001 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1002 samdb = Ldb(session_info=session_info,
1003 credentials=credentials, lp=lp)
1005 message("Pre-loading the Samba 4 and AD schema")
1007 # Load the schema from the one we computed earlier
1008 samdb.set_schema_from_ldb(schema.ldb)
1010 # And now we can connect to the DB - the schema won't be loaded from the DB
1014 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
1016 if fill == FILL_DRS:
1019 samdb.transaction_start()
1021 message("Erasing data from partitions")
1022 # Load the schema (again). This time it will force a reindex,
1023 # and will therefore make the erase_partitions() below
1024 # computationally sane
1025 samdb.set_schema_from_ldb(schema.ldb)
1026 samdb.erase_partitions()
1028 # Set the domain functionality levels onto the database.
1029 # Various module (the password_hash module in particular) need
1030 # to know what level of AD we are emulating.
1032 # These will be fixed into the database via the database
1033 # modifictions below, but we need them set from the start.
1034 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1035 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1036 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1038 samdb.set_domain_sid(str(domainsid))
1039 if serverrole == "domain controller":
1040 samdb.set_invocation_id(invocationid)
1042 message("Adding DomainDN: %s" % names.domaindn)
1044 #impersonate domain admin
1045 admin_session_info = admin_session(lp, str(domainsid))
1046 samdb.set_session_info(admin_session_info)
1047 if domainguid is not None:
1048 domainguid_line = "objectGUID: %s\n-" % domainguid
1050 domainguid_line = ""
1051 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1052 "DOMAINDN": names.domaindn,
1053 "DOMAINGUID": domainguid_line
1057 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1058 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1059 "DOMAINSID": str(domainsid),
1060 "SCHEMADN": names.schemadn,
1061 "NETBIOSNAME": names.netbiosname,
1062 "DEFAULTSITE": names.sitename,
1063 "CONFIGDN": names.configdn,
1064 "SERVERDN": names.serverdn,
1065 "POLICYGUID": policyguid,
1066 "DOMAINDN": names.domaindn,
1067 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1068 "SAMBA_VERSION_STRING": version
1071 message("Adding configuration container")
1072 descr = get_config_descriptor(domainsid);
1073 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1074 "CONFIGDN": names.configdn,
1075 "DESCRIPTOR": descr,
1077 message("Modifying configuration container")
1078 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1079 "CONFIGDN": names.configdn,
1080 "SCHEMADN": names.schemadn,
1083 # The LDIF here was created when the Schema object was constructed
1084 message("Setting up sam.ldb schema")
1085 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1086 samdb.modify_ldif(schema.schema_dn_modify)
1087 samdb.write_prefixes_from_schema()
1088 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1089 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1090 {"SCHEMADN": names.schemadn})
1092 message("Setting up sam.ldb configuration data")
1093 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1094 "CONFIGDN": names.configdn,
1095 "NETBIOSNAME": names.netbiosname,
1096 "DEFAULTSITE": names.sitename,
1097 "DNSDOMAIN": names.dnsdomain,
1098 "DOMAIN": names.domain,
1099 "SCHEMADN": names.schemadn,
1100 "DOMAINDN": names.domaindn,
1101 "SERVERDN": names.serverdn,
1102 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1105 message("Setting up display specifiers")
1106 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1107 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1108 check_all_substituted(display_specifiers_ldif)
1109 samdb.add_ldif(display_specifiers_ldif)
1111 message("Adding users container")
1112 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1113 "DOMAINDN": names.domaindn})
1114 message("Modifying users container")
1115 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1116 "DOMAINDN": names.domaindn})
1117 message("Adding computers container")
1118 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1119 "DOMAINDN": names.domaindn})
1120 message("Modifying computers container")
1121 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1122 "DOMAINDN": names.domaindn})
1123 message("Setting up sam.ldb data")
1124 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1125 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1126 "DOMAINDN": names.domaindn,
1127 "NETBIOSNAME": names.netbiosname,
1128 "DEFAULTSITE": names.sitename,
1129 "CONFIGDN": names.configdn,
1130 "SERVERDN": names.serverdn,
1131 "POLICYGUID_DC": policyguid_dc
1134 if fill == FILL_FULL:
1135 message("Setting up sam.ldb users and groups")
1136 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1137 "DOMAINDN": names.domaindn,
1138 "DOMAINSID": str(domainsid),
1139 "CONFIGDN": names.configdn,
1140 "ADMINPASS_B64": b64encode(adminpass),
1141 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1144 if serverrole == "domain controller":
1145 message("Setting up self join")
1146 setup_self_join(samdb, names=names, invocationid=invocationid,
1148 machinepass=machinepass,
1149 domainsid=domainsid, policyguid=policyguid,
1150 policyguid_dc=policyguid_dc,
1151 setup_path=setup_path,
1152 domainControllerFunctionality=domainControllerFunctionality,
1155 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1156 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1157 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1158 assert isinstance(names.ntdsguid, str)
1161 samdb.transaction_cancel()
1164 samdb.transaction_commit()
1169 FILL_NT4SYNC = "NT4SYNC"
1173 def provision(setup_dir, message, session_info,
1174 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1176 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1178 domain=None, hostname=None, hostip=None, hostip6=None,
1179 domainsid=None, adminpass=None, ldapadminpass=None,
1180 krbtgtpass=None, domainguid=None,
1181 policyguid=None, policyguid_dc=None, invocationid=None,
1182 machinepass=None, ntdsguid=None,
1183 dnspass=None, root=None, nobody=None, users=None,
1184 wheel=None, backup=None, aci=None, serverrole=None,
1185 dom_for_fun_level=None,
1186 ldap_backend_extra_port=None, ldap_backend_type=None,
1188 ol_mmr_urls=None, ol_olc=None,
1189 setup_ds_path=None, slapd_path=None, nosync=False,
1190 ldap_dryrun_mode=False):
1193 :note: caution, this wipes all existing data!
1196 def setup_path(file):
1197 return os.path.join(setup_dir, file)
1199 if domainsid is None:
1200 domainsid = security.random_sid()
1202 domainsid = security.dom_sid(domainsid)
1204 # create/adapt the group policy GUIDs
1205 if policyguid is None:
1206 policyguid = str(uuid.uuid4())
1207 policyguid = policyguid.upper()
1208 if policyguid_dc is None:
1209 policyguid_dc = str(uuid.uuid4())
1210 policyguid_dc = policyguid_dc.upper()
1212 if adminpass is None:
1213 adminpass = glue.generate_random_str(12)
1214 if krbtgtpass is None:
1215 krbtgtpass = glue.generate_random_str(12)
1216 if machinepass is None:
1217 machinepass = glue.generate_random_str(12)
1219 dnspass = glue.generate_random_str(12)
1220 if ldapadminpass is None:
1221 #Make a new, random password between Samba and it's LDAP server
1222 ldapadminpass=glue.generate_random_str(12)
1225 root_uid = findnss_uid([root or "root"])
1226 nobody_uid = findnss_uid([nobody or "nobody"])
1227 users_gid = findnss_gid([users or "users"])
1229 wheel_gid = findnss_gid(["wheel", "adm"])
1231 wheel_gid = findnss_gid([wheel])
1233 if targetdir is not None:
1234 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1235 os.makedirs(os.path.join(targetdir, "etc"))
1236 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1237 elif smbconf is None:
1238 smbconf = param.default_path()
1240 # only install a new smb.conf if there isn't one there already
1241 if not os.path.exists(smbconf):
1242 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1245 lp = param.LoadParm()
1248 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1249 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1250 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1253 paths = provision_paths_from_lp(lp, names.dnsdomain)
1257 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1258 except socket.gaierror, (socket.EAI_NODATA, msg):
1263 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1264 except socket.gaierror, (socket.EAI_NODATA, msg):
1267 if serverrole is None:
1268 serverrole = lp.get("server role")
1270 assert serverrole in ("domain controller", "member server", "standalone")
1271 if invocationid is None and serverrole == "domain controller":
1272 invocationid = str(uuid.uuid4())
1274 if not os.path.exists(paths.private_dir):
1275 os.mkdir(paths.private_dir)
1277 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1279 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1280 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1282 secrets_credentials = credentials
1283 provision_backend = None
1284 if ldap_backend_type:
1285 # We only support an LDAP backend over ldapi://
1287 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1288 lp=lp, credentials=credentials,
1290 message=message, hostname=hostname,
1291 root=root, schema=schema,
1292 ldap_backend_type=ldap_backend_type,
1293 ldapadminpass=ldapadminpass,
1294 ldap_backend_extra_port=ldap_backend_extra_port,
1295 ol_mmr_urls=ol_mmr_urls,
1296 slapd_path=slapd_path,
1297 setup_ds_path=setup_ds_path,
1298 ldap_dryrun_mode=ldap_dryrun_mode)
1300 # Now use the backend credentials to access the databases
1301 credentials = provision_backend.credentials
1302 secrets_credentials = provision_backend.adminCredentials
1303 ldapi_url = provision_backend.ldapi_uri
1305 # only install a new shares config db if there is none
1306 if not os.path.exists(paths.shareconf):
1307 message("Setting up share.ldb")
1308 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1309 credentials=credentials, lp=lp)
1310 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1313 message("Setting up secrets.ldb")
1314 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1315 session_info=session_info,
1316 credentials=secrets_credentials, lp=lp)
1318 message("Setting up the registry")
1319 setup_registry(paths.hklm, setup_path, session_info,
1322 message("Setting up the privileges database")
1323 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1325 message("Setting up idmap db")
1326 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1329 message("Setting up SAM db")
1330 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1331 credentials=credentials, lp=lp, names=names,
1333 domainsid=domainsid,
1334 schema=schema, domainguid=domainguid,
1335 policyguid=policyguid, policyguid_dc=policyguid_dc,
1337 adminpass=adminpass, krbtgtpass=krbtgtpass,
1338 invocationid=invocationid,
1339 machinepass=machinepass, dnspass=dnspass,
1340 ntdsguid=ntdsguid, serverrole=serverrole,
1341 dom_for_fun_level=dom_for_fun_level,
1342 ldap_backend=provision_backend)
1344 if serverrole == "domain controller":
1345 if paths.netlogon is None:
1346 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1347 message("Please either remove %s or see the template at %s" %
1348 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1349 assert(paths.netlogon is not None)
1351 if paths.sysvol is None:
1352 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1353 message("Please either remove %s or see the template at %s" %
1354 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1355 assert(paths.sysvol is not None)
1357 # Set up group policies (domain policy and domain controller policy)
1359 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1360 "{" + policyguid + "}")
1361 os.makedirs(policy_path, 0755)
1362 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1363 "[General]\r\nVersion=65543")
1364 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1365 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1367 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1368 "{" + policyguid_dc + "}")
1369 os.makedirs(policy_path_dc, 0755)
1370 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1371 "[General]\r\nVersion=2")
1372 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1373 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1375 if not os.path.isdir(paths.netlogon):
1376 os.makedirs(paths.netlogon, 0755)
1378 if samdb_fill == FILL_FULL:
1379 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1380 root_uid=root_uid, nobody_uid=nobody_uid,
1381 users_gid=users_gid, wheel_gid=wheel_gid)
1383 message("Setting up sam.ldb rootDSE marking as synchronized")
1384 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1386 # Only make a zone file on the first DC, it should be replicated with DNS replication
1387 if serverrole == "domain controller":
1388 secretsdb_self_join(secrets_ldb, domain=domain,
1390 dnsdomain=names.dnsdomain,
1391 netbiosname=names.netbiosname,
1392 domainsid=domainsid,
1393 machinepass=machinepass,
1394 secure_channel_type=SEC_CHAN_BDC)
1396 secretsdb_setup_dns(secrets_ldb, setup_path,
1397 realm=names.realm, dnsdomain=names.dnsdomain,
1398 dns_keytab_path=paths.dns_keytab,
1401 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1402 assert isinstance(domainguid, str)
1404 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1406 hostip6=hostip6, hostname=names.hostname,
1408 domainguid=domainguid, ntdsguid=names.ntdsguid)
1410 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1411 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1413 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1414 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1415 keytab_name=paths.dns_keytab)
1416 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1417 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1419 create_krb5_conf(paths.krb5conf, setup_path,
1420 dnsdomain=names.dnsdomain, hostname=names.hostname,
1422 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1424 #Now commit the secrets.ldb to disk
1425 secrets_ldb.transaction_commit()
1427 if provision_backend is not None:
1428 if ldap_backend_type == "fedora-ds":
1429 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1431 # delete default SASL mappings
1432 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1434 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1435 for i in range (0, len(res)):
1436 dn = str(res[i]["dn"])
1439 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1442 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1444 m.dn = ldb.Dn(1, names.domaindn)
1447 m.dn = ldb.Dn(1, names.configdn)
1450 m.dn = ldb.Dn(1, names.schemadn)
1453 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1454 if provision_backend.slapd.poll() is None:
1456 if hasattr(provision_backend.slapd, "terminate"):
1457 provision_backend.slapd.terminate()
1459 # Older python versions don't have .terminate()
1461 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1463 #and now wait for it to die
1464 provision_backend.slapd.communicate()
1466 # now display slapd_command_file.txt to show how slapd must be started next time
1467 message("Use later the following commandline to start slapd, then Samba:")
1468 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1469 message(slapd_command)
1470 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1472 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1473 "SLAPD_COMMAND" : slapd_command})
1476 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1479 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1481 message("Once the above files are installed, your Samba4 server will be ready to use")
1482 message("Server Role: %s" % serverrole)
1483 message("Hostname: %s" % names.hostname)
1484 message("NetBIOS Domain: %s" % names.domain)
1485 message("DNS Domain: %s" % names.dnsdomain)
1486 message("DOMAIN SID: %s" % str(domainsid))
1487 if samdb_fill == FILL_FULL:
1488 message("Admin password: %s" % adminpass)
1489 if provision_backend:
1490 if provision_backend.credentials.get_bind_dn() is not None:
1491 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1493 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1495 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1497 result = ProvisionResult()
1498 result.domaindn = domaindn
1499 result.paths = paths
1501 result.samdb = samdb
1506 def provision_become_dc(setup_dir=None,
1507 smbconf=None, targetdir=None, realm=None,
1508 rootdn=None, domaindn=None, schemadn=None,
1509 configdn=None, serverdn=None,
1510 domain=None, hostname=None, domainsid=None,
1511 adminpass=None, krbtgtpass=None, domainguid=None,
1512 policyguid=None, policyguid_dc=None, invocationid=None,
1514 dnspass=None, root=None, nobody=None, users=None,
1515 wheel=None, backup=None, serverrole=None,
1516 ldap_backend=None, ldap_backend_type=None,
1517 sitename=None, debuglevel=1):
1520 """print a message if quiet is not set."""
1523 glue.set_debug_level(debuglevel)
1525 return provision(setup_dir, message, system_session(), None,
1526 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1527 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1528 configdn=configdn, serverdn=serverdn, domain=domain,
1529 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1530 machinepass=machinepass, serverrole="domain controller",
1534 def setup_db_config(setup_path, dbdir):
1535 """Setup a Berkeley database.
1537 :param setup_path: Setup path function.
1538 :param dbdir: Database directory."""
1539 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1540 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1541 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1542 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1544 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1545 {"LDAPDBDIR": dbdir})
1547 class ProvisionBackend(object):
1548 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1549 names=None, message=None,
1550 hostname=None, root=None,
1551 schema=None, ldapadminpass=None,
1552 ldap_backend_type=None, ldap_backend_extra_port=None,
1554 setup_ds_path=None, slapd_path=None,
1555 nosync=False, ldap_dryrun_mode=False):
1556 """Provision an LDAP backend for samba4
1558 This works for OpenLDAP and Fedora DS
1561 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1563 if not os.path.isdir(paths.ldapdir):
1564 os.makedirs(paths.ldapdir, 0700)
1566 if ldap_backend_type == "existing":
1567 #Check to see that this 'existing' LDAP backend in fact exists
1568 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1569 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1570 expression="(objectClass=OpenLDAProotDSE)")
1572 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1573 # This caused them to be set into the long-term database later in the script.
1574 self.credentials = credentials
1575 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1578 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1579 # if another instance of slapd is already running
1581 ldapi_db = Ldb(self.ldapi_uri)
1582 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1583 expression="(objectClass=OpenLDAProotDSE)");
1585 f = open(paths.slapdpid, "r")
1588 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1592 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1597 # Try to print helpful messages when the user has not specified the path to slapd
1598 if slapd_path is None:
1599 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1600 if not os.path.exists(slapd_path):
1601 message (slapd_path)
1602 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1604 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1606 os.unlink(schemadb_path)
1611 # Put the LDIF of the schema into a database so we can search on
1612 # it to generate schema-dependent configurations in Fedora DS and
1614 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1615 schema.ldb.connect(schemadb_path)
1616 schema.ldb.transaction_start()
1618 # These bits of LDIF are supplied when the Schema object is created
1619 schema.ldb.add_ldif(schema.schema_dn_add)
1620 schema.ldb.modify_ldif(schema.schema_dn_modify)
1621 schema.ldb.add_ldif(schema.schema_data)
1622 schema.ldb.transaction_commit()
1624 self.credentials = Credentials()
1625 self.credentials.guess(lp)
1626 #Kerberos to an ldapi:// backend makes no sense
1627 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1629 self.adminCredentials = Credentials()
1630 self.adminCredentials.guess(lp)
1631 #Kerberos to an ldapi:// backend makes no sense
1632 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1634 self.ldap_backend_type = ldap_backend_type
1636 if ldap_backend_type == "fedora-ds":
1637 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1638 names=names, message=message,
1640 ldapadminpass=ldapadminpass, root=root,
1642 ldap_backend_extra_port=ldap_backend_extra_port,
1643 setup_ds_path=setup_ds_path,
1644 slapd_path=slapd_path,
1646 ldap_dryrun_mode=ldap_dryrun_mode)
1648 elif ldap_backend_type == "openldap":
1649 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1650 names=names, message=message,
1652 ldapadminpass=ldapadminpass, root=root,
1654 ldap_backend_extra_port=ldap_backend_extra_port,
1655 ol_mmr_urls=ol_mmr_urls,
1656 slapd_path=slapd_path,
1658 ldap_dryrun_mode=ldap_dryrun_mode)
1660 raise ProvisioningError("Unknown LDAP backend type selected")
1662 self.credentials.set_password(ldapadminpass)
1663 self.adminCredentials.set_username("samba-admin")
1664 self.adminCredentials.set_password(ldapadminpass)
1666 # Now start the slapd, so we can provision onto it. We keep the
1667 # subprocess context around, to kill this off at the successful
1669 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1671 while self.slapd.poll() is None:
1672 # Wait until the socket appears
1674 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1675 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1676 expression="(objectClass=OpenLDAProotDSE)")
1677 # If we have got here, then we must have a valid connection to the LDAP server!
1683 raise ProvisioningError("slapd died before we could make a connection to it")
1686 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1688 hostname=None, ldapadminpass=None, root=None,
1690 ldap_backend_extra_port=None,
1692 slapd_path=None, nosync=False,
1693 ldap_dryrun_mode=False):
1695 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1698 nosync_config = "dbnosync"
1700 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1701 refint_attributes = ""
1702 memberof_config = "# Generated from Samba4 schema\n"
1703 for att in lnkattr.keys():
1704 if lnkattr[att] is not None:
1705 refint_attributes = refint_attributes + " " + att
1707 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1708 { "MEMBER_ATTR" : att ,
1709 "MEMBEROF_ATTR" : lnkattr[att] })
1711 refint_config = read_and_sub_file(setup_path("refint.conf"),
1712 { "LINK_ATTRS" : refint_attributes})
1714 attrs = ["linkID", "lDAPDisplayName"]
1715 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1717 for i in range (0, len(res)):
1718 index_attr = res[i]["lDAPDisplayName"][0]
1719 if index_attr == "objectGUID":
1720 index_attr = "entryUUID"
1722 index_config += "index " + index_attr + " eq\n"
1724 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1726 mmr_replicator_acl = ""
1727 mmr_serverids_config = ""
1728 mmr_syncrepl_schema_config = ""
1729 mmr_syncrepl_config_config = ""
1730 mmr_syncrepl_user_config = ""
1733 if ol_mmr_urls is not None:
1734 # For now, make these equal
1735 mmr_pass = ldapadminpass
1737 url_list=filter(None,ol_mmr_urls.split(' '))
1738 if (len(url_list) == 1):
1739 url_list=filter(None,ol_mmr_urls.split(','))
1742 mmr_on_config = "MirrorMode On"
1743 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1745 for url in url_list:
1747 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1748 { "SERVERID" : str(serverid),
1749 "LDAPSERVER" : url })
1752 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1754 "MMRDN": names.schemadn,
1756 "MMR_PASSWORD": mmr_pass})
1759 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1761 "MMRDN": names.configdn,
1763 "MMR_PASSWORD": mmr_pass})
1766 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1768 "MMRDN": names.domaindn,
1770 "MMR_PASSWORD": mmr_pass })
1771 # OpenLDAP cn=config initialisation
1772 olc_syncrepl_config = ""
1774 # if mmr = yes, generate cn=config-replication directives
1775 # and olc_seed.lif for the other mmr-servers
1776 if ol_mmr_urls is not None:
1778 olc_serverids_config = ""
1779 olc_syncrepl_seed_config = ""
1780 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1782 for url in url_list:
1784 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1785 { "SERVERID" : str(serverid),
1786 "LDAPSERVER" : url })
1789 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1792 "MMR_PASSWORD": mmr_pass})
1794 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1796 "LDAPSERVER" : url})
1798 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1799 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1800 "OLC_PW": ldapadminpass,
1801 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1804 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1805 {"DNSDOMAIN": names.dnsdomain,
1806 "LDAPDIR": paths.ldapdir,
1807 "DOMAINDN": names.domaindn,
1808 "CONFIGDN": names.configdn,
1809 "SCHEMADN": names.schemadn,
1810 "MEMBEROF_CONFIG": memberof_config,
1811 "MIRRORMODE": mmr_on_config,
1812 "REPLICATOR_ACL": mmr_replicator_acl,
1813 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1814 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1815 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1816 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1817 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1818 "OLC_MMR_CONFIG": olc_mmr_config,
1819 "REFINT_CONFIG": refint_config,
1820 "INDEX_CONFIG": index_config,
1821 "NOSYNC": nosync_config})
1823 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1824 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1825 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1827 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1828 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1830 setup_file(setup_path("cn=samba.ldif"),
1831 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1832 { "UUID": str(uuid.uuid4()),
1833 "LDAPTIME": timestring(int(time.time()))} )
1834 setup_file(setup_path("cn=samba-admin.ldif"),
1835 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1836 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1837 "UUID": str(uuid.uuid4()),
1838 "LDAPTIME": timestring(int(time.time()))} )
1840 if ol_mmr_urls is not None:
1841 setup_file(setup_path("cn=replicator.ldif"),
1842 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1843 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1844 "UUID": str(uuid.uuid4()),
1845 "LDAPTIME": timestring(int(time.time()))} )
1848 mapping = "schema-map-openldap-2.3"
1849 backend_schema = "backend-schema.schema"
1851 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1852 assert backend_schema_data is not None
1853 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1855 # now we generate the needed strings to start slapd automatically,
1856 # first ldapi_uri...
1857 if ldap_backend_extra_port is not None:
1858 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1859 # specified there as part of it's clue as to it's own name,
1860 # and not to replicate to itself
1861 if ol_mmr_urls is None:
1862 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1864 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1866 server_port_string = ""
1868 # Prepare the 'result' information - the commands to return in particular
1869 result.slapd_provision_command = [slapd_path]
1871 result.slapd_provision_command.append("-F" + paths.olcdir)
1873 result.slapd_provision_command.append("-h")
1875 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1876 result.slapd_command = list(result.slapd_provision_command)
1878 result.slapd_provision_command.append(result.ldapi_uri)
1879 result.slapd_provision_command.append("-d0")
1881 uris = result.ldapi_uri
1882 if server_port_string is not "":
1883 uris = uris + " " + server_port_string
1885 result.slapd_command.append(uris)
1887 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1888 result.credentials.set_username("samba-admin")
1890 # If we were just looking for crashes up to this point, it's a
1891 # good time to exit before we realise we don't have OpenLDAP on
1893 if ldap_dryrun_mode:
1896 # Finally, convert the configuration into cn=config style!
1897 if not os.path.isdir(paths.olcdir):
1898 os.makedirs(paths.olcdir, 0770)
1900 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1902 # We can't do this, as OpenLDAP is strange. It gives an error
1903 # output to the above, but does the conversion sucessfully...
1906 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1908 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1909 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1911 # Don't confuse the admin by leaving the slapd.conf around
1912 os.remove(paths.slapdconf)
1915 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1917 hostname=None, ldapadminpass=None, root=None,
1919 ldap_backend_extra_port=None,
1923 ldap_dryrun_mode=False):
1925 if ldap_backend_extra_port is not None:
1926 serverport = "ServerPort=%d" % ldap_backend_extra_port
1930 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1932 "HOSTNAME": hostname,
1933 "DNSDOMAIN": names.dnsdomain,
1934 "LDAPDIR": paths.ldapdir,
1935 "DOMAINDN": names.domaindn,
1936 "LDAPMANAGERDN": names.ldapmanagerdn,
1937 "LDAPMANAGERPASS": ldapadminpass,
1938 "SERVERPORT": serverport})
1940 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1941 {"CONFIGDN": names.configdn,
1942 "SCHEMADN": names.schemadn,
1943 "SAMBADN": names.sambadn,
1946 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1947 {"SAMBADN": names.sambadn,
1950 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1952 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1954 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1955 memberof_config = ""
1959 for attr in lnkattr.keys():
1960 if lnkattr[attr] is not None:
1961 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1962 { "ARG_NUMBER" : str(argnum) ,
1963 "LINK_ATTR" : attr })
1964 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1965 { "MEMBER_ATTR" : attr ,
1966 "MEMBEROF_ATTR" : lnkattr[attr] })
1967 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1971 open(paths.fedoradsrefint, 'w').write(refint_config)
1972 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
1974 attrs = ["lDAPDisplayName"]
1975 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1977 for i in range (0, len(res)):
1978 attr = res[i]["lDAPDisplayName"][0]
1980 if attr == "objectGUID":
1983 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1986 open(paths.fedoradsindex, 'w').write(index_config)
1988 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1989 {"SAMBADN": names.sambadn,
1990 "LDAPADMINPASS": ldapadminpass
1993 mapping = "schema-map-fedora-ds-1.0"
1994 backend_schema = "99_ad.ldif"
1996 # Build a schema file in Fedora DS format
1997 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1998 assert backend_schema_data is not None
1999 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2001 result.credentials.set_bind_dn(names.ldapmanagerdn)
2003 # Destory the target directory, or else setup-ds.pl will complain
2004 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
2005 shutil.rmtree(fedora_ds_dir, True)
2007 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2008 #In the 'provision' command line, stay in the foreground so we can easily kill it
2009 result.slapd_provision_command.append("-d0")
2011 #the command for the final run is the normal script
2012 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2014 # If we were just looking for crashes up to this point, it's a
2015 # good time to exit before we realise we don't have Fedora DS on
2016 if ldap_dryrun_mode:
2019 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2020 if setup_ds_path is None:
2021 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\"!")
2022 if not os.path.exists(setup_ds_path):
2023 message (setup_ds_path)
2024 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2026 # Run the Fedora DS setup utility
2027 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2029 raise ProvisioningError("setup-ds failed")
2032 retcode = subprocess.call([
2033 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2034 close_fds=True, shell=False)
2036 raise("ldib2db failed")
2038 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2039 """Create a PHP LDAP admin configuration file.
2041 :param path: Path to write the configuration to.
2042 :param setup_path: Function to generate setup paths.
2044 setup_file(setup_path("phpldapadmin-config.php"), path,
2045 {"S4_LDAPI_URI": ldapi_uri})
2048 def create_zone_file(path, setup_path, dnsdomain,
2049 hostip, hostip6, hostname, realm, domainguid,
2051 """Write out a DNS zone file, from the info in the current database.
2053 :param path: Path of the new zone file.
2054 :param setup_path: Setup path function.
2055 :param dnsdomain: DNS Domain name
2056 :param domaindn: DN of the Domain
2057 :param hostip: Local IPv4 IP
2058 :param hostip6: Local IPv6 IP
2059 :param hostname: Local hostname
2060 :param realm: Realm name
2061 :param domainguid: GUID of the domain.
2062 :param ntdsguid: GUID of the hosts nTDSDSA record.
2064 assert isinstance(domainguid, str)
2066 if hostip6 is not None:
2067 hostip6_base_line = " IN AAAA " + hostip6
2068 hostip6_host_line = hostname + " IN AAAA " + hostip6
2070 hostip6_base_line = ""
2071 hostip6_host_line = ""
2073 if hostip is not None:
2074 hostip_base_line = " IN A " + hostip
2075 hostip_host_line = hostname + " IN A " + hostip
2077 hostip_base_line = ""
2078 hostip_host_line = ""
2080 setup_file(setup_path("provision.zone"), path, {
2081 "HOSTNAME": hostname,
2082 "DNSDOMAIN": dnsdomain,
2084 "HOSTIP_BASE_LINE": hostip_base_line,
2085 "HOSTIP_HOST_LINE": hostip_host_line,
2086 "DOMAINGUID": domainguid,
2087 "DATESTRING": time.strftime("%Y%m%d%H"),
2088 "DEFAULTSITE": DEFAULTSITE,
2089 "NTDSGUID": ntdsguid,
2090 "HOSTIP6_BASE_LINE": hostip6_base_line,
2091 "HOSTIP6_HOST_LINE": hostip6_host_line,
2095 def create_named_conf(path, setup_path, realm, dnsdomain,
2097 """Write out a file containing zone statements suitable for inclusion in a
2098 named.conf file (including GSS-TSIG configuration).
2100 :param path: Path of the new named.conf file.
2101 :param setup_path: Setup path function.
2102 :param realm: Realm name
2103 :param dnsdomain: DNS Domain name
2104 :param private_dir: Path to private directory
2105 :param keytab_name: File name of DNS keytab file
2108 setup_file(setup_path("named.conf"), path, {
2109 "DNSDOMAIN": dnsdomain,
2111 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2112 "PRIVATE_DIR": private_dir
2115 def create_named_txt(path, setup_path, realm, dnsdomain,
2116 private_dir, keytab_name):
2117 """Write out a file containing zone statements suitable for inclusion in a
2118 named.conf file (including GSS-TSIG configuration).
2120 :param path: Path of the new named.conf file.
2121 :param setup_path: Setup path function.
2122 :param realm: Realm name
2123 :param dnsdomain: DNS Domain name
2124 :param private_dir: Path to private directory
2125 :param keytab_name: File name of DNS keytab file
2128 setup_file(setup_path("named.txt"), path, {
2129 "DNSDOMAIN": dnsdomain,
2131 "DNS_KEYTAB": keytab_name,
2132 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2133 "PRIVATE_DIR": private_dir
2136 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2137 """Write out a file containing zone statements suitable for inclusion in a
2138 named.conf file (including GSS-TSIG configuration).
2140 :param path: Path of the new named.conf file.
2141 :param setup_path: Setup path function.
2142 :param dnsdomain: DNS Domain name
2143 :param hostname: Local hostname
2144 :param realm: Realm name
2147 setup_file(setup_path("krb5.conf"), path, {
2148 "DNSDOMAIN": dnsdomain,
2149 "HOSTNAME": hostname,