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",
683 ldap_backend_line = "# No LDAP backend"
684 if ldap_backend is not None:
685 ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri
687 if ldap_backend.ldap_backend_type == "fedora-ds":
688 backend_modules = ["nsuniqueid", "paged_searches"]
689 # We can handle linked attributes here, as we don't have directory-side subtree operations
690 tdb_modules_list = ["extended_dn_out_dereference"]
691 elif ldap_backend.ldap_backend_type == "openldap":
692 backend_modules = ["entryuuid", "paged_searches"]
693 # OpenLDAP handles subtree renames, so we don't want to do any of these things
694 tdb_modules_list = ["extended_dn_out_dereference"]
696 elif serverrole == "domain controller":
697 tdb_modules_list.insert(0, "repl_meta_data")
700 backend_modules = ["objectguid"]
702 if tdb_modules_list is None:
703 tdb_modules_list_as_string = ""
705 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
707 samdb.transaction_start()
709 message("Setting up sam.ldb partitions and settings")
710 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
711 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
712 "SCHEMADN_MOD2": ",objectguid",
713 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
714 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
715 "SCHEMADN_MOD": "schema_fsmo",
716 "CONFIGDN_MOD": "naming_fsmo",
717 "DOMAINDN_MOD": "pdc_fsmo",
718 "MODULES_LIST": ",".join(modules_list),
719 "TDB_MODULES_LIST": tdb_modules_list_as_string,
720 "MODULES_LIST2": ",".join(modules_list2),
721 "BACKEND_MOD": ",".join(backend_modules),
722 "LDAP_BACKEND_LINE": ldap_backend_line,
726 if new_partitions is not None:
728 m.dn = ldb.Dn(samdb, "@PARTITION")
730 m["partition"] = ldb.MessageElement(new_partitions, ldb.FLAG_MOD_ADD, "partition")
733 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
735 message("Setting up sam.ldb rootDSE")
736 setup_samdb_rootdse(samdb, setup_path, names)
739 samdb.transaction_cancel()
742 samdb.transaction_commit()
745 def secretsdb_self_join(secretsdb, domain,
746 netbiosname, domainsid, machinepass,
747 realm=None, dnsdomain=None,
749 key_version_number=1,
750 secure_channel_type=SEC_CHAN_WKSTA):
751 """Add domain join-specific bits to a secrets database.
753 :param secretsdb: Ldb Handle to the secrets database
754 :param machinepass: Machine password
756 attrs=["whenChanged",
764 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
765 msg["secureChannelType"] = str(secure_channel_type)
766 msg["flatname"] = [domain]
767 msg["objectClass"] = ["top", "primaryDomain"]
768 if realm is not None:
769 if dnsdomain is None:
770 dnsdomain = realm.lower()
771 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
773 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
774 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
775 msg["privateKeytab"] = ["secrets.keytab"];
778 msg["secret"] = [machinepass]
779 msg["samAccountName"] = ["%s$" % netbiosname]
780 msg["secureChannelType"] = [str(secure_channel_type)]
781 msg["objectSid"] = [ndr_pack(domainsid)]
783 res = secretsdb.search(base="cn=Primary Domains",
785 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
786 scope=SCOPE_ONELEVEL)
789 if del_msg.dn is not msg.dn:
790 secretsdb.delete(del_msg.dn)
792 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
795 msg["priorSecret"] = res[0]["secret"]
796 msg["priorWhenChanged"] = res[0]["whenChanged"]
798 if res["privateKeytab"] is not None:
799 msg["privateKeytab"] = res[0]["privateKeytab"]
801 if res["krb5Keytab"] is not None:
802 msg["krb5Keytab"] = res[0]["krb5Keytab"]
805 el.set_flags(ldb.FLAG_MOD_REPLACE)
806 secretsdb.modify(msg)
811 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
812 dns_keytab_path, dnspass):
813 """Add DNS specific bits to a secrets database.
815 :param secretsdb: Ldb Handle to the secrets database
816 :param setup_path: Setup path function
817 :param machinepass: Machine password
819 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
821 "DNSDOMAIN": dnsdomain,
822 "DNS_KEYTAB": dns_keytab_path,
823 "DNSPASS_B64": b64encode(dnspass),
827 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
828 """Setup the secrets database.
830 :param path: Path to the secrets database.
831 :param setup_path: Get the path to a setup file.
832 :param session_info: Session info.
833 :param credentials: Credentials
834 :param lp: Loadparm context
835 :return: LDB handle for the created secrets database
837 if os.path.exists(path):
839 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
842 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
843 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
845 secrets_ldb.transaction_start()
846 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
848 if credentials is not None and credentials.authentication_requested():
849 if credentials.get_bind_dn() is not None:
850 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
851 "LDAPMANAGERDN": credentials.get_bind_dn(),
852 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
855 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
856 "LDAPADMINUSER": credentials.get_username(),
857 "LDAPADMINREALM": credentials.get_realm(),
858 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
863 def setup_privileges(path, setup_path, session_info, lp):
864 """Setup the privileges database.
866 :param path: Path to the privileges database.
867 :param setup_path: Get the path to a setup file.
868 :param session_info: Session info.
869 :param credentials: Credentials
870 :param lp: Loadparm context
871 :return: LDB handle for the created secrets database
873 if os.path.exists(path):
875 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
876 privilege_ldb.erase()
877 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
880 def setup_registry(path, setup_path, session_info, lp):
881 """Setup the registry.
883 :param path: Path to the registry database
884 :param setup_path: Function that returns the path to a setup.
885 :param session_info: Session information
886 :param credentials: Credentials
887 :param lp: Loadparm context
889 reg = registry.Registry()
890 hive = registry.open_ldb(path, session_info=session_info,
892 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
893 provision_reg = setup_path("provision.reg")
894 assert os.path.exists(provision_reg)
895 reg.diff_apply(provision_reg)
898 def setup_idmapdb(path, setup_path, session_info, lp):
899 """Setup the idmap database.
901 :param path: path to the idmap database
902 :param setup_path: Function that returns a path to a setup file
903 :param session_info: Session information
904 :param credentials: Credentials
905 :param lp: Loadparm context
907 if os.path.exists(path):
910 idmap_ldb = IDmapDB(path, session_info=session_info,
914 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
918 def setup_samdb_rootdse(samdb, setup_path, names):
919 """Setup the SamDB rootdse.
921 :param samdb: Sam Database handle
922 :param setup_path: Obtain setup path
924 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
925 "SCHEMADN": names.schemadn,
926 "NETBIOSNAME": names.netbiosname,
927 "DNSDOMAIN": names.dnsdomain,
928 "REALM": names.realm,
929 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
930 "DOMAINDN": names.domaindn,
931 "ROOTDN": names.rootdn,
932 "CONFIGDN": names.configdn,
933 "SERVERDN": names.serverdn,
937 def setup_self_join(samdb, names,
938 machinepass, dnspass,
939 domainsid, invocationid, setup_path,
940 policyguid, policyguid_dc, domainControllerFunctionality,
942 """Join a host to its own domain."""
943 assert isinstance(invocationid, str)
944 if ntdsguid is not None:
945 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
948 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
949 "CONFIGDN": names.configdn,
950 "SCHEMADN": names.schemadn,
951 "DOMAINDN": names.domaindn,
952 "SERVERDN": names.serverdn,
953 "INVOCATIONID": invocationid,
954 "NETBIOSNAME": names.netbiosname,
955 "DEFAULTSITE": names.sitename,
956 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
957 "MACHINEPASS_B64": b64encode(machinepass),
958 "DNSPASS_B64": b64encode(dnspass),
959 "REALM": names.realm,
960 "DOMAIN": names.domain,
961 "DNSDOMAIN": names.dnsdomain,
962 "SAMBA_VERSION_STRING": version,
963 "NTDSGUID": ntdsguid_line,
964 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
966 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
967 "POLICYGUID": policyguid,
968 "POLICYGUID_DC": policyguid_dc,
969 "DNSDOMAIN": names.dnsdomain,
970 "DOMAINSID": str(domainsid),
971 "DOMAINDN": names.domaindn})
973 # add the NTDSGUID based SPNs
974 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
975 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
976 expression="", scope=SCOPE_BASE)
977 assert isinstance(names.ntdsguid, str)
979 # Setup fSMORoleOwner entries to point at the newly created DC entry
980 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
981 "DOMAIN": names.domain,
982 "DNSDOMAIN": names.dnsdomain,
983 "DOMAINDN": names.domaindn,
984 "CONFIGDN": names.configdn,
985 "SCHEMADN": names.schemadn,
986 "DEFAULTSITE": names.sitename,
987 "SERVERDN": names.serverdn,
988 "NETBIOSNAME": names.netbiosname,
989 "NTDSGUID": names.ntdsguid
993 def setup_samdb(path, setup_path, session_info, credentials, lp,
995 domainsid, domainguid, policyguid, policyguid_dc,
996 fill, adminpass, krbtgtpass,
997 machinepass, invocationid, dnspass, ntdsguid,
998 serverrole, dom_for_fun_level=None,
999 schema=None, ldap_backend=None):
1000 """Setup a complete SAM Database.
1002 :note: This will wipe the main SAM database file!
1005 # ATTENTION: Do NOT change these default values without discussion with the
1006 # team and/or release manager. They have a big impact on the whole program!
1007 domainControllerFunctionality = DS_DC_FUNCTION_2008
1009 if dom_for_fun_level is None:
1010 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1011 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
1012 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
1014 if dom_for_fun_level > domainControllerFunctionality:
1015 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!")
1017 domainFunctionality = dom_for_fun_level
1018 forestFunctionality = dom_for_fun_level
1020 # Also wipes the database
1021 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
1022 credentials=credentials, session_info=session_info,
1023 names=names, ldap_backend=ldap_backend,
1024 serverrole=serverrole, schema=schema)
1026 if (schema == None):
1027 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1028 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
1030 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1031 samdb = Ldb(session_info=session_info,
1032 credentials=credentials, lp=lp)
1034 message("Pre-loading the Samba 4 and AD schema")
1036 # Load the schema from the one we computed earlier
1037 samdb.set_schema_from_ldb(schema.ldb)
1039 # And now we can connect to the DB - the schema won't be loaded from the DB
1042 if fill == FILL_DRS:
1045 samdb.transaction_start()
1047 message("Erasing data from partitions")
1048 # Load the schema (again). This time it will force a reindex,
1049 # and will therefore make the erase_partitions() below
1050 # computationally sane
1051 samdb.set_schema_from_ldb(schema.ldb)
1052 samdb.erase_partitions()
1054 # Set the domain functionality levels onto the database.
1055 # Various module (the password_hash module in particular) need
1056 # to know what level of AD we are emulating.
1058 # These will be fixed into the database via the database
1059 # modifictions below, but we need them set from the start.
1060 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1061 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1062 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1064 samdb.set_domain_sid(str(domainsid))
1065 if serverrole == "domain controller":
1066 samdb.set_invocation_id(invocationid)
1068 message("Adding DomainDN: %s" % names.domaindn)
1070 #impersonate domain admin
1071 admin_session_info = admin_session(lp, str(domainsid))
1072 samdb.set_session_info(admin_session_info)
1073 if domainguid is not None:
1074 domainguid_line = "objectGUID: %s\n-" % domainguid
1076 domainguid_line = ""
1077 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1078 "DOMAINDN": names.domaindn,
1079 "DOMAINGUID": domainguid_line
1083 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1084 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1085 "DOMAINSID": str(domainsid),
1086 "SCHEMADN": names.schemadn,
1087 "NETBIOSNAME": names.netbiosname,
1088 "DEFAULTSITE": names.sitename,
1089 "CONFIGDN": names.configdn,
1090 "SERVERDN": names.serverdn,
1091 "POLICYGUID": policyguid,
1092 "DOMAINDN": names.domaindn,
1093 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1094 "SAMBA_VERSION_STRING": version
1097 message("Adding configuration container")
1098 descr = get_config_descriptor(domainsid);
1099 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1100 "CONFIGDN": names.configdn,
1101 "DESCRIPTOR": descr,
1103 message("Modifying configuration container")
1104 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1105 "CONFIGDN": names.configdn,
1106 "SCHEMADN": names.schemadn,
1109 # The LDIF here was created when the Schema object was constructed
1110 message("Setting up sam.ldb schema")
1111 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1112 samdb.modify_ldif(schema.schema_dn_modify)
1113 samdb.write_prefixes_from_schema()
1114 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1115 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1116 {"SCHEMADN": names.schemadn})
1118 message("Setting up sam.ldb configuration data")
1119 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1120 "CONFIGDN": names.configdn,
1121 "NETBIOSNAME": names.netbiosname,
1122 "DEFAULTSITE": names.sitename,
1123 "DNSDOMAIN": names.dnsdomain,
1124 "DOMAIN": names.domain,
1125 "SCHEMADN": names.schemadn,
1126 "DOMAINDN": names.domaindn,
1127 "SERVERDN": names.serverdn,
1128 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1131 message("Setting up display specifiers")
1132 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1133 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1134 check_all_substituted(display_specifiers_ldif)
1135 samdb.add_ldif(display_specifiers_ldif)
1137 message("Adding users container")
1138 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1139 "DOMAINDN": names.domaindn})
1140 message("Modifying users container")
1141 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1142 "DOMAINDN": names.domaindn})
1143 message("Adding computers container")
1144 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1145 "DOMAINDN": names.domaindn})
1146 message("Modifying computers container")
1147 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1148 "DOMAINDN": names.domaindn})
1149 message("Setting up sam.ldb data")
1150 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1151 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1152 "DOMAINDN": names.domaindn,
1153 "NETBIOSNAME": names.netbiosname,
1154 "DEFAULTSITE": names.sitename,
1155 "CONFIGDN": names.configdn,
1156 "SERVERDN": names.serverdn,
1157 "POLICYGUID_DC": policyguid_dc
1160 if fill == FILL_FULL:
1161 message("Setting up sam.ldb users and groups")
1162 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1163 "DOMAINDN": names.domaindn,
1164 "DOMAINSID": str(domainsid),
1165 "CONFIGDN": names.configdn,
1166 "ADMINPASS_B64": b64encode(adminpass),
1167 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1170 if serverrole == "domain controller":
1171 message("Setting up self join")
1172 setup_self_join(samdb, names=names, invocationid=invocationid,
1174 machinepass=machinepass,
1175 domainsid=domainsid, policyguid=policyguid,
1176 policyguid_dc=policyguid_dc,
1177 setup_path=setup_path,
1178 domainControllerFunctionality=domainControllerFunctionality,
1181 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1182 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1183 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1184 assert isinstance(names.ntdsguid, str)
1187 samdb.transaction_cancel()
1190 samdb.transaction_commit()
1195 FILL_NT4SYNC = "NT4SYNC"
1199 def provision(setup_dir, message, session_info,
1200 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1202 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1204 domain=None, hostname=None, hostip=None, hostip6=None,
1205 domainsid=None, adminpass=None, ldapadminpass=None,
1206 krbtgtpass=None, domainguid=None,
1207 policyguid=None, policyguid_dc=None, invocationid=None,
1208 machinepass=None, ntdsguid=None,
1209 dnspass=None, root=None, nobody=None, users=None,
1210 wheel=None, backup=None, aci=None, serverrole=None,
1211 dom_for_fun_level=None,
1212 ldap_backend_extra_port=None, ldap_backend_type=None,
1214 ol_mmr_urls=None, ol_olc=None,
1215 setup_ds_path=None, slapd_path=None, nosync=False,
1216 ldap_dryrun_mode=False):
1219 :note: caution, this wipes all existing data!
1222 def setup_path(file):
1223 return os.path.join(setup_dir, file)
1225 if domainsid is None:
1226 domainsid = security.random_sid()
1228 domainsid = security.dom_sid(domainsid)
1230 # create/adapt the group policy GUIDs
1231 if policyguid is None:
1232 policyguid = str(uuid.uuid4())
1233 policyguid = policyguid.upper()
1234 if policyguid_dc is None:
1235 policyguid_dc = str(uuid.uuid4())
1236 policyguid_dc = policyguid_dc.upper()
1238 if adminpass is None:
1239 adminpass = glue.generate_random_str(12)
1240 if krbtgtpass is None:
1241 krbtgtpass = glue.generate_random_str(12)
1242 if machinepass is None:
1243 machinepass = glue.generate_random_str(12)
1245 dnspass = glue.generate_random_str(12)
1246 if ldapadminpass is None:
1247 #Make a new, random password between Samba and it's LDAP server
1248 ldapadminpass=glue.generate_random_str(12)
1251 root_uid = findnss_uid([root or "root"])
1252 nobody_uid = findnss_uid([nobody or "nobody"])
1253 users_gid = findnss_gid([users or "users"])
1255 wheel_gid = findnss_gid(["wheel", "adm"])
1257 wheel_gid = findnss_gid([wheel])
1259 if targetdir is not None:
1260 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1261 os.makedirs(os.path.join(targetdir, "etc"))
1262 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1263 elif smbconf is None:
1264 smbconf = param.default_path()
1266 # only install a new smb.conf if there isn't one there already
1267 if not os.path.exists(smbconf):
1268 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1271 lp = param.LoadParm()
1274 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1275 dnsdomain=realm, serverrole=serverrole,
1276 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1277 serverdn=serverdn, sitename=sitename)
1279 paths = provision_paths_from_lp(lp, names.dnsdomain)
1283 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1284 except socket.gaierror, (socket.EAI_NODATA, msg):
1289 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1290 except socket.gaierror, (socket.EAI_NODATA, msg):
1293 if serverrole is None:
1294 serverrole = lp.get("server role")
1296 assert serverrole in ("domain controller", "member server", "standalone")
1297 if invocationid is None and serverrole == "domain controller":
1298 invocationid = str(uuid.uuid4())
1300 if not os.path.exists(paths.private_dir):
1301 os.mkdir(paths.private_dir)
1303 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1305 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1306 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1308 secrets_credentials = credentials
1309 provision_backend = None
1310 if ldap_backend_type:
1311 # We only support an LDAP backend over ldapi://
1313 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1314 lp=lp, credentials=credentials,
1316 message=message, hostname=hostname,
1317 root=root, schema=schema,
1318 ldap_backend_type=ldap_backend_type,
1319 ldapadminpass=ldapadminpass,
1320 ldap_backend_extra_port=ldap_backend_extra_port,
1321 ol_mmr_urls=ol_mmr_urls,
1322 slapd_path=slapd_path,
1323 setup_ds_path=setup_ds_path,
1324 ldap_dryrun_mode=ldap_dryrun_mode)
1326 # Now use the backend credentials to access the databases
1327 credentials = provision_backend.credentials
1328 secrets_credentials = provision_backend.adminCredentials
1329 ldapi_url = provision_backend.ldapi_uri
1331 # only install a new shares config db if there is none
1332 if not os.path.exists(paths.shareconf):
1333 message("Setting up share.ldb")
1334 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1335 credentials=credentials, lp=lp)
1336 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1339 message("Setting up secrets.ldb")
1340 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1341 session_info=session_info,
1342 credentials=secrets_credentials, lp=lp)
1344 message("Setting up the registry")
1345 setup_registry(paths.hklm, setup_path, session_info,
1348 message("Setting up the privileges database")
1349 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1351 message("Setting up idmap db")
1352 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1355 message("Setting up SAM db")
1356 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1357 credentials=credentials, lp=lp, names=names,
1359 domainsid=domainsid,
1360 schema=schema, domainguid=domainguid,
1361 policyguid=policyguid, policyguid_dc=policyguid_dc,
1363 adminpass=adminpass, krbtgtpass=krbtgtpass,
1364 invocationid=invocationid,
1365 machinepass=machinepass, dnspass=dnspass,
1366 ntdsguid=ntdsguid, serverrole=serverrole,
1367 dom_for_fun_level=dom_for_fun_level,
1368 ldap_backend=provision_backend)
1370 if serverrole == "domain controller":
1371 if paths.netlogon is None:
1372 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1373 message("Please either remove %s or see the template at %s" %
1374 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1375 assert(paths.netlogon is not None)
1377 if paths.sysvol is None:
1378 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1379 message("Please either remove %s or see the template at %s" %
1380 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1381 assert(paths.sysvol is not None)
1383 # Set up group policies (domain policy and domain controller policy)
1385 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1386 "{" + policyguid + "}")
1387 os.makedirs(policy_path, 0755)
1388 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1389 "[General]\r\nVersion=65543")
1390 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1391 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1393 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1394 "{" + policyguid_dc + "}")
1395 os.makedirs(policy_path_dc, 0755)
1396 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1397 "[General]\r\nVersion=2")
1398 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1399 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1401 if not os.path.isdir(paths.netlogon):
1402 os.makedirs(paths.netlogon, 0755)
1404 if samdb_fill == FILL_FULL:
1405 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1406 root_uid=root_uid, nobody_uid=nobody_uid,
1407 users_gid=users_gid, wheel_gid=wheel_gid)
1409 message("Setting up sam.ldb rootDSE marking as synchronized")
1410 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1412 # Only make a zone file on the first DC, it should be replicated with DNS replication
1413 if serverrole == "domain controller":
1414 secretsdb_self_join(secrets_ldb, domain=domain,
1416 dnsdomain=names.dnsdomain,
1417 netbiosname=names.netbiosname,
1418 domainsid=domainsid,
1419 machinepass=machinepass,
1420 secure_channel_type=SEC_CHAN_BDC)
1422 secretsdb_setup_dns(secrets_ldb, setup_path,
1423 realm=names.realm, dnsdomain=names.dnsdomain,
1424 dns_keytab_path=paths.dns_keytab,
1427 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1428 assert isinstance(domainguid, str)
1430 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1432 hostip6=hostip6, hostname=names.hostname,
1434 domainguid=domainguid, ntdsguid=names.ntdsguid)
1436 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1437 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1439 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1440 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1441 keytab_name=paths.dns_keytab)
1442 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1443 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1445 create_krb5_conf(paths.krb5conf, setup_path,
1446 dnsdomain=names.dnsdomain, hostname=names.hostname,
1448 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1450 #Now commit the secrets.ldb to disk
1451 secrets_ldb.transaction_commit()
1453 if provision_backend is not None:
1454 if ldap_backend_type == "fedora-ds":
1455 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1457 # delete default SASL mappings
1458 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1460 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1461 for i in range (0, len(res)):
1462 dn = str(res[i]["dn"])
1465 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1468 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1470 m.dn = ldb.Dn(1, names.domaindn)
1473 m.dn = ldb.Dn(1, names.configdn)
1476 m.dn = ldb.Dn(1, names.schemadn)
1479 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1480 if provision_backend.slapd.poll() is None:
1482 if hasattr(provision_backend.slapd, "terminate"):
1483 provision_backend.slapd.terminate()
1485 # Older python versions don't have .terminate()
1487 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1489 #and now wait for it to die
1490 provision_backend.slapd.communicate()
1492 # now display slapd_command_file.txt to show how slapd must be started next time
1493 message("Use later the following commandline to start slapd, then Samba:")
1494 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1495 message(slapd_command)
1496 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1498 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1499 "SLAPD_COMMAND" : slapd_command})
1502 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1505 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1507 message("Once the above files are installed, your Samba4 server will be ready to use")
1508 message("Server Role: %s" % serverrole)
1509 message("Hostname: %s" % names.hostname)
1510 message("NetBIOS Domain: %s" % names.domain)
1511 message("DNS Domain: %s" % names.dnsdomain)
1512 message("DOMAIN SID: %s" % str(domainsid))
1513 if samdb_fill == FILL_FULL:
1514 message("Admin password: %s" % adminpass)
1515 if provision_backend:
1516 if provision_backend.credentials.get_bind_dn() is not None:
1517 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1519 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1521 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1523 result = ProvisionResult()
1524 result.domaindn = domaindn
1525 result.paths = paths
1527 result.samdb = samdb
1532 def provision_become_dc(setup_dir=None,
1533 smbconf=None, targetdir=None, realm=None,
1534 rootdn=None, domaindn=None, schemadn=None,
1535 configdn=None, serverdn=None,
1536 domain=None, hostname=None, domainsid=None,
1537 adminpass=None, krbtgtpass=None, domainguid=None,
1538 policyguid=None, policyguid_dc=None, invocationid=None,
1540 dnspass=None, root=None, nobody=None, users=None,
1541 wheel=None, backup=None, serverrole=None,
1542 ldap_backend=None, ldap_backend_type=None,
1543 sitename=None, debuglevel=1):
1546 """print a message if quiet is not set."""
1549 glue.set_debug_level(debuglevel)
1551 return provision(setup_dir, message, system_session(), None,
1552 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1553 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1554 configdn=configdn, serverdn=serverdn, domain=domain,
1555 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1556 machinepass=machinepass, serverrole="domain controller",
1560 def setup_db_config(setup_path, dbdir):
1561 """Setup a Berkeley database.
1563 :param setup_path: Setup path function.
1564 :param dbdir: Database directory."""
1565 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1566 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1567 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1568 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1570 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1571 {"LDAPDBDIR": dbdir})
1573 class ProvisionBackend(object):
1574 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1575 names=None, message=None,
1576 hostname=None, root=None,
1577 schema=None, ldapadminpass=None,
1578 ldap_backend_type=None, ldap_backend_extra_port=None,
1580 setup_ds_path=None, slapd_path=None,
1581 nosync=False, ldap_dryrun_mode=False):
1582 """Provision an LDAP backend for samba4
1584 This works for OpenLDAP and Fedora DS
1587 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1589 if not os.path.isdir(paths.ldapdir):
1590 os.makedirs(paths.ldapdir, 0700)
1592 if ldap_backend_type == "existing":
1593 #Check to see that this 'existing' LDAP backend in fact exists
1594 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1595 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1596 expression="(objectClass=OpenLDAProotDSE)")
1598 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1599 # This caused them to be set into the long-term database later in the script.
1600 self.credentials = credentials
1601 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1604 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1605 # if another instance of slapd is already running
1607 ldapi_db = Ldb(self.ldapi_uri)
1608 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1609 expression="(objectClass=OpenLDAProotDSE)");
1611 f = open(paths.slapdpid, "r")
1614 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1618 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1623 # Try to print helpful messages when the user has not specified the path to slapd
1624 if slapd_path is None:
1625 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1626 if not os.path.exists(slapd_path):
1627 message (slapd_path)
1628 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1630 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1632 os.unlink(schemadb_path)
1637 # Put the LDIF of the schema into a database so we can search on
1638 # it to generate schema-dependent configurations in Fedora DS and
1640 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1641 schema.ldb.connect(schemadb_path)
1642 schema.ldb.transaction_start()
1644 # These bits of LDIF are supplied when the Schema object is created
1645 schema.ldb.add_ldif(schema.schema_dn_add)
1646 schema.ldb.modify_ldif(schema.schema_dn_modify)
1647 schema.ldb.add_ldif(schema.schema_data)
1648 schema.ldb.transaction_commit()
1650 self.credentials = Credentials()
1651 self.credentials.guess(lp)
1652 #Kerberos to an ldapi:// backend makes no sense
1653 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1655 self.adminCredentials = Credentials()
1656 self.adminCredentials.guess(lp)
1657 #Kerberos to an ldapi:// backend makes no sense
1658 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1660 self.ldap_backend_type = ldap_backend_type
1662 if ldap_backend_type == "fedora-ds":
1663 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1664 names=names, message=message,
1666 ldapadminpass=ldapadminpass, root=root,
1668 ldap_backend_extra_port=ldap_backend_extra_port,
1669 setup_ds_path=setup_ds_path,
1670 slapd_path=slapd_path,
1672 ldap_dryrun_mode=ldap_dryrun_mode)
1674 elif ldap_backend_type == "openldap":
1675 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1676 names=names, message=message,
1678 ldapadminpass=ldapadminpass, root=root,
1680 ldap_backend_extra_port=ldap_backend_extra_port,
1681 ol_mmr_urls=ol_mmr_urls,
1682 slapd_path=slapd_path,
1684 ldap_dryrun_mode=ldap_dryrun_mode)
1686 raise ProvisioningError("Unknown LDAP backend type selected")
1688 self.credentials.set_password(ldapadminpass)
1689 self.adminCredentials.set_username("samba-admin")
1690 self.adminCredentials.set_password(ldapadminpass)
1692 # Now start the slapd, so we can provision onto it. We keep the
1693 # subprocess context around, to kill this off at the successful
1695 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1697 while self.slapd.poll() is None:
1698 # Wait until the socket appears
1700 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1701 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1702 expression="(objectClass=OpenLDAProotDSE)")
1703 # If we have got here, then we must have a valid connection to the LDAP server!
1709 raise ProvisioningError("slapd died before we could make a connection to it")
1712 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1714 hostname=None, ldapadminpass=None, root=None,
1716 ldap_backend_extra_port=None,
1718 slapd_path=None, nosync=False,
1719 ldap_dryrun_mode=False):
1721 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1724 nosync_config = "dbnosync"
1726 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1727 refint_attributes = ""
1728 memberof_config = "# Generated from Samba4 schema\n"
1729 for att in lnkattr.keys():
1730 if lnkattr[att] is not None:
1731 refint_attributes = refint_attributes + " " + att
1733 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1734 { "MEMBER_ATTR" : att ,
1735 "MEMBEROF_ATTR" : lnkattr[att] })
1737 refint_config = read_and_sub_file(setup_path("refint.conf"),
1738 { "LINK_ATTRS" : refint_attributes})
1740 attrs = ["linkID", "lDAPDisplayName"]
1741 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1743 for i in range (0, len(res)):
1744 index_attr = res[i]["lDAPDisplayName"][0]
1745 if index_attr == "objectGUID":
1746 index_attr = "entryUUID"
1748 index_config += "index " + index_attr + " eq\n"
1750 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1752 mmr_replicator_acl = ""
1753 mmr_serverids_config = ""
1754 mmr_syncrepl_schema_config = ""
1755 mmr_syncrepl_config_config = ""
1756 mmr_syncrepl_user_config = ""
1759 if ol_mmr_urls is not None:
1760 # For now, make these equal
1761 mmr_pass = ldapadminpass
1763 url_list=filter(None,ol_mmr_urls.split(' '))
1764 if (len(url_list) == 1):
1765 url_list=filter(None,ol_mmr_urls.split(','))
1768 mmr_on_config = "MirrorMode On"
1769 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1771 for url in url_list:
1773 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1774 { "SERVERID" : str(serverid),
1775 "LDAPSERVER" : url })
1778 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1780 "MMRDN": names.schemadn,
1782 "MMR_PASSWORD": mmr_pass})
1785 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1787 "MMRDN": names.configdn,
1789 "MMR_PASSWORD": mmr_pass})
1792 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1794 "MMRDN": names.domaindn,
1796 "MMR_PASSWORD": mmr_pass })
1797 # OpenLDAP cn=config initialisation
1798 olc_syncrepl_config = ""
1800 # if mmr = yes, generate cn=config-replication directives
1801 # and olc_seed.lif for the other mmr-servers
1802 if ol_mmr_urls is not None:
1804 olc_serverids_config = ""
1805 olc_syncrepl_seed_config = ""
1806 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1808 for url in url_list:
1810 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1811 { "SERVERID" : str(serverid),
1812 "LDAPSERVER" : url })
1815 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1818 "MMR_PASSWORD": mmr_pass})
1820 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1822 "LDAPSERVER" : url})
1824 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1825 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1826 "OLC_PW": ldapadminpass,
1827 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1830 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1831 {"DNSDOMAIN": names.dnsdomain,
1832 "LDAPDIR": paths.ldapdir,
1833 "DOMAINDN": names.domaindn,
1834 "CONFIGDN": names.configdn,
1835 "SCHEMADN": names.schemadn,
1836 "MEMBEROF_CONFIG": memberof_config,
1837 "MIRRORMODE": mmr_on_config,
1838 "REPLICATOR_ACL": mmr_replicator_acl,
1839 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1840 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1841 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1842 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1843 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1844 "OLC_MMR_CONFIG": olc_mmr_config,
1845 "REFINT_CONFIG": refint_config,
1846 "INDEX_CONFIG": index_config,
1847 "NOSYNC": nosync_config})
1849 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1850 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1851 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1853 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1854 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1856 setup_file(setup_path("cn=samba.ldif"),
1857 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1858 { "UUID": str(uuid.uuid4()),
1859 "LDAPTIME": timestring(int(time.time()))} )
1860 setup_file(setup_path("cn=samba-admin.ldif"),
1861 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1862 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1863 "UUID": str(uuid.uuid4()),
1864 "LDAPTIME": timestring(int(time.time()))} )
1866 if ol_mmr_urls is not None:
1867 setup_file(setup_path("cn=replicator.ldif"),
1868 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1869 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1870 "UUID": str(uuid.uuid4()),
1871 "LDAPTIME": timestring(int(time.time()))} )
1874 mapping = "schema-map-openldap-2.3"
1875 backend_schema = "backend-schema.schema"
1877 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1878 assert backend_schema_data is not None
1879 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1881 # now we generate the needed strings to start slapd automatically,
1882 # first ldapi_uri...
1883 if ldap_backend_extra_port is not None:
1884 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1885 # specified there as part of it's clue as to it's own name,
1886 # and not to replicate to itself
1887 if ol_mmr_urls is None:
1888 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1890 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1892 server_port_string = ""
1894 # Prepare the 'result' information - the commands to return in particular
1895 result.slapd_provision_command = [slapd_path]
1897 result.slapd_provision_command.append("-F" + paths.olcdir)
1899 result.slapd_provision_command.append("-h")
1901 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1902 result.slapd_command = list(result.slapd_provision_command)
1904 result.slapd_provision_command.append(result.ldapi_uri)
1905 result.slapd_provision_command.append("-d0")
1907 uris = result.ldapi_uri
1908 if server_port_string is not "":
1909 uris = uris + " " + server_port_string
1911 result.slapd_command.append(uris)
1913 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1914 result.credentials.set_username("samba-admin")
1916 # If we were just looking for crashes up to this point, it's a
1917 # good time to exit before we realise we don't have OpenLDAP on
1919 if ldap_dryrun_mode:
1922 # Finally, convert the configuration into cn=config style!
1923 if not os.path.isdir(paths.olcdir):
1924 os.makedirs(paths.olcdir, 0770)
1926 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1928 # We can't do this, as OpenLDAP is strange. It gives an error
1929 # output to the above, but does the conversion sucessfully...
1932 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1934 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1935 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1937 # Don't confuse the admin by leaving the slapd.conf around
1938 os.remove(paths.slapdconf)
1941 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1943 hostname=None, ldapadminpass=None, root=None,
1945 ldap_backend_extra_port=None,
1949 ldap_dryrun_mode=False):
1951 if ldap_backend_extra_port is not None:
1952 serverport = "ServerPort=%d" % ldap_backend_extra_port
1956 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1958 "HOSTNAME": hostname,
1959 "DNSDOMAIN": names.dnsdomain,
1960 "LDAPDIR": paths.ldapdir,
1961 "DOMAINDN": names.domaindn,
1962 "LDAPMANAGERDN": names.ldapmanagerdn,
1963 "LDAPMANAGERPASS": ldapadminpass,
1964 "SERVERPORT": serverport})
1966 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1967 {"CONFIGDN": names.configdn,
1968 "SCHEMADN": names.schemadn,
1969 "SAMBADN": names.sambadn,
1972 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1973 {"SAMBADN": names.sambadn,
1976 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1978 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1980 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1981 memberof_config = ""
1985 for attr in lnkattr.keys():
1986 if lnkattr[attr] is not None:
1987 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1988 { "ARG_NUMBER" : str(argnum) ,
1989 "LINK_ATTR" : attr })
1990 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1991 { "MEMBER_ATTR" : attr ,
1992 "MEMBEROF_ATTR" : lnkattr[attr] })
1993 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1997 open(paths.fedoradsrefint, 'w').write(refint_config)
1998 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
2000 attrs = ["lDAPDisplayName"]
2001 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
2003 for i in range (0, len(res)):
2004 attr = res[i]["lDAPDisplayName"][0]
2006 if attr == "objectGUID":
2009 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2012 open(paths.fedoradsindex, 'w').write(index_config)
2014 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
2015 {"SAMBADN": names.sambadn,
2016 "LDAPADMINPASS": ldapadminpass
2019 mapping = "schema-map-fedora-ds-1.0"
2020 backend_schema = "99_ad.ldif"
2022 # Build a schema file in Fedora DS format
2023 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
2024 assert backend_schema_data is not None
2025 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2027 result.credentials.set_bind_dn(names.ldapmanagerdn)
2029 # Destory the target directory, or else setup-ds.pl will complain
2030 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
2031 shutil.rmtree(fedora_ds_dir, True)
2033 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2034 #In the 'provision' command line, stay in the foreground so we can easily kill it
2035 result.slapd_provision_command.append("-d0")
2037 #the command for the final run is the normal script
2038 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2040 # If we were just looking for crashes up to this point, it's a
2041 # good time to exit before we realise we don't have Fedora DS on
2042 if ldap_dryrun_mode:
2045 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2046 if setup_ds_path is None:
2047 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\"!")
2048 if not os.path.exists(setup_ds_path):
2049 message (setup_ds_path)
2050 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2052 # Run the Fedora DS setup utility
2053 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2055 raise ProvisioningError("setup-ds failed")
2058 retcode = subprocess.call([
2059 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2060 close_fds=True, shell=False)
2062 raise("ldib2db failed")
2064 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2065 """Create a PHP LDAP admin configuration file.
2067 :param path: Path to write the configuration to.
2068 :param setup_path: Function to generate setup paths.
2070 setup_file(setup_path("phpldapadmin-config.php"), path,
2071 {"S4_LDAPI_URI": ldapi_uri})
2074 def create_zone_file(path, setup_path, dnsdomain,
2075 hostip, hostip6, hostname, realm, domainguid,
2077 """Write out a DNS zone file, from the info in the current database.
2079 :param path: Path of the new zone file.
2080 :param setup_path: Setup path function.
2081 :param dnsdomain: DNS Domain name
2082 :param domaindn: DN of the Domain
2083 :param hostip: Local IPv4 IP
2084 :param hostip6: Local IPv6 IP
2085 :param hostname: Local hostname
2086 :param realm: Realm name
2087 :param domainguid: GUID of the domain.
2088 :param ntdsguid: GUID of the hosts nTDSDSA record.
2090 assert isinstance(domainguid, str)
2092 if hostip6 is not None:
2093 hostip6_base_line = " IN AAAA " + hostip6
2094 hostip6_host_line = hostname + " IN AAAA " + hostip6
2096 hostip6_base_line = ""
2097 hostip6_host_line = ""
2099 if hostip is not None:
2100 hostip_base_line = " IN A " + hostip
2101 hostip_host_line = hostname + " IN A " + hostip
2103 hostip_base_line = ""
2104 hostip_host_line = ""
2106 setup_file(setup_path("provision.zone"), path, {
2107 "HOSTNAME": hostname,
2108 "DNSDOMAIN": dnsdomain,
2110 "HOSTIP_BASE_LINE": hostip_base_line,
2111 "HOSTIP_HOST_LINE": hostip_host_line,
2112 "DOMAINGUID": domainguid,
2113 "DATESTRING": time.strftime("%Y%m%d%H"),
2114 "DEFAULTSITE": DEFAULTSITE,
2115 "NTDSGUID": ntdsguid,
2116 "HOSTIP6_BASE_LINE": hostip6_base_line,
2117 "HOSTIP6_HOST_LINE": hostip6_host_line,
2121 def create_named_conf(path, setup_path, realm, dnsdomain,
2123 """Write out a file containing zone statements suitable for inclusion in a
2124 named.conf file (including GSS-TSIG configuration).
2126 :param path: Path of the new named.conf file.
2127 :param setup_path: Setup path function.
2128 :param realm: Realm name
2129 :param dnsdomain: DNS Domain name
2130 :param private_dir: Path to private directory
2131 :param keytab_name: File name of DNS keytab file
2134 setup_file(setup_path("named.conf"), path, {
2135 "DNSDOMAIN": dnsdomain,
2137 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2138 "PRIVATE_DIR": private_dir
2141 def create_named_txt(path, setup_path, realm, dnsdomain,
2142 private_dir, keytab_name):
2143 """Write out a file containing zone statements suitable for inclusion in a
2144 named.conf file (including GSS-TSIG configuration).
2146 :param path: Path of the new named.conf file.
2147 :param setup_path: Setup path function.
2148 :param realm: Realm name
2149 :param dnsdomain: DNS Domain name
2150 :param private_dir: Path to private directory
2151 :param keytab_name: File name of DNS keytab file
2154 setup_file(setup_path("named.txt"), path, {
2155 "DNSDOMAIN": dnsdomain,
2157 "DNS_KEYTAB": keytab_name,
2158 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2159 "PRIVATE_DIR": private_dir
2162 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2163 """Write out a file containing zone statements suitable for inclusion in a
2164 named.conf file (including GSS-TSIG configuration).
2166 :param path: Path of the new named.conf file.
2167 :param setup_path: Setup path function.
2168 :param dnsdomain: DNS Domain name
2169 :param hostname: Local hostname
2170 :param realm: Realm name
2173 setup_file(setup_path("krb5.conf"), path, {
2174 "DNSDOMAIN": dnsdomain,
2175 "HOSTNAME": hostname,