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]
439 netbiosname = lp.get("netbios name")
440 if netbiosname is None:
441 netbiosname = hostname
442 assert netbiosname is not None
443 netbiosname = netbiosname.upper()
444 if not valid_netbios_name(netbiosname):
445 raise InvalidNetbiosName(netbiosname)
447 if dnsdomain is None:
448 dnsdomain = lp.get("realm")
449 assert dnsdomain is not None
450 dnsdomain = dnsdomain.lower()
452 if serverrole is None:
453 serverrole = lp.get("server role")
454 assert serverrole is not None
455 serverrole = serverrole.lower()
457 realm = dnsdomain.upper()
459 if lp.get("realm").upper() != realm:
460 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
462 if serverrole == "domain controller":
464 domain = lp.get("workgroup")
465 assert domain is not None
466 domain = domain.upper()
468 if lp.get("workgroup").upper() != domain:
469 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
472 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
476 domaindn = "DC=" + netbiosname
478 if not valid_netbios_name(domain):
479 raise InvalidNetbiosName(domain)
481 if hostname.upper() == realm:
482 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
483 if netbiosname == realm:
484 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
486 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
492 configdn = "CN=Configuration," + rootdn
494 schemadn = "CN=Schema," + configdn
501 names = ProvisionNames()
502 names.rootdn = rootdn
503 names.domaindn = domaindn
504 names.configdn = configdn
505 names.schemadn = schemadn
506 names.sambadn = sambadn
507 names.ldapmanagerdn = "CN=Manager," + rootdn
508 names.dnsdomain = dnsdomain
509 names.domain = domain
511 names.netbiosname = netbiosname
512 names.hostname = hostname
513 names.sitename = sitename
514 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
519 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
521 """Create a new smb.conf file based on a couple of basic settings.
523 assert smbconf is not None
525 hostname = socket.gethostname().split(".")[0]
526 netbiosname = hostname.upper()
528 if serverrole is None:
529 serverrole = "standalone"
531 assert serverrole in ("domain controller", "member server", "standalone")
532 if serverrole == "domain controller":
534 elif serverrole == "member server":
535 smbconfsuffix = "member"
536 elif serverrole == "standalone":
537 smbconfsuffix = "standalone"
539 assert domain is not None
540 domain = domain.upper()
542 assert realm is not None
543 realm = realm.upper()
545 default_lp = param.LoadParm()
546 #Load non-existant file
547 if os.path.exists(smbconf):
548 default_lp.load(smbconf)
550 if targetdir is not None:
551 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
552 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
554 default_lp.set("lock dir", os.path.abspath(targetdir))
559 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
560 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
562 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
564 "NETBIOS_NAME": netbiosname,
567 "SERVERROLE": serverrole,
568 "NETLOGONPATH": netlogon,
569 "SYSVOLPATH": sysvol,
570 "PRIVATEDIR_LINE": privatedir_line,
571 "LOCKDIR_LINE": lockdir_line
575 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
576 users_gid, wheel_gid):
577 """setup reasonable name mappings for sam names to unix names.
579 :param samdb: SamDB object.
580 :param idmap: IDmap db object.
581 :param sid: The domain sid.
582 :param domaindn: The domain DN.
583 :param root_uid: uid of the UNIX root user.
584 :param nobody_uid: uid of the UNIX nobody user.
585 :param users_gid: gid of the UNIX users group.
586 :param wheel_gid: gid of the UNIX wheel group."""
588 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
589 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
591 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
592 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
594 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
595 credentials, names, schema,
596 serverrole, ldap_backend=None,
598 """Setup the partitions for the SAM database.
600 Alternatively, provision() may call this, and then populate the database.
602 :note: This will wipe the Sam Database!
604 :note: This function always removes the local SAM LDB file. The erase
605 parameter controls whether to erase the existing data, which
606 may not be stored locally but in LDAP.
609 assert session_info is not None
611 old_partitions = None
612 new_partitions = None
614 # We use options=["modules:"] to stop the modules loading - we
615 # just want to wipe and re-initialise the database, not start it up
618 samdb = Ldb(url=samdb_path, session_info=session_info,
619 credentials=credentials, lp=lp, options=["modules:"])
620 res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*")
623 old_partitions = res[0]["partition"]
627 if old_partitions is not None:
629 for old_partition in old_partitions:
630 new_partition = old_partition
631 if old_partition.endswith(".ldb"):
632 p = old_partition.split(":")[0]
633 dn = ldb.Dn(schema.ldb, p)
634 new_partition = dn.get_casefold()
635 new_partitions.append(new_partition)
638 samdb.erase_except_schema_controlled()
640 os.unlink(samdb_path)
641 samdb = Ldb(url=samdb_path, session_info=session_info,
642 credentials=credentials, lp=lp, options=["modules:"])
644 samdb.erase_except_schema_controlled()
646 #Add modules to the list to activate them by default
647 #beware often order is important
649 # Some Known ordering constraints:
650 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
651 # - objectclass must be before password_hash, because password_hash checks
652 # that the objectclass is of type person (filled in by objectclass
653 # module when expanding the objectclass list)
654 # - partition must be last
655 # - each partition has its own module list then
656 modules_list = ["resolve_oids",
679 "extended_dn_out_ldb"]
680 modules_list2 = ["show_deleted",
684 ldap_backend_line = "# No LDAP backend"
685 if ldap_backend is not None:
686 ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri
688 if ldap_backend.ldap_backend_type == "fedora-ds":
689 backend_modules = ["nsuniqueid", "paged_searches"]
690 # We can handle linked attributes here, as we don't have directory-side subtree operations
691 tdb_modules_list = ["extended_dn_out_fds"]
692 elif ldap_backend.ldap_backend_type == "openldap":
693 backend_modules = ["entryuuid", "paged_searches"]
694 # OpenLDAP handles subtree renames, so we don't want to do any of these things
695 tdb_modules_list = ["extended_dn_out_openldap"]
697 elif serverrole == "domain controller":
698 tdb_modules_list.insert(0, "repl_meta_data")
701 backend_modules = ["objectguid"]
703 if tdb_modules_list is None:
704 tdb_modules_list_as_string = ""
706 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
708 samdb.transaction_start()
710 message("Setting up sam.ldb partitions and settings")
711 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
712 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
713 "SCHEMADN_MOD2": ",objectguid",
714 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
715 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
716 "SCHEMADN_MOD": "schema_data",
717 "CONFIGDN_MOD": "naming_fsmo",
718 "DOMAINDN_MOD": "pdc_fsmo",
719 "MODULES_LIST": ",".join(modules_list),
720 "TDB_MODULES_LIST": tdb_modules_list_as_string,
721 "MODULES_LIST2": ",".join(modules_list2),
722 "BACKEND_MOD": ",".join(backend_modules),
723 "LDAP_BACKEND_LINE": ldap_backend_line,
727 if new_partitions is not None:
729 m.dn = ldb.Dn(samdb, "@PARTITION")
731 m["partition"] = ldb.MessageElement(new_partitions, ldb.FLAG_MOD_ADD, "partition")
734 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
736 message("Setting up sam.ldb rootDSE")
737 setup_samdb_rootdse(samdb, setup_path, names)
740 samdb.transaction_cancel()
743 samdb.transaction_commit()
746 def secretsdb_self_join(secretsdb, domain,
747 netbiosname, domainsid, machinepass,
748 realm=None, dnsdomain=None,
750 key_version_number=1,
751 secure_channel_type=SEC_CHAN_WKSTA):
752 """Add domain join-specific bits to a secrets database.
754 :param secretsdb: Ldb Handle to the secrets database
755 :param machinepass: Machine password
757 attrs=["whenChanged",
765 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
766 msg["secureChannelType"] = str(secure_channel_type)
767 msg["flatname"] = [domain]
768 msg["objectClass"] = ["top", "primaryDomain"]
769 if realm is not None:
770 if dnsdomain is None:
771 dnsdomain = realm.lower()
772 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
774 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
775 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
776 msg["privateKeytab"] = ["secrets.keytab"];
779 msg["secret"] = [machinepass]
780 msg["samAccountName"] = ["%s$" % netbiosname]
781 msg["secureChannelType"] = [str(secure_channel_type)]
782 msg["objectSid"] = [ndr_pack(domainsid)]
784 res = secretsdb.search(base="cn=Primary Domains",
786 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
787 scope=SCOPE_ONELEVEL)
790 if del_msg.dn is not msg.dn:
791 secretsdb.delete(del_msg.dn)
793 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
796 msg["priorSecret"] = res[0]["secret"]
797 msg["priorWhenChanged"] = res[0]["whenChanged"]
799 if res["privateKeytab"] is not None:
800 msg["privateKeytab"] = res[0]["privateKeytab"]
802 if res["krb5Keytab"] is not None:
803 msg["krb5Keytab"] = res[0]["krb5Keytab"]
806 el.set_flags(ldb.FLAG_MOD_REPLACE)
807 secretsdb.modify(msg)
812 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
813 dns_keytab_path, dnspass):
814 """Add DNS specific bits to a secrets database.
816 :param secretsdb: Ldb Handle to the secrets database
817 :param setup_path: Setup path function
818 :param machinepass: Machine password
820 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
822 "DNSDOMAIN": dnsdomain,
823 "DNS_KEYTAB": dns_keytab_path,
824 "DNSPASS_B64": b64encode(dnspass),
828 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
829 """Setup the secrets database.
831 :param path: Path to the secrets database.
832 :param setup_path: Get the path to a setup file.
833 :param session_info: Session info.
834 :param credentials: Credentials
835 :param lp: Loadparm context
836 :return: LDB handle for the created secrets database
838 if os.path.exists(path):
840 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
843 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
844 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
846 secrets_ldb.transaction_start()
847 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
849 if credentials is not None and credentials.authentication_requested():
850 if credentials.get_bind_dn() is not None:
851 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
852 "LDAPMANAGERDN": credentials.get_bind_dn(),
853 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
856 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
857 "LDAPADMINUSER": credentials.get_username(),
858 "LDAPADMINREALM": credentials.get_realm(),
859 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
864 def setup_privileges(path, setup_path, session_info, lp):
865 """Setup the privileges database.
867 :param path: Path to the privileges database.
868 :param setup_path: Get the path to a setup file.
869 :param session_info: Session info.
870 :param credentials: Credentials
871 :param lp: Loadparm context
872 :return: LDB handle for the created secrets database
874 if os.path.exists(path):
876 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
877 privilege_ldb.erase()
878 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
881 def setup_registry(path, setup_path, session_info, lp):
882 """Setup the registry.
884 :param path: Path to the registry database
885 :param setup_path: Function that returns the path to a setup.
886 :param session_info: Session information
887 :param credentials: Credentials
888 :param lp: Loadparm context
890 reg = registry.Registry()
891 hive = registry.open_ldb(path, session_info=session_info,
893 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
894 provision_reg = setup_path("provision.reg")
895 assert os.path.exists(provision_reg)
896 reg.diff_apply(provision_reg)
899 def setup_idmapdb(path, setup_path, session_info, lp):
900 """Setup the idmap database.
902 :param path: path to the idmap database
903 :param setup_path: Function that returns a path to a setup file
904 :param session_info: Session information
905 :param credentials: Credentials
906 :param lp: Loadparm context
908 if os.path.exists(path):
911 idmap_ldb = IDmapDB(path, session_info=session_info,
915 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
919 def setup_samdb_rootdse(samdb, setup_path, names):
920 """Setup the SamDB rootdse.
922 :param samdb: Sam Database handle
923 :param setup_path: Obtain setup path
925 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
926 "SCHEMADN": names.schemadn,
927 "NETBIOSNAME": names.netbiosname,
928 "DNSDOMAIN": names.dnsdomain,
929 "REALM": names.realm,
930 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
931 "DOMAINDN": names.domaindn,
932 "ROOTDN": names.rootdn,
933 "CONFIGDN": names.configdn,
934 "SERVERDN": names.serverdn,
938 def setup_self_join(samdb, names,
939 machinepass, dnspass,
940 domainsid, invocationid, setup_path,
941 policyguid, policyguid_dc, domainControllerFunctionality,
943 """Join a host to its own domain."""
944 assert isinstance(invocationid, str)
945 if ntdsguid is not None:
946 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
949 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
950 "CONFIGDN": names.configdn,
951 "SCHEMADN": names.schemadn,
952 "DOMAINDN": names.domaindn,
953 "SERVERDN": names.serverdn,
954 "INVOCATIONID": invocationid,
955 "NETBIOSNAME": names.netbiosname,
956 "DEFAULTSITE": names.sitename,
957 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
958 "MACHINEPASS_B64": b64encode(machinepass),
959 "DNSPASS_B64": b64encode(dnspass),
960 "REALM": names.realm,
961 "DOMAIN": names.domain,
962 "DNSDOMAIN": names.dnsdomain,
963 "SAMBA_VERSION_STRING": version,
964 "NTDSGUID": ntdsguid_line,
965 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
967 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
968 "POLICYGUID": policyguid,
969 "POLICYGUID_DC": policyguid_dc,
970 "DNSDOMAIN": names.dnsdomain,
971 "DOMAINSID": str(domainsid),
972 "DOMAINDN": names.domaindn})
974 # add the NTDSGUID based SPNs
975 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
976 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
977 expression="", scope=SCOPE_BASE)
978 assert isinstance(names.ntdsguid, str)
980 # Setup fSMORoleOwner entries to point at the newly created DC entry
981 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
982 "DOMAIN": names.domain,
983 "DNSDOMAIN": names.dnsdomain,
984 "DOMAINDN": names.domaindn,
985 "CONFIGDN": names.configdn,
986 "SCHEMADN": names.schemadn,
987 "DEFAULTSITE": names.sitename,
988 "SERVERDN": names.serverdn,
989 "NETBIOSNAME": names.netbiosname,
990 "NTDSGUID": names.ntdsguid
994 def setup_samdb(path, setup_path, session_info, credentials, lp,
996 domainsid, domainguid, policyguid, policyguid_dc,
997 fill, adminpass, krbtgtpass,
998 machinepass, invocationid, dnspass, ntdsguid,
999 serverrole, dom_for_fun_level=None,
1000 schema=None, ldap_backend=None):
1001 """Setup a complete SAM Database.
1003 :note: This will wipe the main SAM database file!
1006 # ATTENTION: Do NOT change these default values without discussion with the
1007 # team and/or release manager. They have a big impact on the whole program!
1008 domainControllerFunctionality = DS_DC_FUNCTION_2008
1010 if dom_for_fun_level is None:
1011 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1012 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
1013 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
1015 if dom_for_fun_level > domainControllerFunctionality:
1016 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!")
1018 domainFunctionality = dom_for_fun_level
1019 forestFunctionality = dom_for_fun_level
1021 # Also wipes the database
1022 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
1023 credentials=credentials, session_info=session_info,
1024 names=names, ldap_backend=ldap_backend,
1025 serverrole=serverrole, schema=schema)
1027 if (schema == None):
1028 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1029 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
1031 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1032 samdb = Ldb(session_info=session_info,
1033 credentials=credentials, lp=lp)
1035 message("Pre-loading the Samba 4 and AD schema")
1037 # Load the schema from the one we computed earlier
1038 samdb.set_schema_from_ldb(schema.ldb)
1040 # And now we can connect to the DB - the schema won't be loaded from the DB
1043 if fill == FILL_DRS:
1046 samdb.transaction_start()
1048 message("Erasing data from partitions")
1049 # Load the schema (again). This time it will force a reindex,
1050 # and will therefore make the erase_partitions() below
1051 # computationally sane
1052 samdb.set_schema_from_ldb(schema.ldb)
1053 samdb.erase_partitions()
1055 # Set the domain functionality levels onto the database.
1056 # Various module (the password_hash module in particular) need
1057 # to know what level of AD we are emulating.
1059 # These will be fixed into the database via the database
1060 # modifictions below, but we need them set from the start.
1061 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1062 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1063 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1065 samdb.set_domain_sid(str(domainsid))
1066 if serverrole == "domain controller":
1067 samdb.set_invocation_id(invocationid)
1069 message("Adding DomainDN: %s" % names.domaindn)
1071 #impersonate domain admin
1072 admin_session_info = admin_session(lp, str(domainsid))
1073 samdb.set_session_info(admin_session_info)
1074 if domainguid is not None:
1075 domainguid_line = "objectGUID: %s\n-" % domainguid
1077 domainguid_line = ""
1078 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1079 "DOMAINDN": names.domaindn,
1080 "DOMAINGUID": domainguid_line
1084 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1085 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1086 "DOMAINSID": str(domainsid),
1087 "SCHEMADN": names.schemadn,
1088 "NETBIOSNAME": names.netbiosname,
1089 "DEFAULTSITE": names.sitename,
1090 "CONFIGDN": names.configdn,
1091 "SERVERDN": names.serverdn,
1092 "POLICYGUID": policyguid,
1093 "DOMAINDN": names.domaindn,
1094 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1095 "SAMBA_VERSION_STRING": version
1098 message("Adding configuration container")
1099 descr = get_config_descriptor(domainsid);
1100 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1101 "CONFIGDN": names.configdn,
1102 "DESCRIPTOR": descr,
1104 message("Modifying configuration container")
1105 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1106 "CONFIGDN": names.configdn,
1107 "SCHEMADN": names.schemadn,
1110 # The LDIF here was created when the Schema object was constructed
1111 message("Setting up sam.ldb schema")
1112 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1113 samdb.modify_ldif(schema.schema_dn_modify)
1114 samdb.write_prefixes_from_schema()
1115 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1116 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1117 {"SCHEMADN": names.schemadn})
1119 message("Setting up sam.ldb configuration data")
1120 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1121 "CONFIGDN": names.configdn,
1122 "NETBIOSNAME": names.netbiosname,
1123 "DEFAULTSITE": names.sitename,
1124 "DNSDOMAIN": names.dnsdomain,
1125 "DOMAIN": names.domain,
1126 "SCHEMADN": names.schemadn,
1127 "DOMAINDN": names.domaindn,
1128 "SERVERDN": names.serverdn,
1129 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1132 message("Setting up display specifiers")
1133 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1134 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1135 check_all_substituted(display_specifiers_ldif)
1136 samdb.add_ldif(display_specifiers_ldif)
1138 message("Adding users container")
1139 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1140 "DOMAINDN": names.domaindn})
1141 message("Modifying users container")
1142 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1143 "DOMAINDN": names.domaindn})
1144 message("Adding computers container")
1145 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1146 "DOMAINDN": names.domaindn})
1147 message("Modifying computers container")
1148 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1149 "DOMAINDN": names.domaindn})
1150 message("Setting up sam.ldb data")
1151 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1152 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1153 "DOMAINDN": names.domaindn,
1154 "NETBIOSNAME": names.netbiosname,
1155 "DEFAULTSITE": names.sitename,
1156 "CONFIGDN": names.configdn,
1157 "SERVERDN": names.serverdn,
1158 "POLICYGUID_DC": policyguid_dc
1161 if fill == FILL_FULL:
1162 message("Setting up sam.ldb users and groups")
1163 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1164 "DOMAINDN": names.domaindn,
1165 "DOMAINSID": str(domainsid),
1166 "CONFIGDN": names.configdn,
1167 "ADMINPASS_B64": b64encode(adminpass),
1168 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1171 if serverrole == "domain controller":
1172 message("Setting up self join")
1173 setup_self_join(samdb, names=names, invocationid=invocationid,
1175 machinepass=machinepass,
1176 domainsid=domainsid, policyguid=policyguid,
1177 policyguid_dc=policyguid_dc,
1178 setup_path=setup_path,
1179 domainControllerFunctionality=domainControllerFunctionality,
1182 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1183 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1184 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1185 assert isinstance(names.ntdsguid, str)
1188 samdb.transaction_cancel()
1191 samdb.transaction_commit()
1196 FILL_NT4SYNC = "NT4SYNC"
1200 def provision(setup_dir, message, session_info,
1201 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1203 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1205 domain=None, hostname=None, hostip=None, hostip6=None,
1206 domainsid=None, adminpass=None, ldapadminpass=None,
1207 krbtgtpass=None, domainguid=None,
1208 policyguid=None, policyguid_dc=None, invocationid=None,
1209 machinepass=None, ntdsguid=None,
1210 dnspass=None, root=None, nobody=None, users=None,
1211 wheel=None, backup=None, aci=None, serverrole=None,
1212 dom_for_fun_level=None,
1213 ldap_backend_extra_port=None, ldap_backend_type=None,
1215 ol_mmr_urls=None, ol_olc=None,
1216 setup_ds_path=None, slapd_path=None, nosync=False,
1217 ldap_dryrun_mode=False):
1220 :note: caution, this wipes all existing data!
1223 def setup_path(file):
1224 return os.path.join(setup_dir, file)
1226 if domainsid is None:
1227 domainsid = security.random_sid()
1229 domainsid = security.dom_sid(domainsid)
1231 # create/adapt the group policy GUIDs
1232 if policyguid is None:
1233 policyguid = str(uuid.uuid4())
1234 policyguid = policyguid.upper()
1235 if policyguid_dc is None:
1236 policyguid_dc = str(uuid.uuid4())
1237 policyguid_dc = policyguid_dc.upper()
1239 if adminpass is None:
1240 adminpass = glue.generate_random_str(12)
1241 if krbtgtpass is None:
1242 krbtgtpass = glue.generate_random_str(12)
1243 if machinepass is None:
1244 machinepass = glue.generate_random_str(12)
1246 dnspass = glue.generate_random_str(12)
1247 if ldapadminpass is None:
1248 #Make a new, random password between Samba and it's LDAP server
1249 ldapadminpass=glue.generate_random_str(12)
1252 root_uid = findnss_uid([root or "root"])
1253 nobody_uid = findnss_uid([nobody or "nobody"])
1254 users_gid = findnss_gid([users or "users"])
1256 wheel_gid = findnss_gid(["wheel", "adm"])
1258 wheel_gid = findnss_gid([wheel])
1260 if targetdir is not None:
1261 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1262 os.makedirs(os.path.join(targetdir, "etc"))
1263 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1264 elif smbconf is None:
1265 smbconf = param.default_path()
1267 # only install a new smb.conf if there isn't one there already
1268 if not os.path.exists(smbconf):
1269 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1272 lp = param.LoadParm()
1275 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1276 dnsdomain=realm, serverrole=serverrole,
1277 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1278 serverdn=serverdn, sitename=sitename)
1280 paths = provision_paths_from_lp(lp, names.dnsdomain)
1284 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1285 except socket.gaierror, (socket.EAI_NODATA, msg):
1290 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1291 except socket.gaierror, (socket.EAI_NODATA, msg):
1294 if serverrole is None:
1295 serverrole = lp.get("server role")
1297 assert serverrole in ("domain controller", "member server", "standalone")
1298 if invocationid is None and serverrole == "domain controller":
1299 invocationid = str(uuid.uuid4())
1301 if not os.path.exists(paths.private_dir):
1302 os.mkdir(paths.private_dir)
1304 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1306 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1307 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1309 secrets_credentials = credentials
1310 provision_backend = None
1311 if ldap_backend_type:
1312 # We only support an LDAP backend over ldapi://
1314 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1315 lp=lp, credentials=credentials,
1317 message=message, hostname=hostname,
1318 root=root, schema=schema,
1319 ldap_backend_type=ldap_backend_type,
1320 ldapadminpass=ldapadminpass,
1321 ldap_backend_extra_port=ldap_backend_extra_port,
1322 ol_mmr_urls=ol_mmr_urls,
1323 slapd_path=slapd_path,
1324 setup_ds_path=setup_ds_path,
1325 ldap_dryrun_mode=ldap_dryrun_mode)
1327 # Now use the backend credentials to access the databases
1328 credentials = provision_backend.credentials
1329 secrets_credentials = provision_backend.adminCredentials
1330 ldapi_url = provision_backend.ldapi_uri
1332 # only install a new shares config db if there is none
1333 if not os.path.exists(paths.shareconf):
1334 message("Setting up share.ldb")
1335 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1336 credentials=credentials, lp=lp)
1337 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1340 message("Setting up secrets.ldb")
1341 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1342 session_info=session_info,
1343 credentials=secrets_credentials, lp=lp)
1345 message("Setting up the registry")
1346 setup_registry(paths.hklm, setup_path, session_info,
1349 message("Setting up the privileges database")
1350 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1352 message("Setting up idmap db")
1353 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1356 message("Setting up SAM db")
1357 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1358 credentials=credentials, lp=lp, names=names,
1360 domainsid=domainsid,
1361 schema=schema, domainguid=domainguid,
1362 policyguid=policyguid, policyguid_dc=policyguid_dc,
1364 adminpass=adminpass, krbtgtpass=krbtgtpass,
1365 invocationid=invocationid,
1366 machinepass=machinepass, dnspass=dnspass,
1367 ntdsguid=ntdsguid, serverrole=serverrole,
1368 dom_for_fun_level=dom_for_fun_level,
1369 ldap_backend=provision_backend)
1371 if serverrole == "domain controller":
1372 if paths.netlogon is None:
1373 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1374 message("Please either remove %s or see the template at %s" %
1375 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1376 assert(paths.netlogon is not None)
1378 if paths.sysvol is None:
1379 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1380 message("Please either remove %s or see the template at %s" %
1381 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1382 assert(paths.sysvol is not None)
1384 # Set up group policies (domain policy and domain controller policy)
1386 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1387 "{" + policyguid + "}")
1388 os.makedirs(policy_path, 0755)
1389 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1390 "[General]\r\nVersion=65543")
1391 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1392 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1394 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1395 "{" + policyguid_dc + "}")
1396 os.makedirs(policy_path_dc, 0755)
1397 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1398 "[General]\r\nVersion=2")
1399 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1400 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1402 if not os.path.isdir(paths.netlogon):
1403 os.makedirs(paths.netlogon, 0755)
1405 if samdb_fill == FILL_FULL:
1406 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1407 root_uid=root_uid, nobody_uid=nobody_uid,
1408 users_gid=users_gid, wheel_gid=wheel_gid)
1410 message("Setting up sam.ldb rootDSE marking as synchronized")
1411 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1413 # Only make a zone file on the first DC, it should be replicated with DNS replication
1414 if serverrole == "domain controller":
1415 secretsdb_self_join(secrets_ldb, domain=domain,
1417 dnsdomain=names.dnsdomain,
1418 netbiosname=names.netbiosname,
1419 domainsid=domainsid,
1420 machinepass=machinepass,
1421 secure_channel_type=SEC_CHAN_BDC)
1423 secretsdb_setup_dns(secrets_ldb, setup_path,
1424 realm=names.realm, dnsdomain=names.dnsdomain,
1425 dns_keytab_path=paths.dns_keytab,
1428 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1429 assert isinstance(domainguid, str)
1431 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1433 hostip6=hostip6, hostname=names.hostname,
1435 domainguid=domainguid, ntdsguid=names.ntdsguid)
1437 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1438 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1440 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1441 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1442 keytab_name=paths.dns_keytab)
1443 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1444 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1446 create_krb5_conf(paths.krb5conf, setup_path,
1447 dnsdomain=names.dnsdomain, hostname=names.hostname,
1449 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1451 #Now commit the secrets.ldb to disk
1452 secrets_ldb.transaction_commit()
1454 if provision_backend is not None:
1455 if ldap_backend_type == "fedora-ds":
1456 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1458 # delete default SASL mappings
1459 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1461 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1462 for i in range (0, len(res)):
1463 dn = str(res[i]["dn"])
1466 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1469 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1471 m.dn = ldb.Dn(1, names.domaindn)
1474 m.dn = ldb.Dn(1, names.configdn)
1477 m.dn = ldb.Dn(1, names.schemadn)
1480 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1481 if provision_backend.slapd.poll() is None:
1483 if hasattr(provision_backend.slapd, "terminate"):
1484 provision_backend.slapd.terminate()
1486 # Older python versions don't have .terminate()
1488 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1490 #and now wait for it to die
1491 provision_backend.slapd.communicate()
1493 # now display slapd_command_file.txt to show how slapd must be started next time
1494 message("Use later the following commandline to start slapd, then Samba:")
1495 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1496 message(slapd_command)
1497 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1499 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1500 "SLAPD_COMMAND" : slapd_command})
1503 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1506 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1508 message("Once the above files are installed, your Samba4 server will be ready to use")
1509 message("Server Role: %s" % serverrole)
1510 message("Hostname: %s" % names.hostname)
1511 message("NetBIOS Domain: %s" % names.domain)
1512 message("DNS Domain: %s" % names.dnsdomain)
1513 message("DOMAIN SID: %s" % str(domainsid))
1514 if samdb_fill == FILL_FULL:
1515 message("Admin password: %s" % adminpass)
1516 if provision_backend:
1517 if provision_backend.credentials.get_bind_dn() is not None:
1518 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1520 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1522 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1524 result = ProvisionResult()
1525 result.domaindn = domaindn
1526 result.paths = paths
1528 result.samdb = samdb
1533 def provision_become_dc(setup_dir=None,
1534 smbconf=None, targetdir=None, realm=None,
1535 rootdn=None, domaindn=None, schemadn=None,
1536 configdn=None, serverdn=None,
1537 domain=None, hostname=None, domainsid=None,
1538 adminpass=None, krbtgtpass=None, domainguid=None,
1539 policyguid=None, policyguid_dc=None, invocationid=None,
1541 dnspass=None, root=None, nobody=None, users=None,
1542 wheel=None, backup=None, serverrole=None,
1543 ldap_backend=None, ldap_backend_type=None,
1544 sitename=None, debuglevel=1):
1547 """print a message if quiet is not set."""
1550 glue.set_debug_level(debuglevel)
1552 return provision(setup_dir, message, system_session(), None,
1553 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1554 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1555 configdn=configdn, serverdn=serverdn, domain=domain,
1556 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1557 machinepass=machinepass, serverrole="domain controller",
1561 def setup_db_config(setup_path, dbdir):
1562 """Setup a Berkeley database.
1564 :param setup_path: Setup path function.
1565 :param dbdir: Database directory."""
1566 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1567 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1568 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1569 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1571 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1572 {"LDAPDBDIR": dbdir})
1574 class ProvisionBackend(object):
1575 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1576 names=None, message=None,
1577 hostname=None, root=None,
1578 schema=None, ldapadminpass=None,
1579 ldap_backend_type=None, ldap_backend_extra_port=None,
1581 setup_ds_path=None, slapd_path=None,
1582 nosync=False, ldap_dryrun_mode=False):
1583 """Provision an LDAP backend for samba4
1585 This works for OpenLDAP and Fedora DS
1588 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1590 if not os.path.isdir(paths.ldapdir):
1591 os.makedirs(paths.ldapdir, 0700)
1593 if ldap_backend_type == "existing":
1594 #Check to see that this 'existing' LDAP backend in fact exists
1595 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1596 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1597 expression="(objectClass=OpenLDAProotDSE)")
1599 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1600 # This caused them to be set into the long-term database later in the script.
1601 self.credentials = credentials
1602 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1605 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1606 # if another instance of slapd is already running
1608 ldapi_db = Ldb(self.ldapi_uri)
1609 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1610 expression="(objectClass=OpenLDAProotDSE)");
1612 f = open(paths.slapdpid, "r")
1615 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1619 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1624 # Try to print helpful messages when the user has not specified the path to slapd
1625 if slapd_path is None:
1626 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1627 if not os.path.exists(slapd_path):
1628 message (slapd_path)
1629 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1631 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1633 os.unlink(schemadb_path)
1638 # Put the LDIF of the schema into a database so we can search on
1639 # it to generate schema-dependent configurations in Fedora DS and
1641 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1642 schema.ldb.connect(schemadb_path)
1643 schema.ldb.transaction_start()
1645 # These bits of LDIF are supplied when the Schema object is created
1646 schema.ldb.add_ldif(schema.schema_dn_add)
1647 schema.ldb.modify_ldif(schema.schema_dn_modify)
1648 schema.ldb.add_ldif(schema.schema_data)
1649 schema.ldb.transaction_commit()
1651 self.credentials = Credentials()
1652 self.credentials.guess(lp)
1653 #Kerberos to an ldapi:// backend makes no sense
1654 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1656 self.adminCredentials = Credentials()
1657 self.adminCredentials.guess(lp)
1658 #Kerberos to an ldapi:// backend makes no sense
1659 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1661 self.ldap_backend_type = ldap_backend_type
1663 if ldap_backend_type == "fedora-ds":
1664 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1665 names=names, message=message,
1667 ldapadminpass=ldapadminpass, root=root,
1669 ldap_backend_extra_port=ldap_backend_extra_port,
1670 setup_ds_path=setup_ds_path,
1671 slapd_path=slapd_path,
1673 ldap_dryrun_mode=ldap_dryrun_mode)
1675 elif ldap_backend_type == "openldap":
1676 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1677 names=names, message=message,
1679 ldapadminpass=ldapadminpass, root=root,
1681 ldap_backend_extra_port=ldap_backend_extra_port,
1682 ol_mmr_urls=ol_mmr_urls,
1683 slapd_path=slapd_path,
1685 ldap_dryrun_mode=ldap_dryrun_mode)
1687 raise ProvisioningError("Unknown LDAP backend type selected")
1689 self.credentials.set_password(ldapadminpass)
1690 self.adminCredentials.set_username("samba-admin")
1691 self.adminCredentials.set_password(ldapadminpass)
1693 # Now start the slapd, so we can provision onto it. We keep the
1694 # subprocess context around, to kill this off at the successful
1696 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1698 while self.slapd.poll() is None:
1699 # Wait until the socket appears
1701 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1702 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1703 expression="(objectClass=OpenLDAProotDSE)")
1704 # If we have got here, then we must have a valid connection to the LDAP server!
1710 raise ProvisioningError("slapd died before we could make a connection to it")
1713 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1715 hostname=None, ldapadminpass=None, root=None,
1717 ldap_backend_extra_port=None,
1719 slapd_path=None, nosync=False,
1720 ldap_dryrun_mode=False):
1722 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1725 nosync_config = "dbnosync"
1727 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1728 refint_attributes = ""
1729 memberof_config = "# Generated from Samba4 schema\n"
1730 for att in lnkattr.keys():
1731 if lnkattr[att] is not None:
1732 refint_attributes = refint_attributes + " " + att
1734 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1735 { "MEMBER_ATTR" : att ,
1736 "MEMBEROF_ATTR" : lnkattr[att] })
1738 refint_config = read_and_sub_file(setup_path("refint.conf"),
1739 { "LINK_ATTRS" : refint_attributes})
1741 attrs = ["linkID", "lDAPDisplayName"]
1742 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1744 for i in range (0, len(res)):
1745 index_attr = res[i]["lDAPDisplayName"][0]
1746 if index_attr == "objectGUID":
1747 index_attr = "entryUUID"
1749 index_config += "index " + index_attr + " eq\n"
1751 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1753 mmr_replicator_acl = ""
1754 mmr_serverids_config = ""
1755 mmr_syncrepl_schema_config = ""
1756 mmr_syncrepl_config_config = ""
1757 mmr_syncrepl_user_config = ""
1760 if ol_mmr_urls is not None:
1761 # For now, make these equal
1762 mmr_pass = ldapadminpass
1764 url_list=filter(None,ol_mmr_urls.split(' '))
1765 if (len(url_list) == 1):
1766 url_list=filter(None,ol_mmr_urls.split(','))
1769 mmr_on_config = "MirrorMode On"
1770 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1772 for url in url_list:
1774 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1775 { "SERVERID" : str(serverid),
1776 "LDAPSERVER" : url })
1779 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1781 "MMRDN": names.schemadn,
1783 "MMR_PASSWORD": mmr_pass})
1786 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1788 "MMRDN": names.configdn,
1790 "MMR_PASSWORD": mmr_pass})
1793 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1795 "MMRDN": names.domaindn,
1797 "MMR_PASSWORD": mmr_pass })
1798 # OpenLDAP cn=config initialisation
1799 olc_syncrepl_config = ""
1801 # if mmr = yes, generate cn=config-replication directives
1802 # and olc_seed.lif for the other mmr-servers
1803 if ol_mmr_urls is not None:
1805 olc_serverids_config = ""
1806 olc_syncrepl_seed_config = ""
1807 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1809 for url in url_list:
1811 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1812 { "SERVERID" : str(serverid),
1813 "LDAPSERVER" : url })
1816 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1819 "MMR_PASSWORD": mmr_pass})
1821 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1823 "LDAPSERVER" : url})
1825 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1826 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1827 "OLC_PW": ldapadminpass,
1828 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1831 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1832 {"DNSDOMAIN": names.dnsdomain,
1833 "LDAPDIR": paths.ldapdir,
1834 "DOMAINDN": names.domaindn,
1835 "CONFIGDN": names.configdn,
1836 "SCHEMADN": names.schemadn,
1837 "MEMBEROF_CONFIG": memberof_config,
1838 "MIRRORMODE": mmr_on_config,
1839 "REPLICATOR_ACL": mmr_replicator_acl,
1840 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1841 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1842 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1843 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1844 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1845 "OLC_MMR_CONFIG": olc_mmr_config,
1846 "REFINT_CONFIG": refint_config,
1847 "INDEX_CONFIG": index_config,
1848 "NOSYNC": nosync_config})
1850 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1851 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1852 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1854 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1855 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1857 setup_file(setup_path("cn=samba.ldif"),
1858 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1859 { "UUID": str(uuid.uuid4()),
1860 "LDAPTIME": timestring(int(time.time()))} )
1861 setup_file(setup_path("cn=samba-admin.ldif"),
1862 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1863 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1864 "UUID": str(uuid.uuid4()),
1865 "LDAPTIME": timestring(int(time.time()))} )
1867 if ol_mmr_urls is not None:
1868 setup_file(setup_path("cn=replicator.ldif"),
1869 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1870 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1871 "UUID": str(uuid.uuid4()),
1872 "LDAPTIME": timestring(int(time.time()))} )
1875 mapping = "schema-map-openldap-2.3"
1876 backend_schema = "backend-schema.schema"
1878 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1879 assert backend_schema_data is not None
1880 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1882 # now we generate the needed strings to start slapd automatically,
1883 # first ldapi_uri...
1884 if ldap_backend_extra_port is not None:
1885 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1886 # specified there as part of it's clue as to it's own name,
1887 # and not to replicate to itself
1888 if ol_mmr_urls is None:
1889 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1891 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1893 server_port_string = ""
1895 # Prepare the 'result' information - the commands to return in particular
1896 result.slapd_provision_command = [slapd_path]
1898 result.slapd_provision_command.append("-F" + paths.olcdir)
1900 result.slapd_provision_command.append("-h")
1902 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1903 result.slapd_command = list(result.slapd_provision_command)
1905 result.slapd_provision_command.append(result.ldapi_uri)
1906 result.slapd_provision_command.append("-d0")
1908 uris = result.ldapi_uri
1909 if server_port_string is not "":
1910 uris = uris + " " + server_port_string
1912 result.slapd_command.append(uris)
1914 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1915 result.credentials.set_username("samba-admin")
1917 # If we were just looking for crashes up to this point, it's a
1918 # good time to exit before we realise we don't have OpenLDAP on
1920 if ldap_dryrun_mode:
1923 # Finally, convert the configuration into cn=config style!
1924 if not os.path.isdir(paths.olcdir):
1925 os.makedirs(paths.olcdir, 0770)
1927 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1929 # We can't do this, as OpenLDAP is strange. It gives an error
1930 # output to the above, but does the conversion sucessfully...
1933 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1935 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1936 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1938 # Don't confuse the admin by leaving the slapd.conf around
1939 os.remove(paths.slapdconf)
1942 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1944 hostname=None, ldapadminpass=None, root=None,
1946 ldap_backend_extra_port=None,
1950 ldap_dryrun_mode=False):
1952 if ldap_backend_extra_port is not None:
1953 serverport = "ServerPort=%d" % ldap_backend_extra_port
1957 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1959 "HOSTNAME": hostname,
1960 "DNSDOMAIN": names.dnsdomain,
1961 "LDAPDIR": paths.ldapdir,
1962 "DOMAINDN": names.domaindn,
1963 "LDAPMANAGERDN": names.ldapmanagerdn,
1964 "LDAPMANAGERPASS": ldapadminpass,
1965 "SERVERPORT": serverport})
1967 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1968 {"CONFIGDN": names.configdn,
1969 "SCHEMADN": names.schemadn,
1970 "SAMBADN": names.sambadn,
1973 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1974 {"SAMBADN": names.sambadn,
1977 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1979 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1981 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1982 memberof_config = ""
1986 for attr in lnkattr.keys():
1987 if lnkattr[attr] is not None:
1988 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1989 { "ARG_NUMBER" : str(argnum) ,
1990 "LINK_ATTR" : attr })
1991 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1992 { "MEMBER_ATTR" : attr ,
1993 "MEMBEROF_ATTR" : lnkattr[attr] })
1994 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1998 open(paths.fedoradsrefint, 'w').write(refint_config)
1999 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
2001 attrs = ["lDAPDisplayName"]
2002 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
2004 for i in range (0, len(res)):
2005 attr = res[i]["lDAPDisplayName"][0]
2007 if attr == "objectGUID":
2010 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2013 open(paths.fedoradsindex, 'w').write(index_config)
2015 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
2016 {"SAMBADN": names.sambadn,
2017 "LDAPADMINPASS": ldapadminpass
2020 mapping = "schema-map-fedora-ds-1.0"
2021 backend_schema = "99_ad.ldif"
2023 # Build a schema file in Fedora DS format
2024 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
2025 assert backend_schema_data is not None
2026 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2028 result.credentials.set_bind_dn(names.ldapmanagerdn)
2030 # Destory the target directory, or else setup-ds.pl will complain
2031 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
2032 shutil.rmtree(fedora_ds_dir, True)
2034 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2035 #In the 'provision' command line, stay in the foreground so we can easily kill it
2036 result.slapd_provision_command.append("-d0")
2038 #the command for the final run is the normal script
2039 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2041 # If we were just looking for crashes up to this point, it's a
2042 # good time to exit before we realise we don't have Fedora DS on
2043 if ldap_dryrun_mode:
2046 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2047 if setup_ds_path is None:
2048 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\"!")
2049 if not os.path.exists(setup_ds_path):
2050 message (setup_ds_path)
2051 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2053 # Run the Fedora DS setup utility
2054 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2056 raise ProvisioningError("setup-ds failed")
2059 retcode = subprocess.call([
2060 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2061 close_fds=True, shell=False)
2063 raise("ldib2db failed")
2065 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2066 """Create a PHP LDAP admin configuration file.
2068 :param path: Path to write the configuration to.
2069 :param setup_path: Function to generate setup paths.
2071 setup_file(setup_path("phpldapadmin-config.php"), path,
2072 {"S4_LDAPI_URI": ldapi_uri})
2075 def create_zone_file(path, setup_path, dnsdomain,
2076 hostip, hostip6, hostname, realm, domainguid,
2078 """Write out a DNS zone file, from the info in the current database.
2080 :param path: Path of the new zone file.
2081 :param setup_path: Setup path function.
2082 :param dnsdomain: DNS Domain name
2083 :param domaindn: DN of the Domain
2084 :param hostip: Local IPv4 IP
2085 :param hostip6: Local IPv6 IP
2086 :param hostname: Local hostname
2087 :param realm: Realm name
2088 :param domainguid: GUID of the domain.
2089 :param ntdsguid: GUID of the hosts nTDSDSA record.
2091 assert isinstance(domainguid, str)
2093 if hostip6 is not None:
2094 hostip6_base_line = " IN AAAA " + hostip6
2095 hostip6_host_line = hostname + " IN AAAA " + hostip6
2097 hostip6_base_line = ""
2098 hostip6_host_line = ""
2100 if hostip is not None:
2101 hostip_base_line = " IN A " + hostip
2102 hostip_host_line = hostname + " IN A " + hostip
2104 hostip_base_line = ""
2105 hostip_host_line = ""
2107 setup_file(setup_path("provision.zone"), path, {
2108 "HOSTNAME": hostname,
2109 "DNSDOMAIN": dnsdomain,
2111 "HOSTIP_BASE_LINE": hostip_base_line,
2112 "HOSTIP_HOST_LINE": hostip_host_line,
2113 "DOMAINGUID": domainguid,
2114 "DATESTRING": time.strftime("%Y%m%d%H"),
2115 "DEFAULTSITE": DEFAULTSITE,
2116 "NTDSGUID": ntdsguid,
2117 "HOSTIP6_BASE_LINE": hostip6_base_line,
2118 "HOSTIP6_HOST_LINE": hostip6_host_line,
2122 def create_named_conf(path, setup_path, realm, dnsdomain,
2124 """Write out a file containing zone statements suitable for inclusion in a
2125 named.conf file (including GSS-TSIG configuration).
2127 :param path: Path of the new named.conf file.
2128 :param setup_path: Setup path function.
2129 :param realm: Realm name
2130 :param dnsdomain: DNS Domain name
2131 :param private_dir: Path to private directory
2132 :param keytab_name: File name of DNS keytab file
2135 setup_file(setup_path("named.conf"), path, {
2136 "DNSDOMAIN": dnsdomain,
2138 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2139 "PRIVATE_DIR": private_dir
2142 def create_named_txt(path, setup_path, realm, dnsdomain,
2143 private_dir, keytab_name):
2144 """Write out a file containing zone statements suitable for inclusion in a
2145 named.conf file (including GSS-TSIG configuration).
2147 :param path: Path of the new named.conf file.
2148 :param setup_path: Setup path function.
2149 :param realm: Realm name
2150 :param dnsdomain: DNS Domain name
2151 :param private_dir: Path to private directory
2152 :param keytab_name: File name of DNS keytab file
2155 setup_file(setup_path("named.txt"), path, {
2156 "DNSDOMAIN": dnsdomain,
2158 "DNS_KEYTAB": keytab_name,
2159 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2160 "PRIVATE_DIR": private_dir
2163 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2164 """Write out a file containing zone statements suitable for inclusion in a
2165 named.conf file (including GSS-TSIG configuration).
2167 :param path: Path of the new named.conf file.
2168 :param setup_path: Setup path function.
2169 :param dnsdomain: DNS Domain name
2170 :param hostname: Local hostname
2171 :param realm: Realm name
2174 setup_file(setup_path("krb5.conf"), path, {
2175 "DNSDOMAIN": dnsdomain,
2176 "HOSTNAME": hostname,