2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
74 raise Exception("Unable to find setup directory.")
76 def get_schema_descriptor(domain_sid):
77 sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
78 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
79 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
81 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
83 "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
84 "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
85 "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
86 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
87 sec = security.descriptor.from_sddl(sddl, domain_sid)
88 return b64encode(ndr_pack(sec))
90 def get_config_descriptor(domain_sid):
91 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
97 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
98 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
99 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
101 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
103 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
104 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
105 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
106 sec = security.descriptor.from_sddl(sddl, domain_sid)
107 return b64encode(ndr_pack(sec))
110 DEFAULTSITE = "Default-First-Site-Name"
114 class ProvisioningError(Exception):
115 """A generic provision error."""
117 class InvalidNetbiosName(Exception):
118 """A specified name was not a valid NetBIOS name."""
119 def __init__(self, name):
120 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
123 class ProvisionPaths(object):
125 self.shareconf = None
136 self.dns_keytab = None
139 self.private_dir = None
141 self.slapdconf = None
142 self.modulesconf = None
143 self.memberofconf = None
144 self.fedoradsinf = None
145 self.fedoradspartitions = None
146 self.fedoradssasl = None
147 self.fedoradspam = None
148 self.fedoradsrefint = None
149 self.fedoradslinkedattributes = None
150 self.fedoradsindex = None
151 self.fedoradssamba = None
153 self.olmmrserveridsconf = None
154 self.olmmrsyncreplconf = None
157 self.olcseedldif = None
160 class ProvisionNames(object):
167 self.ldapmanagerdn = None
168 self.dnsdomain = None
170 self.netbiosname = None
177 class ProvisionResult(object):
184 class Schema(object):
185 def __init__(self, setup_path, domain_sid, schemadn=None,
186 serverdn=None, sambadn=None, ldap_backend_type=None):
187 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
189 :param samdb: Load a schema into a SamDB.
190 :param setup_path: Setup path function.
191 :param schemadn: DN of the schema
192 :param serverdn: DN of the server
194 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
198 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
199 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
200 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
201 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
202 check_all_substituted(self.schema_data)
204 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
205 {"SCHEMADN": schemadn,
206 "SERVERDN": serverdn,
209 descr = get_schema_descriptor(domain_sid)
210 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
211 {"SCHEMADN": schemadn,
215 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
216 prefixmap = b64encode(prefixmap)
220 # We don't actually add this ldif, just parse it
221 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
222 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
225 # Return a hash with the forward attribute as a key and the back as the value
226 def get_linked_attributes(schemadn,schemaldb):
227 attrs = ["linkID", "lDAPDisplayName"]
228 res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
230 for i in range (0, len(res)):
231 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
232 target = schemaldb.searchone(basedn=schemadn,
233 expression=expression,
234 attribute="lDAPDisplayName",
236 if target is not None:
237 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
241 def get_dnsyntax_attributes(schemadn,schemaldb):
242 attrs = ["linkID", "lDAPDisplayName"]
243 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
245 for i in range (0, len(res)):
246 attributes.append(str(res[i]["lDAPDisplayName"]))
251 def check_install(lp, session_info, credentials):
252 """Check whether the current install seems ok.
254 :param lp: Loadparm context
255 :param session_info: Session information
256 :param credentials: Credentials
258 if lp.get("realm") == "":
259 raise Exception("Realm empty")
260 ldb = Ldb(lp.get("sam database"), session_info=session_info,
261 credentials=credentials, lp=lp)
262 if len(ldb.search("(cn=Administrator)")) != 1:
263 raise ProvisioningError("No administrator account found")
266 def findnss(nssfn, names):
267 """Find a user or group from a list of possibilities.
269 :param nssfn: NSS Function to try (should raise KeyError if not found)
270 :param names: Names to check.
271 :return: Value return by first names list.
278 raise KeyError("Unable to find user/group %r" % names)
281 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
282 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
285 def read_and_sub_file(file, subst_vars):
286 """Read a file and sub in variables found in it
288 :param file: File to be read (typically from setup directory)
289 param subst_vars: Optional variables to subsitute in the file.
291 data = open(file, 'r').read()
292 if subst_vars is not None:
293 data = substitute_var(data, subst_vars)
294 check_all_substituted(data)
298 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
299 """Setup a ldb in the private dir.
301 :param ldb: LDB file to import data into
302 :param ldif_path: Path of the LDIF file to load
303 :param subst_vars: Optional variables to subsitute in LDIF.
304 :param nocontrols: Optional list of controls, can be None for no controls
306 assert isinstance(ldif_path, str)
307 data = read_and_sub_file(ldif_path, subst_vars)
308 ldb.add_ldif(data,controls)
311 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
312 """Modify a ldb in the private dir.
314 :param ldb: LDB object.
315 :param ldif_path: LDIF file path.
316 :param subst_vars: Optional dictionary with substitution variables.
318 data = read_and_sub_file(ldif_path, subst_vars)
320 ldb.modify_ldif(data)
323 def setup_ldb(ldb, ldif_path, subst_vars):
324 """Import a LDIF a file into a LDB handle, optionally substituting variables.
326 :note: Either all LDIF data will be added or none (using transactions).
328 :param ldb: LDB file to import into.
329 :param ldif_path: Path to the LDIF file.
330 :param subst_vars: Dictionary with substitution variables.
332 assert ldb is not None
333 ldb.transaction_start()
335 setup_add_ldif(ldb, ldif_path, subst_vars)
337 ldb.transaction_cancel()
339 ldb.transaction_commit()
342 def setup_file(template, fname, subst_vars=None):
343 """Setup a file in the private dir.
345 :param template: Path of the template file.
346 :param fname: Path of the file to create.
347 :param subst_vars: Substitution variables.
351 if os.path.exists(f):
354 data = read_and_sub_file(template, subst_vars)
355 open(f, 'w').write(data)
358 def provision_paths_from_lp(lp, dnsdomain):
359 """Set the default paths for provisioning.
361 :param lp: Loadparm context.
362 :param dnsdomain: DNS Domain name
364 paths = ProvisionPaths()
365 paths.private_dir = lp.get("private dir")
366 paths.dns_keytab = "dns.keytab"
368 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
369 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
370 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
371 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
372 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
373 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
374 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
375 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
376 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
377 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
378 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
379 paths.phpldapadminconfig = os.path.join(paths.private_dir,
380 "phpldapadmin-config.php")
381 paths.ldapdir = os.path.join(paths.private_dir,
383 paths.slapdconf = os.path.join(paths.ldapdir,
385 paths.slapdpid = os.path.join(paths.ldapdir,
387 paths.modulesconf = os.path.join(paths.ldapdir,
389 paths.memberofconf = os.path.join(paths.ldapdir,
391 paths.fedoradsinf = os.path.join(paths.ldapdir,
393 paths.fedoradspartitions = os.path.join(paths.ldapdir,
394 "fedorads-partitions.ldif")
395 paths.fedoradssasl = os.path.join(paths.ldapdir,
396 "fedorads-sasl.ldif")
397 paths.fedoradspam = os.path.join(paths.ldapdir,
399 paths.fedoradsrefint = os.path.join(paths.ldapdir,
400 "fedorads-refint.ldif")
401 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
402 "fedorads-linked-attributes.ldif")
403 paths.fedoradsindex = os.path.join(paths.ldapdir,
404 "fedorads-index.ldif")
405 paths.fedoradssamba = os.path.join(paths.ldapdir,
406 "fedorads-samba.ldif")
407 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
408 "mmr_serverids.conf")
409 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
411 paths.olcdir = os.path.join(paths.ldapdir,
413 paths.olcseedldif = os.path.join(paths.ldapdir,
415 paths.hklm = "hklm.ldb"
416 paths.hkcr = "hkcr.ldb"
417 paths.hkcu = "hkcu.ldb"
418 paths.hku = "hku.ldb"
419 paths.hkpd = "hkpd.ldb"
420 paths.hkpt = "hkpt.ldb"
422 paths.sysvol = lp.get("path", "sysvol")
424 paths.netlogon = lp.get("path", "netlogon")
426 paths.smbconf = lp.configfile
431 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
432 serverrole=None, rootdn=None, domaindn=None, configdn=None,
433 schemadn=None, serverdn=None, sitename=None, sambadn=None):
434 """Guess configuration settings to use."""
437 hostname = socket.gethostname().split(".")[0].lower()
439 netbiosname = hostname.upper()
440 if not valid_netbios_name(netbiosname):
441 raise InvalidNetbiosName(netbiosname)
443 hostname = hostname.lower()
445 if dnsdomain is None:
446 dnsdomain = lp.get("realm").lower()
448 if serverrole is None:
449 serverrole = lp.get("server role")
451 assert dnsdomain is not None
452 realm = dnsdomain.upper()
454 if lp.get("realm").upper() != realm:
455 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
456 (lp.get("realm"), lp.configfile, realm))
458 if serverrole == "domain controller":
460 domain = lp.get("workgroup")
462 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
463 if lp.get("workgroup").upper() != domain.upper():
464 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
465 lp.get("workgroup"), domain)
469 domaindn = "DC=" + netbiosname
471 assert domain is not None
472 domain = domain.upper()
474 if not valid_netbios_name(domain):
475 raise InvalidNetbiosName(domain)
477 if netbiosname.upper() == realm:
478 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
480 if hostname.upper() == realm:
481 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
483 if domain.upper() == realm:
484 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
490 configdn = "CN=Configuration," + rootdn
492 schemadn = "CN=Schema," + configdn
499 names = ProvisionNames()
500 names.rootdn = rootdn
501 names.domaindn = domaindn
502 names.configdn = configdn
503 names.schemadn = schemadn
504 names.sambadn = sambadn
505 names.ldapmanagerdn = "CN=Manager," + rootdn
506 names.dnsdomain = dnsdomain
507 names.domain = domain
509 names.netbiosname = netbiosname
510 names.hostname = hostname
511 names.sitename = sitename
512 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
517 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
519 """Create a new smb.conf file based on a couple of basic settings.
521 assert smbconf is not None
523 hostname = socket.gethostname().split(".")[0].lower()
525 if serverrole is None:
526 serverrole = "standalone"
528 assert serverrole in ("domain controller", "member server", "standalone")
529 if serverrole == "domain controller":
531 elif serverrole == "member server":
532 smbconfsuffix = "member"
533 elif serverrole == "standalone":
534 smbconfsuffix = "standalone"
536 assert domain is not None
537 assert realm is not None
539 default_lp = param.LoadParm()
540 #Load non-existant file
541 if os.path.exists(smbconf):
542 default_lp.load(smbconf)
544 if targetdir is not None:
545 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
546 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
548 default_lp.set("lock dir", os.path.abspath(targetdir))
553 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
554 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
556 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
558 "HOSTNAME": hostname,
561 "SERVERROLE": serverrole,
562 "NETLOGONPATH": netlogon,
563 "SYSVOLPATH": sysvol,
564 "PRIVATEDIR_LINE": privatedir_line,
565 "LOCKDIR_LINE": lockdir_line
569 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
570 users_gid, wheel_gid):
571 """setup reasonable name mappings for sam names to unix names.
573 :param samdb: SamDB object.
574 :param idmap: IDmap db object.
575 :param sid: The domain sid.
576 :param domaindn: The domain DN.
577 :param root_uid: uid of the UNIX root user.
578 :param nobody_uid: uid of the UNIX nobody user.
579 :param users_gid: gid of the UNIX users group.
580 :param wheel_gid: gid of the UNIX wheel group."""
582 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
583 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
585 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
586 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
588 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
589 credentials, names, schema,
590 serverrole, ldap_backend=None,
592 """Setup the partitions for the SAM database.
594 Alternatively, provision() may call this, and then populate the database.
596 :note: This will wipe the Sam Database!
598 :note: This function always removes the local SAM LDB file. The erase
599 parameter controls whether to erase the existing data, which
600 may not be stored locally but in LDAP.
603 assert session_info is not None
605 old_partitions = None
606 new_partitions = None
608 # We use options=["modules:"] to stop the modules loading - we
609 # just want to wipe and re-initialise the database, not start it up
612 samdb = Ldb(url=samdb_path, session_info=session_info,
613 credentials=credentials, lp=lp, options=["modules:"])
614 res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*")
617 old_partitions = res[0]["partition"]
621 if old_partitions is not None:
623 for old_partition in old_partitions:
624 new_partition = old_partition
625 if old_partition.endswith(".ldb"):
626 p = old_partition.split(":")[0]
627 dn = ldb.Dn(schema.ldb, p)
628 new_partition = dn.get_casefold()
629 new_partitions.append(new_partition)
632 samdb.erase_except_schema_controlled()
634 os.unlink(samdb_path)
635 samdb = Ldb(url=samdb_path, session_info=session_info,
636 credentials=credentials, lp=lp, options=["modules:"])
638 samdb.erase_except_schema_controlled()
640 #Add modules to the list to activate them by default
641 #beware often order is important
643 # Some Known ordering constraints:
644 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
645 # - objectclass must be before password_hash, because password_hash checks
646 # that the objectclass is of type person (filled in by objectclass
647 # module when expanding the objectclass list)
648 # - partition must be last
649 # - each partition has its own module list then
650 modules_list = ["resolve_oids",
673 "extended_dn_out_ldb"]
674 modules_list2 = ["show_deleted",
677 ldap_backend_line = "# No LDAP backend"
678 if ldap_backend is not None:
679 ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri
681 if ldap_backend.ldap_backend_type == "fedora-ds":
682 backend_modules = ["nsuniqueid", "paged_searches"]
683 # We can handle linked attributes here, as we don't have directory-side subtree operations
684 tdb_modules_list = ["extended_dn_out_dereference"]
685 elif ldap_backend.ldap_backend_type == "openldap":
686 backend_modules = ["entryuuid", "paged_searches"]
687 # OpenLDAP handles subtree renames, so we don't want to do any of these things
688 tdb_modules_list = ["extended_dn_out_dereference"]
690 elif serverrole == "domain controller":
691 tdb_modules_list.insert(0, "repl_meta_data")
694 backend_modules = ["objectguid"]
696 if tdb_modules_list is None:
697 tdb_modules_list_as_string = ""
699 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
701 samdb.transaction_start()
703 message("Setting up sam.ldb partitions and settings")
704 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
705 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
706 "SCHEMADN_MOD2": ",objectguid",
707 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
708 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
709 "SCHEMADN_MOD": "schema_fsmo",
710 "CONFIGDN_MOD": "naming_fsmo",
711 "DOMAINDN_MOD": "pdc_fsmo",
712 "MODULES_LIST": ",".join(modules_list),
713 "TDB_MODULES_LIST": tdb_modules_list_as_string,
714 "MODULES_LIST2": ",".join(modules_list2),
715 "BACKEND_MOD": ",".join(backend_modules),
716 "LDAP_BACKEND_LINE": ldap_backend_line,
720 if new_partitions is not None:
722 m.dn = ldb.Dn(samdb, "@PARTITION")
724 m["partition"] = ldb.MessageElement(new_partitions, ldb.FLAG_MOD_ADD, "partition")
727 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
729 message("Setting up sam.ldb rootDSE")
730 setup_samdb_rootdse(samdb, setup_path, names)
733 samdb.transaction_cancel()
736 samdb.transaction_commit()
739 def secretsdb_self_join(secretsdb, domain,
740 netbiosname, domainsid, machinepass,
741 realm=None, dnsdomain=None,
743 key_version_number=1,
744 secure_channel_type=SEC_CHAN_WKSTA):
745 """Add domain join-specific bits to a secrets database.
747 :param secretsdb: Ldb Handle to the secrets database
748 :param machinepass: Machine password
750 attrs=["whenChanged",
758 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
759 msg["secureChannelType"] = str(secure_channel_type)
760 msg["flatname"] = [domain]
761 msg["objectClass"] = ["top", "primaryDomain"]
762 if realm is not None:
763 if dnsdomain is None:
764 dnsdomain = realm.lower()
765 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
767 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
768 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
769 msg["privateKeytab"] = ["secrets.keytab"];
772 msg["secret"] = [machinepass]
773 msg["samAccountName"] = ["%s$" % netbiosname]
774 msg["secureChannelType"] = [str(secure_channel_type)]
775 msg["objectSid"] = [ndr_pack(domainsid)]
777 res = secretsdb.search(base="cn=Primary Domains",
779 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
780 scope=SCOPE_ONELEVEL)
783 if del_msg.dn is not msg.dn:
784 secretsdb.delete(del_msg.dn)
786 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
789 msg["priorSecret"] = res[0]["secret"]
790 msg["priorWhenChanged"] = res[0]["whenChanged"]
792 if res["privateKeytab"] is not None:
793 msg["privateKeytab"] = res[0]["privateKeytab"]
795 if res["krb5Keytab"] is not None:
796 msg["krb5Keytab"] = res[0]["krb5Keytab"]
799 el.set_flags(ldb.FLAG_MOD_REPLACE)
800 secretsdb.modify(msg)
805 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
806 dns_keytab_path, dnspass):
807 """Add DNS specific bits to a secrets database.
809 :param secretsdb: Ldb Handle to the secrets database
810 :param setup_path: Setup path function
811 :param machinepass: Machine password
813 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
815 "DNSDOMAIN": dnsdomain,
816 "DNS_KEYTAB": dns_keytab_path,
817 "DNSPASS_B64": b64encode(dnspass),
821 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
822 """Setup the secrets database.
824 :param path: Path to the secrets database.
825 :param setup_path: Get the path to a setup file.
826 :param session_info: Session info.
827 :param credentials: Credentials
828 :param lp: Loadparm context
829 :return: LDB handle for the created secrets database
831 if os.path.exists(path):
833 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
836 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
837 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
839 secrets_ldb.transaction_start()
840 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
842 if credentials is not None and credentials.authentication_requested():
843 if credentials.get_bind_dn() is not None:
844 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
845 "LDAPMANAGERDN": credentials.get_bind_dn(),
846 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
849 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
850 "LDAPADMINUSER": credentials.get_username(),
851 "LDAPADMINREALM": credentials.get_realm(),
852 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
857 def setup_privileges(path, setup_path, session_info, lp):
858 """Setup the privileges database.
860 :param path: Path to the privileges database.
861 :param setup_path: Get the path to a setup file.
862 :param session_info: Session info.
863 :param credentials: Credentials
864 :param lp: Loadparm context
865 :return: LDB handle for the created secrets database
867 if os.path.exists(path):
869 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
870 privilege_ldb.erase()
871 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
874 def setup_registry(path, setup_path, session_info, lp):
875 """Setup the registry.
877 :param path: Path to the registry database
878 :param setup_path: Function that returns the path to a setup.
879 :param session_info: Session information
880 :param credentials: Credentials
881 :param lp: Loadparm context
883 reg = registry.Registry()
884 hive = registry.open_ldb(path, session_info=session_info,
886 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
887 provision_reg = setup_path("provision.reg")
888 assert os.path.exists(provision_reg)
889 reg.diff_apply(provision_reg)
892 def setup_idmapdb(path, setup_path, session_info, lp):
893 """Setup the idmap database.
895 :param path: path to the idmap database
896 :param setup_path: Function that returns a path to a setup file
897 :param session_info: Session information
898 :param credentials: Credentials
899 :param lp: Loadparm context
901 if os.path.exists(path):
904 idmap_ldb = IDmapDB(path, session_info=session_info,
908 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
912 def setup_samdb_rootdse(samdb, setup_path, names):
913 """Setup the SamDB rootdse.
915 :param samdb: Sam Database handle
916 :param setup_path: Obtain setup path
918 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
919 "SCHEMADN": names.schemadn,
920 "NETBIOSNAME": names.netbiosname,
921 "DNSDOMAIN": names.dnsdomain,
922 "REALM": names.realm,
923 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
924 "DOMAINDN": names.domaindn,
925 "ROOTDN": names.rootdn,
926 "CONFIGDN": names.configdn,
927 "SERVERDN": names.serverdn,
931 def setup_self_join(samdb, names,
932 machinepass, dnspass,
933 domainsid, invocationid, setup_path,
934 policyguid, policyguid_dc, domainControllerFunctionality,
936 """Join a host to its own domain."""
937 assert isinstance(invocationid, str)
938 if ntdsguid is not None:
939 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
942 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
943 "CONFIGDN": names.configdn,
944 "SCHEMADN": names.schemadn,
945 "DOMAINDN": names.domaindn,
946 "SERVERDN": names.serverdn,
947 "INVOCATIONID": invocationid,
948 "NETBIOSNAME": names.netbiosname,
949 "DEFAULTSITE": names.sitename,
950 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
951 "MACHINEPASS_B64": b64encode(machinepass),
952 "DNSPASS_B64": b64encode(dnspass),
953 "REALM": names.realm,
954 "DOMAIN": names.domain,
955 "DNSDOMAIN": names.dnsdomain,
956 "SAMBA_VERSION_STRING": version,
957 "NTDSGUID": ntdsguid_line,
958 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
960 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
961 "POLICYGUID": policyguid,
962 "POLICYGUID_DC": policyguid_dc,
963 "DNSDOMAIN": names.dnsdomain,
964 "DOMAINSID": str(domainsid),
965 "DOMAINDN": names.domaindn})
967 # add the NTDSGUID based SPNs
968 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
969 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
970 expression="", scope=SCOPE_BASE)
971 assert isinstance(names.ntdsguid, str)
973 # Setup fSMORoleOwner entries to point at the newly created DC entry
974 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
975 "DOMAIN": names.domain,
976 "DNSDOMAIN": names.dnsdomain,
977 "DOMAINDN": names.domaindn,
978 "CONFIGDN": names.configdn,
979 "SCHEMADN": names.schemadn,
980 "DEFAULTSITE": names.sitename,
981 "SERVERDN": names.serverdn,
982 "NETBIOSNAME": names.netbiosname,
983 "NTDSGUID": names.ntdsguid
987 def setup_samdb(path, setup_path, session_info, credentials, lp,
989 domainsid, domainguid, policyguid, policyguid_dc,
990 fill, adminpass, krbtgtpass,
991 machinepass, invocationid, dnspass, ntdsguid,
992 serverrole, dom_for_fun_level=None,
993 schema=None, ldap_backend=None):
994 """Setup a complete SAM Database.
996 :note: This will wipe the main SAM database file!
999 # ATTENTION: Do NOT change these default values without discussion with the
1000 # team and/or release manager. They have a big impact on the whole program!
1001 domainControllerFunctionality = DS_DC_FUNCTION_2008
1003 if dom_for_fun_level is None:
1004 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1005 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
1006 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
1008 if dom_for_fun_level > domainControllerFunctionality:
1009 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!")
1011 domainFunctionality = dom_for_fun_level
1012 forestFunctionality = dom_for_fun_level
1014 # Also wipes the database
1015 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
1016 credentials=credentials, session_info=session_info,
1017 names=names, ldap_backend=ldap_backend,
1018 serverrole=serverrole, schema=schema)
1020 if (schema == None):
1021 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1022 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
1024 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1025 samdb = Ldb(session_info=session_info,
1026 credentials=credentials, lp=lp)
1028 message("Pre-loading the Samba 4 and AD schema")
1030 # Load the schema from the one we computed earlier
1031 samdb.set_schema_from_ldb(schema.ldb)
1033 # And now we can connect to the DB - the schema won't be loaded from the DB
1036 if fill == FILL_DRS:
1039 samdb.transaction_start()
1041 message("Erasing data from partitions")
1042 # Load the schema (again). This time it will force a reindex,
1043 # and will therefore make the erase_partitions() below
1044 # computationally sane
1045 samdb.set_schema_from_ldb(schema.ldb)
1046 samdb.erase_partitions()
1048 # Set the domain functionality levels onto the database.
1049 # Various module (the password_hash module in particular) need
1050 # to know what level of AD we are emulating.
1052 # These will be fixed into the database via the database
1053 # modifictions below, but we need them set from the start.
1054 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1055 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1056 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1058 samdb.set_domain_sid(str(domainsid))
1059 if serverrole == "domain controller":
1060 samdb.set_invocation_id(invocationid)
1062 message("Adding DomainDN: %s" % names.domaindn)
1064 #impersonate domain admin
1065 admin_session_info = admin_session(lp, str(domainsid))
1066 samdb.set_session_info(admin_session_info)
1067 if domainguid is not None:
1068 domainguid_line = "objectGUID: %s\n-" % domainguid
1070 domainguid_line = ""
1071 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1072 "DOMAINDN": names.domaindn,
1073 "DOMAINGUID": domainguid_line
1077 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1078 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1079 "DOMAINSID": str(domainsid),
1080 "SCHEMADN": names.schemadn,
1081 "NETBIOSNAME": names.netbiosname,
1082 "DEFAULTSITE": names.sitename,
1083 "CONFIGDN": names.configdn,
1084 "SERVERDN": names.serverdn,
1085 "POLICYGUID": policyguid,
1086 "DOMAINDN": names.domaindn,
1087 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1088 "SAMBA_VERSION_STRING": version
1091 message("Adding configuration container")
1092 descr = get_config_descriptor(domainsid);
1093 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1094 "CONFIGDN": names.configdn,
1095 "DESCRIPTOR": descr,
1097 message("Modifying configuration container")
1098 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1099 "CONFIGDN": names.configdn,
1100 "SCHEMADN": names.schemadn,
1103 # The LDIF here was created when the Schema object was constructed
1104 message("Setting up sam.ldb schema")
1105 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1106 samdb.modify_ldif(schema.schema_dn_modify)
1107 samdb.write_prefixes_from_schema()
1108 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1109 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1110 {"SCHEMADN": names.schemadn})
1112 message("Setting up sam.ldb configuration data")
1113 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1114 "CONFIGDN": names.configdn,
1115 "NETBIOSNAME": names.netbiosname,
1116 "DEFAULTSITE": names.sitename,
1117 "DNSDOMAIN": names.dnsdomain,
1118 "DOMAIN": names.domain,
1119 "SCHEMADN": names.schemadn,
1120 "DOMAINDN": names.domaindn,
1121 "SERVERDN": names.serverdn,
1122 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1125 message("Setting up display specifiers")
1126 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1127 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1128 check_all_substituted(display_specifiers_ldif)
1129 samdb.add_ldif(display_specifiers_ldif)
1131 message("Adding users container")
1132 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1133 "DOMAINDN": names.domaindn})
1134 message("Modifying users container")
1135 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1136 "DOMAINDN": names.domaindn})
1137 message("Adding computers container")
1138 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1139 "DOMAINDN": names.domaindn})
1140 message("Modifying computers container")
1141 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1142 "DOMAINDN": names.domaindn})
1143 message("Setting up sam.ldb data")
1144 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1145 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1146 "DOMAINDN": names.domaindn,
1147 "NETBIOSNAME": names.netbiosname,
1148 "DEFAULTSITE": names.sitename,
1149 "CONFIGDN": names.configdn,
1150 "SERVERDN": names.serverdn,
1151 "POLICYGUID_DC": policyguid_dc
1154 if fill == FILL_FULL:
1155 message("Setting up sam.ldb users and groups")
1156 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1157 "DOMAINDN": names.domaindn,
1158 "DOMAINSID": str(domainsid),
1159 "CONFIGDN": names.configdn,
1160 "ADMINPASS_B64": b64encode(adminpass),
1161 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1164 if serverrole == "domain controller":
1165 message("Setting up self join")
1166 setup_self_join(samdb, names=names, invocationid=invocationid,
1168 machinepass=machinepass,
1169 domainsid=domainsid, policyguid=policyguid,
1170 policyguid_dc=policyguid_dc,
1171 setup_path=setup_path,
1172 domainControllerFunctionality=domainControllerFunctionality,
1175 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1176 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1177 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1178 assert isinstance(names.ntdsguid, str)
1181 samdb.transaction_cancel()
1184 samdb.transaction_commit()
1189 FILL_NT4SYNC = "NT4SYNC"
1193 def provision(setup_dir, message, session_info,
1194 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1196 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1198 domain=None, hostname=None, hostip=None, hostip6=None,
1199 domainsid=None, adminpass=None, ldapadminpass=None,
1200 krbtgtpass=None, domainguid=None,
1201 policyguid=None, policyguid_dc=None, invocationid=None,
1202 machinepass=None, ntdsguid=None,
1203 dnspass=None, root=None, nobody=None, users=None,
1204 wheel=None, backup=None, aci=None, serverrole=None,
1205 dom_for_fun_level=None,
1206 ldap_backend_extra_port=None, ldap_backend_type=None,
1208 ol_mmr_urls=None, ol_olc=None,
1209 setup_ds_path=None, slapd_path=None, nosync=False,
1210 ldap_dryrun_mode=False):
1213 :note: caution, this wipes all existing data!
1216 def setup_path(file):
1217 return os.path.join(setup_dir, file)
1219 if domainsid is None:
1220 domainsid = security.random_sid()
1222 domainsid = security.dom_sid(domainsid)
1224 # create/adapt the group policy GUIDs
1225 if policyguid is None:
1226 policyguid = str(uuid.uuid4())
1227 policyguid = policyguid.upper()
1228 if policyguid_dc is None:
1229 policyguid_dc = str(uuid.uuid4())
1230 policyguid_dc = policyguid_dc.upper()
1232 if adminpass is None:
1233 adminpass = glue.generate_random_str(12)
1234 if krbtgtpass is None:
1235 krbtgtpass = glue.generate_random_str(12)
1236 if machinepass is None:
1237 machinepass = glue.generate_random_str(12)
1239 dnspass = glue.generate_random_str(12)
1240 if ldapadminpass is None:
1241 #Make a new, random password between Samba and it's LDAP server
1242 ldapadminpass=glue.generate_random_str(12)
1245 root_uid = findnss_uid([root or "root"])
1246 nobody_uid = findnss_uid([nobody or "nobody"])
1247 users_gid = findnss_gid([users or "users"])
1249 wheel_gid = findnss_gid(["wheel", "adm"])
1251 wheel_gid = findnss_gid([wheel])
1253 if targetdir is not None:
1254 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1255 os.makedirs(os.path.join(targetdir, "etc"))
1256 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1257 elif smbconf is None:
1258 smbconf = param.default_path()
1260 # only install a new smb.conf if there isn't one there already
1261 if not os.path.exists(smbconf):
1262 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1265 lp = param.LoadParm()
1268 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1269 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1270 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1273 paths = provision_paths_from_lp(lp, names.dnsdomain)
1277 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1278 except socket.gaierror, (socket.EAI_NODATA, msg):
1283 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1284 except socket.gaierror, (socket.EAI_NODATA, msg):
1287 if serverrole is None:
1288 serverrole = lp.get("server role")
1290 assert serverrole in ("domain controller", "member server", "standalone")
1291 if invocationid is None and serverrole == "domain controller":
1292 invocationid = str(uuid.uuid4())
1294 if not os.path.exists(paths.private_dir):
1295 os.mkdir(paths.private_dir)
1297 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1299 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1300 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1302 secrets_credentials = credentials
1303 provision_backend = None
1304 if ldap_backend_type:
1305 # We only support an LDAP backend over ldapi://
1307 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1308 lp=lp, credentials=credentials,
1310 message=message, hostname=hostname,
1311 root=root, schema=schema,
1312 ldap_backend_type=ldap_backend_type,
1313 ldapadminpass=ldapadminpass,
1314 ldap_backend_extra_port=ldap_backend_extra_port,
1315 ol_mmr_urls=ol_mmr_urls,
1316 slapd_path=slapd_path,
1317 setup_ds_path=setup_ds_path,
1318 ldap_dryrun_mode=ldap_dryrun_mode)
1320 # Now use the backend credentials to access the databases
1321 credentials = provision_backend.credentials
1322 secrets_credentials = provision_backend.adminCredentials
1323 ldapi_url = provision_backend.ldapi_uri
1325 # only install a new shares config db if there is none
1326 if not os.path.exists(paths.shareconf):
1327 message("Setting up share.ldb")
1328 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1329 credentials=credentials, lp=lp)
1330 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1333 message("Setting up secrets.ldb")
1334 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1335 session_info=session_info,
1336 credentials=secrets_credentials, lp=lp)
1338 message("Setting up the registry")
1339 setup_registry(paths.hklm, setup_path, session_info,
1342 message("Setting up the privileges database")
1343 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1345 message("Setting up idmap db")
1346 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1349 message("Setting up SAM db")
1350 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1351 credentials=credentials, lp=lp, names=names,
1353 domainsid=domainsid,
1354 schema=schema, domainguid=domainguid,
1355 policyguid=policyguid, policyguid_dc=policyguid_dc,
1357 adminpass=adminpass, krbtgtpass=krbtgtpass,
1358 invocationid=invocationid,
1359 machinepass=machinepass, dnspass=dnspass,
1360 ntdsguid=ntdsguid, serverrole=serverrole,
1361 dom_for_fun_level=dom_for_fun_level,
1362 ldap_backend=provision_backend)
1364 if serverrole == "domain controller":
1365 if paths.netlogon is None:
1366 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1367 message("Please either remove %s or see the template at %s" %
1368 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1369 assert(paths.netlogon is not None)
1371 if paths.sysvol is None:
1372 message("Existing smb.conf does not have a [sysvol] 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.sysvol is not None)
1377 # Set up group policies (domain policy and domain controller policy)
1379 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1380 "{" + policyguid + "}")
1381 os.makedirs(policy_path, 0755)
1382 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1383 "[General]\r\nVersion=65543")
1384 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1385 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1387 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1388 "{" + policyguid_dc + "}")
1389 os.makedirs(policy_path_dc, 0755)
1390 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1391 "[General]\r\nVersion=2")
1392 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1393 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1395 if not os.path.isdir(paths.netlogon):
1396 os.makedirs(paths.netlogon, 0755)
1398 if samdb_fill == FILL_FULL:
1399 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1400 root_uid=root_uid, nobody_uid=nobody_uid,
1401 users_gid=users_gid, wheel_gid=wheel_gid)
1403 message("Setting up sam.ldb rootDSE marking as synchronized")
1404 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1406 # Only make a zone file on the first DC, it should be replicated with DNS replication
1407 if serverrole == "domain controller":
1408 secretsdb_self_join(secrets_ldb, domain=domain,
1410 dnsdomain=names.dnsdomain,
1411 netbiosname=names.netbiosname,
1412 domainsid=domainsid,
1413 machinepass=machinepass,
1414 secure_channel_type=SEC_CHAN_BDC)
1416 secretsdb_setup_dns(secrets_ldb, setup_path,
1417 realm=names.realm, dnsdomain=names.dnsdomain,
1418 dns_keytab_path=paths.dns_keytab,
1421 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1422 assert isinstance(domainguid, str)
1424 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1426 hostip6=hostip6, hostname=names.hostname,
1428 domainguid=domainguid, ntdsguid=names.ntdsguid)
1430 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1431 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1433 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1434 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1435 keytab_name=paths.dns_keytab)
1436 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1437 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1439 create_krb5_conf(paths.krb5conf, setup_path,
1440 dnsdomain=names.dnsdomain, hostname=names.hostname,
1442 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1444 #Now commit the secrets.ldb to disk
1445 secrets_ldb.transaction_commit()
1447 if provision_backend is not None:
1448 if ldap_backend_type == "fedora-ds":
1449 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1451 # delete default SASL mappings
1452 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1454 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1455 for i in range (0, len(res)):
1456 dn = str(res[i]["dn"])
1459 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1462 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1464 m.dn = ldb.Dn(1, names.domaindn)
1467 m.dn = ldb.Dn(1, names.configdn)
1470 m.dn = ldb.Dn(1, names.schemadn)
1473 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1474 if provision_backend.slapd.poll() is None:
1476 if hasattr(provision_backend.slapd, "terminate"):
1477 provision_backend.slapd.terminate()
1479 # Older python versions don't have .terminate()
1481 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1483 #and now wait for it to die
1484 provision_backend.slapd.communicate()
1486 # now display slapd_command_file.txt to show how slapd must be started next time
1487 message("Use later the following commandline to start slapd, then Samba:")
1488 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1489 message(slapd_command)
1490 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1492 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1493 "SLAPD_COMMAND" : slapd_command})
1496 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1499 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1501 message("Once the above files are installed, your Samba4 server will be ready to use")
1502 message("Server Role: %s" % serverrole)
1503 message("Hostname: %s" % names.hostname)
1504 message("NetBIOS Domain: %s" % names.domain)
1505 message("DNS Domain: %s" % names.dnsdomain)
1506 message("DOMAIN SID: %s" % str(domainsid))
1507 if samdb_fill == FILL_FULL:
1508 message("Admin password: %s" % adminpass)
1509 if provision_backend:
1510 if provision_backend.credentials.get_bind_dn() is not None:
1511 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1513 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1515 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1517 result = ProvisionResult()
1518 result.domaindn = domaindn
1519 result.paths = paths
1521 result.samdb = samdb
1526 def provision_become_dc(setup_dir=None,
1527 smbconf=None, targetdir=None, realm=None,
1528 rootdn=None, domaindn=None, schemadn=None,
1529 configdn=None, serverdn=None,
1530 domain=None, hostname=None, domainsid=None,
1531 adminpass=None, krbtgtpass=None, domainguid=None,
1532 policyguid=None, policyguid_dc=None, invocationid=None,
1534 dnspass=None, root=None, nobody=None, users=None,
1535 wheel=None, backup=None, serverrole=None,
1536 ldap_backend=None, ldap_backend_type=None,
1537 sitename=None, debuglevel=1):
1540 """print a message if quiet is not set."""
1543 glue.set_debug_level(debuglevel)
1545 return provision(setup_dir, message, system_session(), None,
1546 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1547 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1548 configdn=configdn, serverdn=serverdn, domain=domain,
1549 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1550 machinepass=machinepass, serverrole="domain controller",
1554 def setup_db_config(setup_path, dbdir):
1555 """Setup a Berkeley database.
1557 :param setup_path: Setup path function.
1558 :param dbdir: Database directory."""
1559 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1560 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1561 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1562 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1564 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1565 {"LDAPDBDIR": dbdir})
1567 class ProvisionBackend(object):
1568 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1569 names=None, message=None,
1570 hostname=None, root=None,
1571 schema=None, ldapadminpass=None,
1572 ldap_backend_type=None, ldap_backend_extra_port=None,
1574 setup_ds_path=None, slapd_path=None,
1575 nosync=False, ldap_dryrun_mode=False):
1576 """Provision an LDAP backend for samba4
1578 This works for OpenLDAP and Fedora DS
1581 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1583 if not os.path.isdir(paths.ldapdir):
1584 os.makedirs(paths.ldapdir, 0700)
1586 if ldap_backend_type == "existing":
1587 #Check to see that this 'existing' LDAP backend in fact exists
1588 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1589 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1590 expression="(objectClass=OpenLDAProotDSE)")
1592 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1593 # This caused them to be set into the long-term database later in the script.
1594 self.credentials = credentials
1595 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1598 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1599 # if another instance of slapd is already running
1601 ldapi_db = Ldb(self.ldapi_uri)
1602 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1603 expression="(objectClass=OpenLDAProotDSE)");
1605 f = open(paths.slapdpid, "r")
1608 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1612 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1617 # Try to print helpful messages when the user has not specified the path to slapd
1618 if slapd_path is None:
1619 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1620 if not os.path.exists(slapd_path):
1621 message (slapd_path)
1622 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1624 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1626 os.unlink(schemadb_path)
1631 # Put the LDIF of the schema into a database so we can search on
1632 # it to generate schema-dependent configurations in Fedora DS and
1634 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1635 schema.ldb.connect(schemadb_path)
1636 schema.ldb.transaction_start()
1638 # These bits of LDIF are supplied when the Schema object is created
1639 schema.ldb.add_ldif(schema.schema_dn_add)
1640 schema.ldb.modify_ldif(schema.schema_dn_modify)
1641 schema.ldb.add_ldif(schema.schema_data)
1642 schema.ldb.transaction_commit()
1644 self.credentials = Credentials()
1645 self.credentials.guess(lp)
1646 #Kerberos to an ldapi:// backend makes no sense
1647 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1649 self.adminCredentials = Credentials()
1650 self.adminCredentials.guess(lp)
1651 #Kerberos to an ldapi:// backend makes no sense
1652 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1654 self.ldap_backend_type = ldap_backend_type
1656 if ldap_backend_type == "fedora-ds":
1657 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1658 names=names, message=message,
1660 ldapadminpass=ldapadminpass, root=root,
1662 ldap_backend_extra_port=ldap_backend_extra_port,
1663 setup_ds_path=setup_ds_path,
1664 slapd_path=slapd_path,
1666 ldap_dryrun_mode=ldap_dryrun_mode)
1668 elif ldap_backend_type == "openldap":
1669 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1670 names=names, message=message,
1672 ldapadminpass=ldapadminpass, root=root,
1674 ldap_backend_extra_port=ldap_backend_extra_port,
1675 ol_mmr_urls=ol_mmr_urls,
1676 slapd_path=slapd_path,
1678 ldap_dryrun_mode=ldap_dryrun_mode)
1680 raise ProvisioningError("Unknown LDAP backend type selected")
1682 self.credentials.set_password(ldapadminpass)
1683 self.adminCredentials.set_username("samba-admin")
1684 self.adminCredentials.set_password(ldapadminpass)
1686 # Now start the slapd, so we can provision onto it. We keep the
1687 # subprocess context around, to kill this off at the successful
1689 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1691 while self.slapd.poll() is None:
1692 # Wait until the socket appears
1694 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1695 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1696 expression="(objectClass=OpenLDAProotDSE)")
1697 # If we have got here, then we must have a valid connection to the LDAP server!
1703 raise ProvisioningError("slapd died before we could make a connection to it")
1706 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1708 hostname=None, ldapadminpass=None, root=None,
1710 ldap_backend_extra_port=None,
1712 slapd_path=None, nosync=False,
1713 ldap_dryrun_mode=False):
1715 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1718 nosync_config = "dbnosync"
1720 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1721 refint_attributes = ""
1722 memberof_config = "# Generated from Samba4 schema\n"
1723 for att in lnkattr.keys():
1724 if lnkattr[att] is not None:
1725 refint_attributes = refint_attributes + " " + att
1727 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1728 { "MEMBER_ATTR" : att ,
1729 "MEMBEROF_ATTR" : lnkattr[att] })
1731 refint_config = read_and_sub_file(setup_path("refint.conf"),
1732 { "LINK_ATTRS" : refint_attributes})
1734 attrs = ["linkID", "lDAPDisplayName"]
1735 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1737 for i in range (0, len(res)):
1738 index_attr = res[i]["lDAPDisplayName"][0]
1739 if index_attr == "objectGUID":
1740 index_attr = "entryUUID"
1742 index_config += "index " + index_attr + " eq\n"
1744 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1746 mmr_replicator_acl = ""
1747 mmr_serverids_config = ""
1748 mmr_syncrepl_schema_config = ""
1749 mmr_syncrepl_config_config = ""
1750 mmr_syncrepl_user_config = ""
1753 if ol_mmr_urls is not None:
1754 # For now, make these equal
1755 mmr_pass = ldapadminpass
1757 url_list=filter(None,ol_mmr_urls.split(' '))
1758 if (len(url_list) == 1):
1759 url_list=filter(None,ol_mmr_urls.split(','))
1762 mmr_on_config = "MirrorMode On"
1763 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1765 for url in url_list:
1767 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1768 { "SERVERID" : str(serverid),
1769 "LDAPSERVER" : url })
1772 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1774 "MMRDN": names.schemadn,
1776 "MMR_PASSWORD": mmr_pass})
1779 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1781 "MMRDN": names.configdn,
1783 "MMR_PASSWORD": mmr_pass})
1786 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1788 "MMRDN": names.domaindn,
1790 "MMR_PASSWORD": mmr_pass })
1791 # OpenLDAP cn=config initialisation
1792 olc_syncrepl_config = ""
1794 # if mmr = yes, generate cn=config-replication directives
1795 # and olc_seed.lif for the other mmr-servers
1796 if ol_mmr_urls is not None:
1798 olc_serverids_config = ""
1799 olc_syncrepl_seed_config = ""
1800 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1802 for url in url_list:
1804 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1805 { "SERVERID" : str(serverid),
1806 "LDAPSERVER" : url })
1809 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1812 "MMR_PASSWORD": mmr_pass})
1814 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1816 "LDAPSERVER" : url})
1818 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1819 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1820 "OLC_PW": ldapadminpass,
1821 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1824 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1825 {"DNSDOMAIN": names.dnsdomain,
1826 "LDAPDIR": paths.ldapdir,
1827 "DOMAINDN": names.domaindn,
1828 "CONFIGDN": names.configdn,
1829 "SCHEMADN": names.schemadn,
1830 "MEMBEROF_CONFIG": memberof_config,
1831 "MIRRORMODE": mmr_on_config,
1832 "REPLICATOR_ACL": mmr_replicator_acl,
1833 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1834 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1835 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1836 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1837 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1838 "OLC_MMR_CONFIG": olc_mmr_config,
1839 "REFINT_CONFIG": refint_config,
1840 "INDEX_CONFIG": index_config,
1841 "NOSYNC": nosync_config})
1843 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1844 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1845 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1847 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1848 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1850 setup_file(setup_path("cn=samba.ldif"),
1851 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1852 { "UUID": str(uuid.uuid4()),
1853 "LDAPTIME": timestring(int(time.time()))} )
1854 setup_file(setup_path("cn=samba-admin.ldif"),
1855 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1856 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1857 "UUID": str(uuid.uuid4()),
1858 "LDAPTIME": timestring(int(time.time()))} )
1860 if ol_mmr_urls is not None:
1861 setup_file(setup_path("cn=replicator.ldif"),
1862 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1863 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1864 "UUID": str(uuid.uuid4()),
1865 "LDAPTIME": timestring(int(time.time()))} )
1868 mapping = "schema-map-openldap-2.3"
1869 backend_schema = "backend-schema.schema"
1871 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1872 assert backend_schema_data is not None
1873 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1875 # now we generate the needed strings to start slapd automatically,
1876 # first ldapi_uri...
1877 if ldap_backend_extra_port is not None:
1878 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1879 # specified there as part of it's clue as to it's own name,
1880 # and not to replicate to itself
1881 if ol_mmr_urls is None:
1882 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1884 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1886 server_port_string = ""
1888 # Prepare the 'result' information - the commands to return in particular
1889 result.slapd_provision_command = [slapd_path]
1891 result.slapd_provision_command.append("-F" + paths.olcdir)
1893 result.slapd_provision_command.append("-h")
1895 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1896 result.slapd_command = list(result.slapd_provision_command)
1898 result.slapd_provision_command.append(result.ldapi_uri)
1899 result.slapd_provision_command.append("-d0")
1901 uris = result.ldapi_uri
1902 if server_port_string is not "":
1903 uris = uris + " " + server_port_string
1905 result.slapd_command.append(uris)
1907 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1908 result.credentials.set_username("samba-admin")
1910 # If we were just looking for crashes up to this point, it's a
1911 # good time to exit before we realise we don't have OpenLDAP on
1913 if ldap_dryrun_mode:
1916 # Finally, convert the configuration into cn=config style!
1917 if not os.path.isdir(paths.olcdir):
1918 os.makedirs(paths.olcdir, 0770)
1920 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1922 # We can't do this, as OpenLDAP is strange. It gives an error
1923 # output to the above, but does the conversion sucessfully...
1926 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1928 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1929 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1931 # Don't confuse the admin by leaving the slapd.conf around
1932 os.remove(paths.slapdconf)
1935 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1937 hostname=None, ldapadminpass=None, root=None,
1939 ldap_backend_extra_port=None,
1943 ldap_dryrun_mode=False):
1945 if ldap_backend_extra_port is not None:
1946 serverport = "ServerPort=%d" % ldap_backend_extra_port
1950 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1952 "HOSTNAME": hostname,
1953 "DNSDOMAIN": names.dnsdomain,
1954 "LDAPDIR": paths.ldapdir,
1955 "DOMAINDN": names.domaindn,
1956 "LDAPMANAGERDN": names.ldapmanagerdn,
1957 "LDAPMANAGERPASS": ldapadminpass,
1958 "SERVERPORT": serverport})
1960 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1961 {"CONFIGDN": names.configdn,
1962 "SCHEMADN": names.schemadn,
1963 "SAMBADN": names.sambadn,
1966 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1967 {"SAMBADN": names.sambadn,
1970 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1972 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1974 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1975 memberof_config = ""
1979 for attr in lnkattr.keys():
1980 if lnkattr[attr] is not None:
1981 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1982 { "ARG_NUMBER" : str(argnum) ,
1983 "LINK_ATTR" : attr })
1984 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1985 { "MEMBER_ATTR" : attr ,
1986 "MEMBEROF_ATTR" : lnkattr[attr] })
1987 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1991 open(paths.fedoradsrefint, 'w').write(refint_config)
1992 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
1994 attrs = ["lDAPDisplayName"]
1995 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1997 for i in range (0, len(res)):
1998 attr = res[i]["lDAPDisplayName"][0]
2000 if attr == "objectGUID":
2003 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2006 open(paths.fedoradsindex, 'w').write(index_config)
2008 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
2009 {"SAMBADN": names.sambadn,
2010 "LDAPADMINPASS": ldapadminpass
2013 mapping = "schema-map-fedora-ds-1.0"
2014 backend_schema = "99_ad.ldif"
2016 # Build a schema file in Fedora DS format
2017 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
2018 assert backend_schema_data is not None
2019 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2021 result.credentials.set_bind_dn(names.ldapmanagerdn)
2023 # Destory the target directory, or else setup-ds.pl will complain
2024 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
2025 shutil.rmtree(fedora_ds_dir, True)
2027 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2028 #In the 'provision' command line, stay in the foreground so we can easily kill it
2029 result.slapd_provision_command.append("-d0")
2031 #the command for the final run is the normal script
2032 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2034 # If we were just looking for crashes up to this point, it's a
2035 # good time to exit before we realise we don't have Fedora DS on
2036 if ldap_dryrun_mode:
2039 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2040 if setup_ds_path is None:
2041 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\"!")
2042 if not os.path.exists(setup_ds_path):
2043 message (setup_ds_path)
2044 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2046 # Run the Fedora DS setup utility
2047 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2049 raise ProvisioningError("setup-ds failed")
2052 retcode = subprocess.call([
2053 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2054 close_fds=True, shell=False)
2056 raise("ldib2db failed")
2058 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2059 """Create a PHP LDAP admin configuration file.
2061 :param path: Path to write the configuration to.
2062 :param setup_path: Function to generate setup paths.
2064 setup_file(setup_path("phpldapadmin-config.php"), path,
2065 {"S4_LDAPI_URI": ldapi_uri})
2068 def create_zone_file(path, setup_path, dnsdomain,
2069 hostip, hostip6, hostname, realm, domainguid,
2071 """Write out a DNS zone file, from the info in the current database.
2073 :param path: Path of the new zone file.
2074 :param setup_path: Setup path function.
2075 :param dnsdomain: DNS Domain name
2076 :param domaindn: DN of the Domain
2077 :param hostip: Local IPv4 IP
2078 :param hostip6: Local IPv6 IP
2079 :param hostname: Local hostname
2080 :param realm: Realm name
2081 :param domainguid: GUID of the domain.
2082 :param ntdsguid: GUID of the hosts nTDSDSA record.
2084 assert isinstance(domainguid, str)
2086 if hostip6 is not None:
2087 hostip6_base_line = " IN AAAA " + hostip6
2088 hostip6_host_line = hostname + " IN AAAA " + hostip6
2090 hostip6_base_line = ""
2091 hostip6_host_line = ""
2093 if hostip is not None:
2094 hostip_base_line = " IN A " + hostip
2095 hostip_host_line = hostname + " IN A " + hostip
2097 hostip_base_line = ""
2098 hostip_host_line = ""
2100 setup_file(setup_path("provision.zone"), path, {
2101 "HOSTNAME": hostname,
2102 "DNSDOMAIN": dnsdomain,
2104 "HOSTIP_BASE_LINE": hostip_base_line,
2105 "HOSTIP_HOST_LINE": hostip_host_line,
2106 "DOMAINGUID": domainguid,
2107 "DATESTRING": time.strftime("%Y%m%d%H"),
2108 "DEFAULTSITE": DEFAULTSITE,
2109 "NTDSGUID": ntdsguid,
2110 "HOSTIP6_BASE_LINE": hostip6_base_line,
2111 "HOSTIP6_HOST_LINE": hostip6_host_line,
2115 def create_named_conf(path, setup_path, realm, dnsdomain,
2117 """Write out a file containing zone statements suitable for inclusion in a
2118 named.conf file (including GSS-TSIG configuration).
2120 :param path: Path of the new named.conf file.
2121 :param setup_path: Setup path function.
2122 :param realm: Realm name
2123 :param dnsdomain: DNS Domain name
2124 :param private_dir: Path to private directory
2125 :param keytab_name: File name of DNS keytab file
2128 setup_file(setup_path("named.conf"), path, {
2129 "DNSDOMAIN": dnsdomain,
2131 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2132 "PRIVATE_DIR": private_dir
2135 def create_named_txt(path, setup_path, realm, dnsdomain,
2136 private_dir, keytab_name):
2137 """Write out a file containing zone statements suitable for inclusion in a
2138 named.conf file (including GSS-TSIG configuration).
2140 :param path: Path of the new named.conf file.
2141 :param setup_path: Setup path function.
2142 :param realm: Realm name
2143 :param dnsdomain: DNS Domain name
2144 :param private_dir: Path to private directory
2145 :param keytab_name: File name of DNS keytab file
2148 setup_file(setup_path("named.txt"), path, {
2149 "DNSDOMAIN": dnsdomain,
2151 "DNS_KEYTAB": keytab_name,
2152 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2153 "PRIVATE_DIR": private_dir
2156 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2157 """Write out a file containing zone statements suitable for inclusion in a
2158 named.conf file (including GSS-TSIG configuration).
2160 :param path: Path of the new named.conf file.
2161 :param setup_path: Setup path function.
2162 :param dnsdomain: DNS Domain name
2163 :param hostname: Local hostname
2164 :param realm: Realm name
2167 setup_file(setup_path("krb5.conf"), path, {
2168 "DNSDOMAIN": dnsdomain,
2169 "HOSTNAME": hostname,