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.
603 assert session_info is not None
605 old_partitions = None
607 # We use options=["modules:"] to stop the modules loading - we
608 # just want to wipe and re-initialise the database, not start it up
611 samdb = Ldb(url=samdb_path, session_info=session_info,
612 credentials=credentials, lp=lp, options=["modules:"])
613 res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*")
616 old_partitions = res[0]["partition"]
621 samdb.erase_except_schema_controlled()
623 os.unlink(samdb_path)
624 samdb = Ldb(url=samdb_path, session_info=session_info,
625 credentials=credentials, lp=lp, options=["modules:"])
627 samdb.erase_except_schema_controlled()
629 #Add modules to the list to activate them by default
630 #beware often order is important
632 # Some Known ordering constraints:
633 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
634 # - objectclass must be before password_hash, because password_hash checks
635 # that the objectclass is of type person (filled in by objectclass
636 # module when expanding the objectclass list)
637 # - partition must be last
638 # - each partition has its own module list then
639 modules_list = ["resolve_oids",
662 "extended_dn_out_ldb"]
663 modules_list2 = ["show_deleted",
666 ldap_backend_line = "# No LDAP backend"
667 if ldap_backend is not None:
668 ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri
670 if ldap_backend.ldap_backend_type == "fedora-ds":
671 backend_modules = ["nsuniqueid", "paged_searches"]
672 # We can handle linked attributes here, as we don't have directory-side subtree operations
673 tdb_modules_list = ["extended_dn_out_dereference"]
674 elif ldap_backend.ldap_backend_type == "openldap":
675 backend_modules = ["entryuuid", "paged_searches"]
676 # OpenLDAP handles subtree renames, so we don't want to do any of these things
677 tdb_modules_list = ["extended_dn_out_dereference"]
679 elif serverrole == "domain controller":
680 tdb_modules_list.insert(0, "repl_meta_data")
683 backend_modules = ["objectguid"]
685 if tdb_modules_list is None:
686 tdb_modules_list_as_string = ""
688 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
690 samdb.transaction_start()
692 message("Setting up sam.ldb partitions and settings")
693 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
694 "SCHEMADN": ldb.Dn(samdb, names.schemadn).get_casefold(),
695 "SCHEMADN_MOD2": ",objectguid",
696 "CONFIGDN": ldb.Dn(samdb, names.configdn).get_casefold(),
697 "DOMAINDN": ldb.Dn(samdb, names.domaindn).get_casefold(),
698 "SCHEMADN_MOD": "schema_fsmo",
699 "CONFIGDN_MOD": "naming_fsmo",
700 "DOMAINDN_MOD": "pdc_fsmo",
701 "MODULES_LIST": ",".join(modules_list),
702 "TDB_MODULES_LIST": tdb_modules_list_as_string,
703 "MODULES_LIST2": ",".join(modules_list2),
704 "BACKEND_MOD": ",".join(backend_modules),
705 "LDAP_BACKEND_LINE": ldap_backend_line,
709 if old_partitions is not None:
711 m.dn = ldb.Dn(samdb, "@PARTITION")
712 m["partition"] = ldb.MessageElement(old_partitions, ldb.FLAG_MOD_ADD, "partition")
715 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
717 message("Setting up sam.ldb rootDSE")
718 setup_samdb_rootdse(samdb, setup_path, names)
721 samdb.transaction_cancel()
724 samdb.transaction_commit()
727 def secretsdb_self_join(secretsdb, domain,
728 netbiosname, domainsid, machinepass,
729 realm=None, dnsdomain=None,
731 key_version_number=1,
732 secure_channel_type=SEC_CHAN_WKSTA):
733 """Add domain join-specific bits to a secrets database.
735 :param secretsdb: Ldb Handle to the secrets database
736 :param machinepass: Machine password
738 attrs=["whenChanged",
746 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
747 msg["secureChannelType"] = str(secure_channel_type)
748 msg["flatname"] = [domain]
749 msg["objectClass"] = ["top", "primaryDomain"]
750 if realm is not None:
751 if dnsdomain is None:
752 dnsdomain = realm.lower()
753 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
755 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
756 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
757 msg["privateKeytab"] = ["secrets.keytab"];
760 msg["secret"] = [machinepass]
761 msg["samAccountName"] = ["%s$" % netbiosname]
762 msg["secureChannelType"] = [str(secure_channel_type)]
763 msg["objectSid"] = [ndr_pack(domainsid)]
765 res = secretsdb.search(base="cn=Primary Domains",
767 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
768 scope=SCOPE_ONELEVEL)
771 if del_msg.dn is not msg.dn:
772 secretsdb.delete(del_msg.dn)
774 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
777 msg["priorSecret"] = res[0]["secret"]
778 msg["priorWhenChanged"] = res[0]["whenChanged"]
780 if res["privateKeytab"] is not None:
781 msg["privateKeytab"] = res[0]["privateKeytab"]
783 if res["krb5Keytab"] is not None:
784 msg["krb5Keytab"] = res[0]["krb5Keytab"]
787 el.set_flags(ldb.FLAG_MOD_REPLACE)
788 secretsdb.modify(msg)
793 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
794 dns_keytab_path, dnspass):
795 """Add DNS specific bits to a secrets database.
797 :param secretsdb: Ldb Handle to the secrets database
798 :param setup_path: Setup path function
799 :param machinepass: Machine password
801 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
803 "DNSDOMAIN": dnsdomain,
804 "DNS_KEYTAB": dns_keytab_path,
805 "DNSPASS_B64": b64encode(dnspass),
809 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
810 """Setup the secrets database.
812 :param path: Path to the secrets database.
813 :param setup_path: Get the path to a setup file.
814 :param session_info: Session info.
815 :param credentials: Credentials
816 :param lp: Loadparm context
817 :return: LDB handle for the created secrets database
819 if os.path.exists(path):
821 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
824 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
825 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
827 secrets_ldb.transaction_start()
828 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
830 if credentials is not None and credentials.authentication_requested():
831 if credentials.get_bind_dn() is not None:
832 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
833 "LDAPMANAGERDN": credentials.get_bind_dn(),
834 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
837 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
838 "LDAPADMINUSER": credentials.get_username(),
839 "LDAPADMINREALM": credentials.get_realm(),
840 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
845 def setup_privileges(path, setup_path, session_info, lp):
846 """Setup the privileges database.
848 :param path: Path to the privileges database.
849 :param setup_path: Get the path to a setup file.
850 :param session_info: Session info.
851 :param credentials: Credentials
852 :param lp: Loadparm context
853 :return: LDB handle for the created secrets database
855 if os.path.exists(path):
857 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
858 privilege_ldb.erase()
859 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
862 def setup_registry(path, setup_path, session_info, lp):
863 """Setup the registry.
865 :param path: Path to the registry database
866 :param setup_path: Function that returns the path to a setup.
867 :param session_info: Session information
868 :param credentials: Credentials
869 :param lp: Loadparm context
871 reg = registry.Registry()
872 hive = registry.open_ldb(path, session_info=session_info,
874 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
875 provision_reg = setup_path("provision.reg")
876 assert os.path.exists(provision_reg)
877 reg.diff_apply(provision_reg)
880 def setup_idmapdb(path, setup_path, session_info, lp):
881 """Setup the idmap database.
883 :param path: path to the idmap database
884 :param setup_path: Function that returns a path to a setup file
885 :param session_info: Session information
886 :param credentials: Credentials
887 :param lp: Loadparm context
889 if os.path.exists(path):
892 idmap_ldb = IDmapDB(path, session_info=session_info,
896 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
900 def setup_samdb_rootdse(samdb, setup_path, names):
901 """Setup the SamDB rootdse.
903 :param samdb: Sam Database handle
904 :param setup_path: Obtain setup path
906 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
907 "SCHEMADN": names.schemadn,
908 "NETBIOSNAME": names.netbiosname,
909 "DNSDOMAIN": names.dnsdomain,
910 "REALM": names.realm,
911 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
912 "DOMAINDN": names.domaindn,
913 "ROOTDN": names.rootdn,
914 "CONFIGDN": names.configdn,
915 "SERVERDN": names.serverdn,
919 def setup_self_join(samdb, names,
920 machinepass, dnspass,
921 domainsid, invocationid, setup_path,
922 policyguid, policyguid_dc, domainControllerFunctionality,
924 """Join a host to its own domain."""
925 assert isinstance(invocationid, str)
926 if ntdsguid is not None:
927 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
930 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
931 "CONFIGDN": names.configdn,
932 "SCHEMADN": names.schemadn,
933 "DOMAINDN": names.domaindn,
934 "SERVERDN": names.serverdn,
935 "INVOCATIONID": invocationid,
936 "NETBIOSNAME": names.netbiosname,
937 "DEFAULTSITE": names.sitename,
938 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
939 "MACHINEPASS_B64": b64encode(machinepass),
940 "DNSPASS_B64": b64encode(dnspass),
941 "REALM": names.realm,
942 "DOMAIN": names.domain,
943 "DNSDOMAIN": names.dnsdomain,
944 "SAMBA_VERSION_STRING": version,
945 "NTDSGUID": ntdsguid_line,
946 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
948 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
949 "POLICYGUID": policyguid,
950 "POLICYGUID_DC": policyguid_dc,
951 "DNSDOMAIN": names.dnsdomain,
952 "DOMAINSID": str(domainsid),
953 "DOMAINDN": names.domaindn})
955 # add the NTDSGUID based SPNs
956 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
957 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
958 expression="", scope=SCOPE_BASE)
959 assert isinstance(names.ntdsguid, str)
961 # Setup fSMORoleOwner entries to point at the newly created DC entry
962 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
963 "DOMAIN": names.domain,
964 "DNSDOMAIN": names.dnsdomain,
965 "DOMAINDN": names.domaindn,
966 "CONFIGDN": names.configdn,
967 "SCHEMADN": names.schemadn,
968 "DEFAULTSITE": names.sitename,
969 "SERVERDN": names.serverdn,
970 "NETBIOSNAME": names.netbiosname,
971 "NTDSGUID": names.ntdsguid
975 def setup_samdb(path, setup_path, session_info, credentials, lp,
977 domainsid, domainguid, policyguid, policyguid_dc,
978 fill, adminpass, krbtgtpass,
979 machinepass, invocationid, dnspass, ntdsguid,
980 serverrole, dom_for_fun_level=None,
981 schema=None, ldap_backend=None):
982 """Setup a complete SAM Database.
984 :note: This will wipe the main SAM database file!
987 # ATTENTION: Do NOT change these default values without discussion with the
988 # team and/or release manager. They have a big impact on the whole program!
989 domainControllerFunctionality = DS_DC_FUNCTION_2008
991 if dom_for_fun_level is None:
992 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
993 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
994 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
996 if dom_for_fun_level > domainControllerFunctionality:
997 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!")
999 domainFunctionality = dom_for_fun_level
1000 forestFunctionality = dom_for_fun_level
1002 # Also wipes the database
1003 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
1004 credentials=credentials, session_info=session_info,
1005 names=names, ldap_backend=ldap_backend,
1006 serverrole=serverrole)
1008 if (schema == None):
1009 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1010 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
1012 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1013 samdb = Ldb(session_info=session_info,
1014 credentials=credentials, lp=lp)
1016 message("Pre-loading the Samba 4 and AD schema")
1018 # Load the schema from the one we computed earlier
1019 samdb.set_schema_from_ldb(schema.ldb)
1021 # And now we can connect to the DB - the schema won't be loaded from the DB
1024 if fill == FILL_DRS:
1027 samdb.transaction_start()
1029 message("Erasing data from partitions")
1030 # Load the schema (again). This time it will force a reindex,
1031 # and will therefore make the erase_partitions() below
1032 # computationally sane
1033 samdb.set_schema_from_ldb(schema.ldb)
1034 samdb.erase_partitions()
1036 # Set the domain functionality levels onto the database.
1037 # Various module (the password_hash module in particular) need
1038 # to know what level of AD we are emulating.
1040 # These will be fixed into the database via the database
1041 # modifictions below, but we need them set from the start.
1042 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1043 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1044 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1046 samdb.set_domain_sid(str(domainsid))
1047 if serverrole == "domain controller":
1048 samdb.set_invocation_id(invocationid)
1050 message("Adding DomainDN: %s" % names.domaindn)
1052 #impersonate domain admin
1053 admin_session_info = admin_session(lp, str(domainsid))
1054 samdb.set_session_info(admin_session_info)
1055 if domainguid is not None:
1056 domainguid_line = "objectGUID: %s\n-" % domainguid
1058 domainguid_line = ""
1059 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1060 "DOMAINDN": names.domaindn,
1061 "DOMAINGUID": domainguid_line
1065 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1066 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1067 "DOMAINSID": str(domainsid),
1068 "SCHEMADN": names.schemadn,
1069 "NETBIOSNAME": names.netbiosname,
1070 "DEFAULTSITE": names.sitename,
1071 "CONFIGDN": names.configdn,
1072 "SERVERDN": names.serverdn,
1073 "POLICYGUID": policyguid,
1074 "DOMAINDN": names.domaindn,
1075 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1076 "SAMBA_VERSION_STRING": version
1079 message("Adding configuration container")
1080 descr = get_config_descriptor(domainsid);
1081 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1082 "CONFIGDN": names.configdn,
1083 "DESCRIPTOR": descr,
1085 message("Modifying configuration container")
1086 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1087 "CONFIGDN": names.configdn,
1088 "SCHEMADN": names.schemadn,
1091 # The LDIF here was created when the Schema object was constructed
1092 message("Setting up sam.ldb schema")
1093 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1094 samdb.modify_ldif(schema.schema_dn_modify)
1095 samdb.write_prefixes_from_schema()
1096 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1097 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1098 {"SCHEMADN": names.schemadn})
1100 message("Setting up sam.ldb configuration data")
1101 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1102 "CONFIGDN": names.configdn,
1103 "NETBIOSNAME": names.netbiosname,
1104 "DEFAULTSITE": names.sitename,
1105 "DNSDOMAIN": names.dnsdomain,
1106 "DOMAIN": names.domain,
1107 "SCHEMADN": names.schemadn,
1108 "DOMAINDN": names.domaindn,
1109 "SERVERDN": names.serverdn,
1110 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1113 message("Setting up display specifiers")
1114 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1115 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1116 check_all_substituted(display_specifiers_ldif)
1117 samdb.add_ldif(display_specifiers_ldif)
1119 message("Adding users container")
1120 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1121 "DOMAINDN": names.domaindn})
1122 message("Modifying users container")
1123 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1124 "DOMAINDN": names.domaindn})
1125 message("Adding computers container")
1126 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1127 "DOMAINDN": names.domaindn})
1128 message("Modifying computers container")
1129 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1130 "DOMAINDN": names.domaindn})
1131 message("Setting up sam.ldb data")
1132 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1133 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1134 "DOMAINDN": names.domaindn,
1135 "NETBIOSNAME": names.netbiosname,
1136 "DEFAULTSITE": names.sitename,
1137 "CONFIGDN": names.configdn,
1138 "SERVERDN": names.serverdn,
1139 "POLICYGUID_DC": policyguid_dc
1142 if fill == FILL_FULL:
1143 message("Setting up sam.ldb users and groups")
1144 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1145 "DOMAINDN": names.domaindn,
1146 "DOMAINSID": str(domainsid),
1147 "CONFIGDN": names.configdn,
1148 "ADMINPASS_B64": b64encode(adminpass),
1149 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1152 if serverrole == "domain controller":
1153 message("Setting up self join")
1154 setup_self_join(samdb, names=names, invocationid=invocationid,
1156 machinepass=machinepass,
1157 domainsid=domainsid, policyguid=policyguid,
1158 policyguid_dc=policyguid_dc,
1159 setup_path=setup_path,
1160 domainControllerFunctionality=domainControllerFunctionality,
1163 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1164 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1165 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1166 assert isinstance(names.ntdsguid, str)
1169 samdb.transaction_cancel()
1172 samdb.transaction_commit()
1177 FILL_NT4SYNC = "NT4SYNC"
1181 def provision(setup_dir, message, session_info,
1182 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1184 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1186 domain=None, hostname=None, hostip=None, hostip6=None,
1187 domainsid=None, adminpass=None, ldapadminpass=None,
1188 krbtgtpass=None, domainguid=None,
1189 policyguid=None, policyguid_dc=None, invocationid=None,
1190 machinepass=None, ntdsguid=None,
1191 dnspass=None, root=None, nobody=None, users=None,
1192 wheel=None, backup=None, aci=None, serverrole=None,
1193 dom_for_fun_level=None,
1194 ldap_backend_extra_port=None, ldap_backend_type=None,
1196 ol_mmr_urls=None, ol_olc=None,
1197 setup_ds_path=None, slapd_path=None, nosync=False,
1198 ldap_dryrun_mode=False):
1201 :note: caution, this wipes all existing data!
1204 def setup_path(file):
1205 return os.path.join(setup_dir, file)
1207 if domainsid is None:
1208 domainsid = security.random_sid()
1210 domainsid = security.dom_sid(domainsid)
1212 # create/adapt the group policy GUIDs
1213 if policyguid is None:
1214 policyguid = str(uuid.uuid4())
1215 policyguid = policyguid.upper()
1216 if policyguid_dc is None:
1217 policyguid_dc = str(uuid.uuid4())
1218 policyguid_dc = policyguid_dc.upper()
1220 if adminpass is None:
1221 adminpass = glue.generate_random_str(12)
1222 if krbtgtpass is None:
1223 krbtgtpass = glue.generate_random_str(12)
1224 if machinepass is None:
1225 machinepass = glue.generate_random_str(12)
1227 dnspass = glue.generate_random_str(12)
1228 if ldapadminpass is None:
1229 #Make a new, random password between Samba and it's LDAP server
1230 ldapadminpass=glue.generate_random_str(12)
1233 root_uid = findnss_uid([root or "root"])
1234 nobody_uid = findnss_uid([nobody or "nobody"])
1235 users_gid = findnss_gid([users or "users"])
1237 wheel_gid = findnss_gid(["wheel", "adm"])
1239 wheel_gid = findnss_gid([wheel])
1241 if targetdir is not None:
1242 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1243 os.makedirs(os.path.join(targetdir, "etc"))
1244 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1245 elif smbconf is None:
1246 smbconf = param.default_path()
1248 # only install a new smb.conf if there isn't one there already
1249 if not os.path.exists(smbconf):
1250 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1253 lp = param.LoadParm()
1256 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1257 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1258 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1261 paths = provision_paths_from_lp(lp, names.dnsdomain)
1265 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1266 except socket.gaierror, (socket.EAI_NODATA, msg):
1271 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1272 except socket.gaierror, (socket.EAI_NODATA, msg):
1275 if serverrole is None:
1276 serverrole = lp.get("server role")
1278 assert serverrole in ("domain controller", "member server", "standalone")
1279 if invocationid is None and serverrole == "domain controller":
1280 invocationid = str(uuid.uuid4())
1282 if not os.path.exists(paths.private_dir):
1283 os.mkdir(paths.private_dir)
1285 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1287 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1288 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1290 secrets_credentials = credentials
1291 provision_backend = None
1292 if ldap_backend_type:
1293 # We only support an LDAP backend over ldapi://
1295 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1296 lp=lp, credentials=credentials,
1298 message=message, hostname=hostname,
1299 root=root, schema=schema,
1300 ldap_backend_type=ldap_backend_type,
1301 ldapadminpass=ldapadminpass,
1302 ldap_backend_extra_port=ldap_backend_extra_port,
1303 ol_mmr_urls=ol_mmr_urls,
1304 slapd_path=slapd_path,
1305 setup_ds_path=setup_ds_path,
1306 ldap_dryrun_mode=ldap_dryrun_mode)
1308 # Now use the backend credentials to access the databases
1309 credentials = provision_backend.credentials
1310 secrets_credentials = provision_backend.adminCredentials
1311 ldapi_url = provision_backend.ldapi_uri
1313 # only install a new shares config db if there is none
1314 if not os.path.exists(paths.shareconf):
1315 message("Setting up share.ldb")
1316 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1317 credentials=credentials, lp=lp)
1318 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1321 message("Setting up secrets.ldb")
1322 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1323 session_info=session_info,
1324 credentials=secrets_credentials, lp=lp)
1326 message("Setting up the registry")
1327 setup_registry(paths.hklm, setup_path, session_info,
1330 message("Setting up the privileges database")
1331 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1333 message("Setting up idmap db")
1334 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1337 message("Setting up SAM db")
1338 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1339 credentials=credentials, lp=lp, names=names,
1341 domainsid=domainsid,
1342 schema=schema, domainguid=domainguid,
1343 policyguid=policyguid, policyguid_dc=policyguid_dc,
1345 adminpass=adminpass, krbtgtpass=krbtgtpass,
1346 invocationid=invocationid,
1347 machinepass=machinepass, dnspass=dnspass,
1348 ntdsguid=ntdsguid, serverrole=serverrole,
1349 dom_for_fun_level=dom_for_fun_level,
1350 ldap_backend=provision_backend)
1352 if serverrole == "domain controller":
1353 if paths.netlogon is None:
1354 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1355 message("Please either remove %s or see the template at %s" %
1356 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1357 assert(paths.netlogon is not None)
1359 if paths.sysvol is None:
1360 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1361 message("Please either remove %s or see the template at %s" %
1362 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1363 assert(paths.sysvol is not None)
1365 # Set up group policies (domain policy and domain controller policy)
1367 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1368 "{" + policyguid + "}")
1369 os.makedirs(policy_path, 0755)
1370 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1371 "[General]\r\nVersion=65543")
1372 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1373 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1375 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1376 "{" + policyguid_dc + "}")
1377 os.makedirs(policy_path_dc, 0755)
1378 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1379 "[General]\r\nVersion=2")
1380 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1381 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1383 if not os.path.isdir(paths.netlogon):
1384 os.makedirs(paths.netlogon, 0755)
1386 if samdb_fill == FILL_FULL:
1387 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1388 root_uid=root_uid, nobody_uid=nobody_uid,
1389 users_gid=users_gid, wheel_gid=wheel_gid)
1391 message("Setting up sam.ldb rootDSE marking as synchronized")
1392 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1394 # Only make a zone file on the first DC, it should be replicated with DNS replication
1395 if serverrole == "domain controller":
1396 secretsdb_self_join(secrets_ldb, domain=domain,
1398 dnsdomain=names.dnsdomain,
1399 netbiosname=names.netbiosname,
1400 domainsid=domainsid,
1401 machinepass=machinepass,
1402 secure_channel_type=SEC_CHAN_BDC)
1404 secretsdb_setup_dns(secrets_ldb, setup_path,
1405 realm=names.realm, dnsdomain=names.dnsdomain,
1406 dns_keytab_path=paths.dns_keytab,
1409 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1410 assert isinstance(domainguid, str)
1412 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1414 hostip6=hostip6, hostname=names.hostname,
1416 domainguid=domainguid, ntdsguid=names.ntdsguid)
1418 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1419 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1421 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1422 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1423 keytab_name=paths.dns_keytab)
1424 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1425 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1427 create_krb5_conf(paths.krb5conf, setup_path,
1428 dnsdomain=names.dnsdomain, hostname=names.hostname,
1430 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1432 #Now commit the secrets.ldb to disk
1433 secrets_ldb.transaction_commit()
1435 if provision_backend is not None:
1436 if ldap_backend_type == "fedora-ds":
1437 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1439 # delete default SASL mappings
1440 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1442 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1443 for i in range (0, len(res)):
1444 dn = str(res[i]["dn"])
1447 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1450 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1452 m.dn = ldb.Dn(1, names.domaindn)
1455 m.dn = ldb.Dn(1, names.configdn)
1458 m.dn = ldb.Dn(1, names.schemadn)
1461 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1462 if provision_backend.slapd.poll() is None:
1464 if hasattr(provision_backend.slapd, "terminate"):
1465 provision_backend.slapd.terminate()
1467 # Older python versions don't have .terminate()
1469 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1471 #and now wait for it to die
1472 provision_backend.slapd.communicate()
1474 # now display slapd_command_file.txt to show how slapd must be started next time
1475 message("Use later the following commandline to start slapd, then Samba:")
1476 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1477 message(slapd_command)
1478 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1480 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1481 "SLAPD_COMMAND" : slapd_command})
1484 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1487 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1489 message("Once the above files are installed, your Samba4 server will be ready to use")
1490 message("Server Role: %s" % serverrole)
1491 message("Hostname: %s" % names.hostname)
1492 message("NetBIOS Domain: %s" % names.domain)
1493 message("DNS Domain: %s" % names.dnsdomain)
1494 message("DOMAIN SID: %s" % str(domainsid))
1495 if samdb_fill == FILL_FULL:
1496 message("Admin password: %s" % adminpass)
1497 if provision_backend:
1498 if provision_backend.credentials.get_bind_dn() is not None:
1499 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1501 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1503 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1505 result = ProvisionResult()
1506 result.domaindn = domaindn
1507 result.paths = paths
1509 result.samdb = samdb
1514 def provision_become_dc(setup_dir=None,
1515 smbconf=None, targetdir=None, realm=None,
1516 rootdn=None, domaindn=None, schemadn=None,
1517 configdn=None, serverdn=None,
1518 domain=None, hostname=None, domainsid=None,
1519 adminpass=None, krbtgtpass=None, domainguid=None,
1520 policyguid=None, policyguid_dc=None, invocationid=None,
1522 dnspass=None, root=None, nobody=None, users=None,
1523 wheel=None, backup=None, serverrole=None,
1524 ldap_backend=None, ldap_backend_type=None,
1525 sitename=None, debuglevel=1):
1528 """print a message if quiet is not set."""
1531 glue.set_debug_level(debuglevel)
1533 return provision(setup_dir, message, system_session(), None,
1534 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1535 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1536 configdn=configdn, serverdn=serverdn, domain=domain,
1537 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1538 machinepass=machinepass, serverrole="domain controller",
1542 def setup_db_config(setup_path, dbdir):
1543 """Setup a Berkeley database.
1545 :param setup_path: Setup path function.
1546 :param dbdir: Database directory."""
1547 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1548 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1549 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1550 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1552 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1553 {"LDAPDBDIR": dbdir})
1555 class ProvisionBackend(object):
1556 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1557 names=None, message=None,
1558 hostname=None, root=None,
1559 schema=None, ldapadminpass=None,
1560 ldap_backend_type=None, ldap_backend_extra_port=None,
1562 setup_ds_path=None, slapd_path=None,
1563 nosync=False, ldap_dryrun_mode=False):
1564 """Provision an LDAP backend for samba4
1566 This works for OpenLDAP and Fedora DS
1569 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1571 if not os.path.isdir(paths.ldapdir):
1572 os.makedirs(paths.ldapdir, 0700)
1574 if ldap_backend_type == "existing":
1575 #Check to see that this 'existing' LDAP backend in fact exists
1576 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1577 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1578 expression="(objectClass=OpenLDAProotDSE)")
1580 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1581 # This caused them to be set into the long-term database later in the script.
1582 self.credentials = credentials
1583 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1586 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1587 # if another instance of slapd is already running
1589 ldapi_db = Ldb(self.ldapi_uri)
1590 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1591 expression="(objectClass=OpenLDAProotDSE)");
1593 f = open(paths.slapdpid, "r")
1596 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1600 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1605 # Try to print helpful messages when the user has not specified the path to slapd
1606 if slapd_path is None:
1607 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1608 if not os.path.exists(slapd_path):
1609 message (slapd_path)
1610 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1612 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1614 os.unlink(schemadb_path)
1619 # Put the LDIF of the schema into a database so we can search on
1620 # it to generate schema-dependent configurations in Fedora DS and
1622 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1623 schema.ldb.connect(schemadb_path)
1624 schema.ldb.transaction_start()
1626 # These bits of LDIF are supplied when the Schema object is created
1627 schema.ldb.add_ldif(schema.schema_dn_add)
1628 schema.ldb.modify_ldif(schema.schema_dn_modify)
1629 schema.ldb.add_ldif(schema.schema_data)
1630 schema.ldb.transaction_commit()
1632 self.credentials = Credentials()
1633 self.credentials.guess(lp)
1634 #Kerberos to an ldapi:// backend makes no sense
1635 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1637 self.adminCredentials = Credentials()
1638 self.adminCredentials.guess(lp)
1639 #Kerberos to an ldapi:// backend makes no sense
1640 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1642 self.ldap_backend_type = ldap_backend_type
1644 if ldap_backend_type == "fedora-ds":
1645 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1646 names=names, message=message,
1648 ldapadminpass=ldapadminpass, root=root,
1650 ldap_backend_extra_port=ldap_backend_extra_port,
1651 setup_ds_path=setup_ds_path,
1652 slapd_path=slapd_path,
1654 ldap_dryrun_mode=ldap_dryrun_mode)
1656 elif ldap_backend_type == "openldap":
1657 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1658 names=names, message=message,
1660 ldapadminpass=ldapadminpass, root=root,
1662 ldap_backend_extra_port=ldap_backend_extra_port,
1663 ol_mmr_urls=ol_mmr_urls,
1664 slapd_path=slapd_path,
1666 ldap_dryrun_mode=ldap_dryrun_mode)
1668 raise ProvisioningError("Unknown LDAP backend type selected")
1670 self.credentials.set_password(ldapadminpass)
1671 self.adminCredentials.set_username("samba-admin")
1672 self.adminCredentials.set_password(ldapadminpass)
1674 # Now start the slapd, so we can provision onto it. We keep the
1675 # subprocess context around, to kill this off at the successful
1677 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1679 while self.slapd.poll() is None:
1680 # Wait until the socket appears
1682 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1683 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1684 expression="(objectClass=OpenLDAProotDSE)")
1685 # If we have got here, then we must have a valid connection to the LDAP server!
1691 raise ProvisioningError("slapd died before we could make a connection to it")
1694 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1696 hostname=None, ldapadminpass=None, root=None,
1698 ldap_backend_extra_port=None,
1700 slapd_path=None, nosync=False,
1701 ldap_dryrun_mode=False):
1703 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1706 nosync_config = "dbnosync"
1708 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1709 refint_attributes = ""
1710 memberof_config = "# Generated from Samba4 schema\n"
1711 for att in lnkattr.keys():
1712 if lnkattr[att] is not None:
1713 refint_attributes = refint_attributes + " " + att
1715 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1716 { "MEMBER_ATTR" : att ,
1717 "MEMBEROF_ATTR" : lnkattr[att] })
1719 refint_config = read_and_sub_file(setup_path("refint.conf"),
1720 { "LINK_ATTRS" : refint_attributes})
1722 attrs = ["linkID", "lDAPDisplayName"]
1723 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1725 for i in range (0, len(res)):
1726 index_attr = res[i]["lDAPDisplayName"][0]
1727 if index_attr == "objectGUID":
1728 index_attr = "entryUUID"
1730 index_config += "index " + index_attr + " eq\n"
1732 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1734 mmr_replicator_acl = ""
1735 mmr_serverids_config = ""
1736 mmr_syncrepl_schema_config = ""
1737 mmr_syncrepl_config_config = ""
1738 mmr_syncrepl_user_config = ""
1741 if ol_mmr_urls is not None:
1742 # For now, make these equal
1743 mmr_pass = ldapadminpass
1745 url_list=filter(None,ol_mmr_urls.split(' '))
1746 if (len(url_list) == 1):
1747 url_list=filter(None,ol_mmr_urls.split(','))
1750 mmr_on_config = "MirrorMode On"
1751 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1753 for url in url_list:
1755 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1756 { "SERVERID" : str(serverid),
1757 "LDAPSERVER" : url })
1760 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1762 "MMRDN": names.schemadn,
1764 "MMR_PASSWORD": mmr_pass})
1767 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1769 "MMRDN": names.configdn,
1771 "MMR_PASSWORD": mmr_pass})
1774 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1776 "MMRDN": names.domaindn,
1778 "MMR_PASSWORD": mmr_pass })
1779 # OpenLDAP cn=config initialisation
1780 olc_syncrepl_config = ""
1782 # if mmr = yes, generate cn=config-replication directives
1783 # and olc_seed.lif for the other mmr-servers
1784 if ol_mmr_urls is not None:
1786 olc_serverids_config = ""
1787 olc_syncrepl_seed_config = ""
1788 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1790 for url in url_list:
1792 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1793 { "SERVERID" : str(serverid),
1794 "LDAPSERVER" : url })
1797 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1800 "MMR_PASSWORD": mmr_pass})
1802 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1804 "LDAPSERVER" : url})
1806 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1807 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1808 "OLC_PW": ldapadminpass,
1809 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1812 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1813 {"DNSDOMAIN": names.dnsdomain,
1814 "LDAPDIR": paths.ldapdir,
1815 "DOMAINDN": names.domaindn,
1816 "CONFIGDN": names.configdn,
1817 "SCHEMADN": names.schemadn,
1818 "MEMBEROF_CONFIG": memberof_config,
1819 "MIRRORMODE": mmr_on_config,
1820 "REPLICATOR_ACL": mmr_replicator_acl,
1821 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1822 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1823 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1824 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1825 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1826 "OLC_MMR_CONFIG": olc_mmr_config,
1827 "REFINT_CONFIG": refint_config,
1828 "INDEX_CONFIG": index_config,
1829 "NOSYNC": nosync_config})
1831 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1832 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1833 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1835 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1836 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1838 setup_file(setup_path("cn=samba.ldif"),
1839 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1840 { "UUID": str(uuid.uuid4()),
1841 "LDAPTIME": timestring(int(time.time()))} )
1842 setup_file(setup_path("cn=samba-admin.ldif"),
1843 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1844 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1845 "UUID": str(uuid.uuid4()),
1846 "LDAPTIME": timestring(int(time.time()))} )
1848 if ol_mmr_urls is not None:
1849 setup_file(setup_path("cn=replicator.ldif"),
1850 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1851 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1852 "UUID": str(uuid.uuid4()),
1853 "LDAPTIME": timestring(int(time.time()))} )
1856 mapping = "schema-map-openldap-2.3"
1857 backend_schema = "backend-schema.schema"
1859 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1860 assert backend_schema_data is not None
1861 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1863 # now we generate the needed strings to start slapd automatically,
1864 # first ldapi_uri...
1865 if ldap_backend_extra_port is not None:
1866 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1867 # specified there as part of it's clue as to it's own name,
1868 # and not to replicate to itself
1869 if ol_mmr_urls is None:
1870 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1872 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1874 server_port_string = ""
1876 # Prepare the 'result' information - the commands to return in particular
1877 result.slapd_provision_command = [slapd_path]
1879 result.slapd_provision_command.append("-F" + paths.olcdir)
1881 result.slapd_provision_command.append("-h")
1883 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1884 result.slapd_command = list(result.slapd_provision_command)
1886 result.slapd_provision_command.append(result.ldapi_uri)
1887 result.slapd_provision_command.append("-d0")
1889 uris = result.ldapi_uri
1890 if server_port_string is not "":
1891 uris = uris + " " + server_port_string
1893 result.slapd_command.append(uris)
1895 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1896 result.credentials.set_username("samba-admin")
1898 # If we were just looking for crashes up to this point, it's a
1899 # good time to exit before we realise we don't have OpenLDAP on
1901 if ldap_dryrun_mode:
1904 # Finally, convert the configuration into cn=config style!
1905 if not os.path.isdir(paths.olcdir):
1906 os.makedirs(paths.olcdir, 0770)
1908 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1910 # We can't do this, as OpenLDAP is strange. It gives an error
1911 # output to the above, but does the conversion sucessfully...
1914 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1916 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1917 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1919 # Don't confuse the admin by leaving the slapd.conf around
1920 os.remove(paths.slapdconf)
1923 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1925 hostname=None, ldapadminpass=None, root=None,
1927 ldap_backend_extra_port=None,
1931 ldap_dryrun_mode=False):
1933 if ldap_backend_extra_port is not None:
1934 serverport = "ServerPort=%d" % ldap_backend_extra_port
1938 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1940 "HOSTNAME": hostname,
1941 "DNSDOMAIN": names.dnsdomain,
1942 "LDAPDIR": paths.ldapdir,
1943 "DOMAINDN": names.domaindn,
1944 "LDAPMANAGERDN": names.ldapmanagerdn,
1945 "LDAPMANAGERPASS": ldapadminpass,
1946 "SERVERPORT": serverport})
1948 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1949 {"CONFIGDN": names.configdn,
1950 "SCHEMADN": names.schemadn,
1951 "SAMBADN": names.sambadn,
1954 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1955 {"SAMBADN": names.sambadn,
1958 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1960 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1962 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1963 memberof_config = ""
1967 for attr in lnkattr.keys():
1968 if lnkattr[attr] is not None:
1969 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1970 { "ARG_NUMBER" : str(argnum) ,
1971 "LINK_ATTR" : attr })
1972 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1973 { "MEMBER_ATTR" : attr ,
1974 "MEMBEROF_ATTR" : lnkattr[attr] })
1975 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1979 open(paths.fedoradsrefint, 'w').write(refint_config)
1980 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
1982 attrs = ["lDAPDisplayName"]
1983 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1985 for i in range (0, len(res)):
1986 attr = res[i]["lDAPDisplayName"][0]
1988 if attr == "objectGUID":
1991 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1994 open(paths.fedoradsindex, 'w').write(index_config)
1996 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1997 {"SAMBADN": names.sambadn,
1998 "LDAPADMINPASS": ldapadminpass
2001 mapping = "schema-map-fedora-ds-1.0"
2002 backend_schema = "99_ad.ldif"
2004 # Build a schema file in Fedora DS format
2005 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
2006 assert backend_schema_data is not None
2007 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2009 result.credentials.set_bind_dn(names.ldapmanagerdn)
2011 # Destory the target directory, or else setup-ds.pl will complain
2012 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
2013 shutil.rmtree(fedora_ds_dir, True)
2015 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2016 #In the 'provision' command line, stay in the foreground so we can easily kill it
2017 result.slapd_provision_command.append("-d0")
2019 #the command for the final run is the normal script
2020 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2022 # If we were just looking for crashes up to this point, it's a
2023 # good time to exit before we realise we don't have Fedora DS on
2024 if ldap_dryrun_mode:
2027 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2028 if setup_ds_path is None:
2029 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\"!")
2030 if not os.path.exists(setup_ds_path):
2031 message (setup_ds_path)
2032 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2034 # Run the Fedora DS setup utility
2035 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2037 raise ProvisioningError("setup-ds failed")
2040 retcode = subprocess.call([
2041 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2042 close_fds=True, shell=False)
2044 raise("ldib2db failed")
2046 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2047 """Create a PHP LDAP admin configuration file.
2049 :param path: Path to write the configuration to.
2050 :param setup_path: Function to generate setup paths.
2052 setup_file(setup_path("phpldapadmin-config.php"), path,
2053 {"S4_LDAPI_URI": ldapi_uri})
2056 def create_zone_file(path, setup_path, dnsdomain,
2057 hostip, hostip6, hostname, realm, domainguid,
2059 """Write out a DNS zone file, from the info in the current database.
2061 :param path: Path of the new zone file.
2062 :param setup_path: Setup path function.
2063 :param dnsdomain: DNS Domain name
2064 :param domaindn: DN of the Domain
2065 :param hostip: Local IPv4 IP
2066 :param hostip6: Local IPv6 IP
2067 :param hostname: Local hostname
2068 :param realm: Realm name
2069 :param domainguid: GUID of the domain.
2070 :param ntdsguid: GUID of the hosts nTDSDSA record.
2072 assert isinstance(domainguid, str)
2074 if hostip6 is not None:
2075 hostip6_base_line = " IN AAAA " + hostip6
2076 hostip6_host_line = hostname + " IN AAAA " + hostip6
2078 hostip6_base_line = ""
2079 hostip6_host_line = ""
2081 if hostip is not None:
2082 hostip_base_line = " IN A " + hostip
2083 hostip_host_line = hostname + " IN A " + hostip
2085 hostip_base_line = ""
2086 hostip_host_line = ""
2088 setup_file(setup_path("provision.zone"), path, {
2089 "HOSTNAME": hostname,
2090 "DNSDOMAIN": dnsdomain,
2092 "HOSTIP_BASE_LINE": hostip_base_line,
2093 "HOSTIP_HOST_LINE": hostip_host_line,
2094 "DOMAINGUID": domainguid,
2095 "DATESTRING": time.strftime("%Y%m%d%H"),
2096 "DEFAULTSITE": DEFAULTSITE,
2097 "NTDSGUID": ntdsguid,
2098 "HOSTIP6_BASE_LINE": hostip6_base_line,
2099 "HOSTIP6_HOST_LINE": hostip6_host_line,
2103 def create_named_conf(path, setup_path, realm, dnsdomain,
2105 """Write out a file containing zone statements suitable for inclusion in a
2106 named.conf file (including GSS-TSIG configuration).
2108 :param path: Path of the new named.conf file.
2109 :param setup_path: Setup path function.
2110 :param realm: Realm name
2111 :param dnsdomain: DNS Domain name
2112 :param private_dir: Path to private directory
2113 :param keytab_name: File name of DNS keytab file
2116 setup_file(setup_path("named.conf"), path, {
2117 "DNSDOMAIN": dnsdomain,
2119 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2120 "PRIVATE_DIR": private_dir
2123 def create_named_txt(path, setup_path, realm, dnsdomain,
2124 private_dir, keytab_name):
2125 """Write out a file containing zone statements suitable for inclusion in a
2126 named.conf file (including GSS-TSIG configuration).
2128 :param path: Path of the new named.conf file.
2129 :param setup_path: Setup path function.
2130 :param realm: Realm name
2131 :param dnsdomain: DNS Domain name
2132 :param private_dir: Path to private directory
2133 :param keytab_name: File name of DNS keytab file
2136 setup_file(setup_path("named.txt"), path, {
2137 "DNSDOMAIN": dnsdomain,
2139 "DNS_KEYTAB": keytab_name,
2140 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2141 "PRIVATE_DIR": private_dir
2144 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2145 """Write out a file containing zone statements suitable for inclusion in a
2146 named.conf file (including GSS-TSIG configuration).
2148 :param path: Path of the new named.conf file.
2149 :param setup_path: Setup path function.
2150 :param dnsdomain: DNS Domain name
2151 :param hostname: Local hostname
2152 :param realm: Realm name
2155 setup_file(setup_path("krb5.conf"), path, {
2156 "DNSDOMAIN": dnsdomain,
2157 "HOSTNAME": hostname,