2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
74 raise Exception("Unable to find setup directory.")
76 def get_schema_descriptor(domain_sid):
77 sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
78 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
79 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
81 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
83 "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
84 "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
85 "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
86 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
87 sec = security.descriptor.from_sddl(sddl, domain_sid)
88 return b64encode(ndr_pack(sec))
90 def get_config_descriptor(domain_sid):
91 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
97 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
98 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
99 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
101 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
103 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
104 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
105 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
106 sec = security.descriptor.from_sddl(sddl, domain_sid)
107 return b64encode(ndr_pack(sec))
110 DEFAULTSITE = "Default-First-Site-Name"
114 class ProvisioningError(Exception):
115 """A generic provision error."""
117 class InvalidNetbiosName(Exception):
118 """A specified name was not a valid NetBIOS name."""
119 def __init__(self, name):
120 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
123 class ProvisionPaths(object):
125 self.shareconf = None
136 self.dns_keytab = None
139 self.private_dir = None
141 self.slapdconf = None
142 self.modulesconf = None
143 self.memberofconf = None
144 self.fedoradsinf = None
145 self.fedoradspartitions = None
146 self.fedoradssasl = None
147 self.fedoradspam = None
148 self.fedoradsrefint = None
149 self.fedoradslinkedattributes = None
150 self.fedoradsindex = None
151 self.fedoradssamba = None
153 self.olmmrserveridsconf = None
154 self.olmmrsyncreplconf = None
157 self.olcseedldif = None
160 class ProvisionNames(object):
167 self.ldapmanagerdn = None
168 self.dnsdomain = None
170 self.netbiosname = None
177 class ProvisionResult(object):
184 class Schema(object):
185 def __init__(self, setup_path, domain_sid, schemadn=None,
186 serverdn=None, sambadn=None, ldap_backend_type=None):
187 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
189 :param samdb: Load a schema into a SamDB.
190 :param setup_path: Setup path function.
191 :param schemadn: DN of the schema
192 :param serverdn: DN of the server
194 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
198 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
199 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
200 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
201 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
202 check_all_substituted(self.schema_data)
204 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
205 {"SCHEMADN": schemadn,
206 "SERVERDN": serverdn,
209 descr = get_schema_descriptor(domain_sid)
210 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
211 {"SCHEMADN": schemadn,
215 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
216 prefixmap = b64encode(prefixmap)
220 # We don't actually add this ldif, just parse it
221 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
222 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
225 # Return a hash with the forward attribute as a key and the back as the value
226 def get_linked_attributes(schemadn,schemaldb):
227 attrs = ["linkID", "lDAPDisplayName"]
228 res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
230 for i in range (0, len(res)):
231 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
232 target = schemaldb.searchone(basedn=schemadn,
233 expression=expression,
234 attribute="lDAPDisplayName",
236 if target is not None:
237 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
241 def get_dnsyntax_attributes(schemadn,schemaldb):
242 attrs = ["linkID", "lDAPDisplayName"]
243 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
245 for i in range (0, len(res)):
246 attributes.append(str(res[i]["lDAPDisplayName"]))
251 def check_install(lp, session_info, credentials):
252 """Check whether the current install seems ok.
254 :param lp: Loadparm context
255 :param session_info: Session information
256 :param credentials: Credentials
258 if lp.get("realm") == "":
259 raise Exception("Realm empty")
260 ldb = Ldb(lp.get("sam database"), session_info=session_info,
261 credentials=credentials, lp=lp)
262 if len(ldb.search("(cn=Administrator)")) != 1:
263 raise ProvisioningError("No administrator account found")
266 def findnss(nssfn, names):
267 """Find a user or group from a list of possibilities.
269 :param nssfn: NSS Function to try (should raise KeyError if not found)
270 :param names: Names to check.
271 :return: Value return by first names list.
278 raise KeyError("Unable to find user/group %r" % names)
281 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
282 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
285 def read_and_sub_file(file, subst_vars):
286 """Read a file and sub in variables found in it
288 :param file: File to be read (typically from setup directory)
289 param subst_vars: Optional variables to subsitute in the file.
291 data = open(file, 'r').read()
292 if subst_vars is not None:
293 data = substitute_var(data, subst_vars)
294 check_all_substituted(data)
298 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
299 """Setup a ldb in the private dir.
301 :param ldb: LDB file to import data into
302 :param ldif_path: Path of the LDIF file to load
303 :param subst_vars: Optional variables to subsitute in LDIF.
304 :param nocontrols: Optional list of controls, can be None for no controls
306 assert isinstance(ldif_path, str)
307 data = read_and_sub_file(ldif_path, subst_vars)
308 ldb.add_ldif(data,controls)
311 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
312 """Modify a ldb in the private dir.
314 :param ldb: LDB object.
315 :param ldif_path: LDIF file path.
316 :param subst_vars: Optional dictionary with substitution variables.
318 data = read_and_sub_file(ldif_path, subst_vars)
320 ldb.modify_ldif(data)
323 def setup_ldb(ldb, ldif_path, subst_vars):
324 """Import a LDIF a file into a LDB handle, optionally substituting variables.
326 :note: Either all LDIF data will be added or none (using transactions).
328 :param ldb: LDB file to import into.
329 :param ldif_path: Path to the LDIF file.
330 :param subst_vars: Dictionary with substitution variables.
332 assert ldb is not None
333 ldb.transaction_start()
335 setup_add_ldif(ldb, ldif_path, subst_vars)
337 ldb.transaction_cancel()
339 ldb.transaction_commit()
342 def setup_file(template, fname, subst_vars=None):
343 """Setup a file in the private dir.
345 :param template: Path of the template file.
346 :param fname: Path of the file to create.
347 :param subst_vars: Substitution variables.
351 if os.path.exists(f):
354 data = read_and_sub_file(template, subst_vars)
355 open(f, 'w').write(data)
358 def provision_paths_from_lp(lp, dnsdomain):
359 """Set the default paths for provisioning.
361 :param lp: Loadparm context.
362 :param dnsdomain: DNS Domain name
364 paths = ProvisionPaths()
365 paths.private_dir = lp.get("private dir")
366 paths.dns_keytab = "dns.keytab"
368 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
369 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
370 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
371 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
372 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
373 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
374 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
375 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
376 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
377 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
378 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
379 paths.phpldapadminconfig = os.path.join(paths.private_dir,
380 "phpldapadmin-config.php")
381 paths.ldapdir = os.path.join(paths.private_dir,
383 paths.slapdconf = os.path.join(paths.ldapdir,
385 paths.slapdpid = os.path.join(paths.ldapdir,
387 paths.modulesconf = os.path.join(paths.ldapdir,
389 paths.memberofconf = os.path.join(paths.ldapdir,
391 paths.fedoradsinf = os.path.join(paths.ldapdir,
393 paths.fedoradspartitions = os.path.join(paths.ldapdir,
394 "fedorads-partitions.ldif")
395 paths.fedoradssasl = os.path.join(paths.ldapdir,
396 "fedorads-sasl.ldif")
397 paths.fedoradspam = os.path.join(paths.ldapdir,
399 paths.fedoradsrefint = os.path.join(paths.ldapdir,
400 "fedorads-refint.ldif")
401 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
402 "fedorads-linked-attributes.ldif")
403 paths.fedoradsindex = os.path.join(paths.ldapdir,
404 "fedorads-index.ldif")
405 paths.fedoradssamba = os.path.join(paths.ldapdir,
406 "fedorads-samba.ldif")
407 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
408 "mmr_serverids.conf")
409 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
411 paths.olcdir = os.path.join(paths.ldapdir,
413 paths.olcseedldif = os.path.join(paths.ldapdir,
415 paths.hklm = "hklm.ldb"
416 paths.hkcr = "hkcr.ldb"
417 paths.hkcu = "hkcu.ldb"
418 paths.hku = "hku.ldb"
419 paths.hkpd = "hkpd.ldb"
420 paths.hkpt = "hkpt.ldb"
422 paths.sysvol = lp.get("path", "sysvol")
424 paths.netlogon = lp.get("path", "netlogon")
426 paths.smbconf = lp.configfile
431 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
432 serverrole=None, rootdn=None, domaindn=None, configdn=None,
433 schemadn=None, serverdn=None, sitename=None, sambadn=None):
434 """Guess configuration settings to use."""
437 hostname = socket.gethostname().split(".")[0].lower()
439 netbiosname = hostname.upper()
440 if not valid_netbios_name(netbiosname):
441 raise InvalidNetbiosName(netbiosname)
443 hostname = hostname.lower()
445 if dnsdomain is None:
446 dnsdomain = lp.get("realm").lower()
448 if serverrole is None:
449 serverrole = lp.get("server role")
451 assert dnsdomain is not None
452 realm = dnsdomain.upper()
454 if lp.get("realm").upper() != realm:
455 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
456 (lp.get("realm"), lp.configfile, realm))
458 if serverrole == "domain controller":
460 domain = lp.get("workgroup")
462 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
463 if lp.get("workgroup").upper() != domain.upper():
464 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
465 lp.get("workgroup"), domain)
469 domaindn = "DC=" + netbiosname
471 assert domain is not None
472 domain = domain.upper()
474 if not valid_netbios_name(domain):
475 raise InvalidNetbiosName(domain)
477 if netbiosname.upper() == realm:
478 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
480 if hostname.upper() == realm:
481 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
483 if domain.upper() == realm:
484 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
490 configdn = "CN=Configuration," + rootdn
492 schemadn = "CN=Schema," + configdn
499 names = ProvisionNames()
500 names.rootdn = rootdn
501 names.domaindn = domaindn
502 names.configdn = configdn
503 names.schemadn = schemadn
504 names.sambadn = sambadn
505 names.ldapmanagerdn = "CN=Manager," + rootdn
506 names.dnsdomain = dnsdomain
507 names.domain = domain
509 names.netbiosname = netbiosname
510 names.hostname = hostname
511 names.sitename = sitename
512 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
517 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
519 """Create a new smb.conf file based on a couple of basic settings.
521 assert smbconf is not None
523 hostname = socket.gethostname().split(".")[0].lower()
525 if serverrole is None:
526 serverrole = "standalone"
528 assert serverrole in ("domain controller", "member server", "standalone")
529 if serverrole == "domain controller":
531 elif serverrole == "member server":
532 smbconfsuffix = "member"
533 elif serverrole == "standalone":
534 smbconfsuffix = "standalone"
536 assert domain is not None
537 assert realm is not None
539 default_lp = param.LoadParm()
540 #Load non-existant file
541 if os.path.exists(smbconf):
542 default_lp.load(smbconf)
544 if targetdir is not None:
545 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
546 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
548 default_lp.set("lock dir", os.path.abspath(targetdir))
553 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
554 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
556 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
558 "HOSTNAME": hostname,
561 "SERVERROLE": serverrole,
562 "NETLOGONPATH": netlogon,
563 "SYSVOLPATH": sysvol,
564 "PRIVATEDIR_LINE": privatedir_line,
565 "LOCKDIR_LINE": lockdir_line
569 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
570 users_gid, wheel_gid):
571 """setup reasonable name mappings for sam names to unix names.
573 :param samdb: SamDB object.
574 :param idmap: IDmap db object.
575 :param sid: The domain sid.
576 :param domaindn: The domain DN.
577 :param root_uid: uid of the UNIX root user.
578 :param nobody_uid: uid of the UNIX nobody user.
579 :param users_gid: gid of the UNIX users group.
580 :param wheel_gid: gid of the UNIX wheel group."""
582 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
583 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
585 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
586 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
588 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
590 serverrole, ldap_backend=None,
592 """Setup the partitions for the SAM database.
594 Alternatively, provision() may call this, and then populate the database.
596 :note: This will wipe the Sam Database!
598 :note: This function always removes the local SAM LDB file. The erase
599 parameter controls whether to erase the existing data, which
600 may not be stored locally but in LDAP.
602 assert session_info is not None
604 # We use options=["modules:"] to stop the modules loading - we
605 # just want to wipe and re-initialise the database, not start it up
608 samdb = Ldb(url=samdb_path, session_info=session_info,
609 credentials=credentials, lp=lp, options=["modules:"])
611 samdb.erase_except_schema_controlled()
613 os.unlink(samdb_path)
614 samdb = Ldb(url=samdb_path, session_info=session_info,
615 credentials=credentials, lp=lp, options=["modules:"])
617 samdb.erase_except_schema_controlled()
620 #Add modules to the list to activate them by default
621 #beware often order is important
623 # Some Known ordering constraints:
624 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
625 # - objectclass must be before password_hash, because password_hash checks
626 # that the objectclass is of type person (filled in by objectclass
627 # module when expanding the objectclass list)
628 # - partition must be last
629 # - each partition has its own module list then
630 modules_list = ["resolve_oids",
653 "extended_dn_out_ldb"]
654 modules_list2 = ["show_deleted",
657 ldap_backend_line = "# No LDAP backend"
658 if ldap_backend is not None:
659 ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri
661 if ldap_backend.ldap_backend_type == "fedora-ds":
662 backend_modules = ["nsuniqueid", "paged_searches"]
663 # We can handle linked attributes here, as we don't have directory-side subtree operations
664 tdb_modules_list = ["extended_dn_out_dereference"]
665 elif ldap_backend.ldap_backend_type == "openldap":
666 backend_modules = ["entryuuid", "paged_searches"]
667 # OpenLDAP handles subtree renames, so we don't want to do any of these things
668 tdb_modules_list = ["extended_dn_out_dereference"]
670 elif serverrole == "domain controller":
671 tdb_modules_list.insert(0, "repl_meta_data")
674 backend_modules = ["objectguid"]
676 if tdb_modules_list is None:
677 tdb_modules_list_as_string = ""
679 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
681 samdb.transaction_start()
683 message("Setting up sam.ldb partitions and settings")
684 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
685 "SCHEMADN": ldb.Dn(samdb, names.schemadn).get_casefold(),
686 "SCHEMADN_MOD2": ",objectguid",
687 "CONFIGDN": ldb.Dn(samdb, names.configdn).get_casefold(),
688 "DOMAINDN": ldb.Dn(samdb, names.domaindn).get_casefold(),
689 "SCHEMADN_MOD": "schema_fsmo",
690 "CONFIGDN_MOD": "naming_fsmo",
691 "DOMAINDN_MOD": "pdc_fsmo",
692 "MODULES_LIST": ",".join(modules_list),
693 "TDB_MODULES_LIST": tdb_modules_list_as_string,
694 "MODULES_LIST2": ",".join(modules_list2),
695 "BACKEND_MOD": ",".join(backend_modules),
696 "LDAP_BACKEND_LINE": ldap_backend_line,
699 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
701 message("Setting up sam.ldb rootDSE")
702 setup_samdb_rootdse(samdb, setup_path, names)
705 samdb.transaction_cancel()
708 samdb.transaction_commit()
710 def secretsdb_self_join(secretsdb, domain,
711 netbiosname, domainsid, machinepass,
712 realm=None, dnsdomain=None,
714 key_version_number=1,
715 secure_channel_type=SEC_CHAN_WKSTA):
716 """Add domain join-specific bits to a secrets database.
718 :param secretsdb: Ldb Handle to the secrets database
719 :param machinepass: Machine password
721 attrs=["whenChanged",
729 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
730 msg["secureChannelType"] = str(secure_channel_type)
731 msg["flatname"] = [domain]
732 msg["objectClass"] = ["top", "primaryDomain"]
733 if realm is not None:
734 if dnsdomain is None:
735 dnsdomain = realm.lower()
736 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
738 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
739 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
740 msg["privateKeytab"] = ["secrets.keytab"];
743 msg["secret"] = [machinepass]
744 msg["samAccountName"] = ["%s$" % netbiosname]
745 msg["secureChannelType"] = [str(secure_channel_type)]
746 msg["objectSid"] = [ndr_pack(domainsid)]
748 res = secretsdb.search(base="cn=Primary Domains",
750 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
751 scope=SCOPE_ONELEVEL)
754 if del_msg.dn is not msg.dn:
755 secretsdb.delete(del_msg.dn)
757 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
760 msg["priorSecret"] = res[0]["secret"]
761 msg["priorWhenChanged"] = res[0]["whenChanged"]
763 if res["privateKeytab"] is not None:
764 msg["privateKeytab"] = res[0]["privateKeytab"]
766 if res["krb5Keytab"] is not None:
767 msg["krb5Keytab"] = res[0]["krb5Keytab"]
770 el.set_flags(ldb.FLAG_MOD_REPLACE)
771 secretsdb.modify(msg)
776 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
777 dns_keytab_path, dnspass):
778 """Add DNS specific bits to a secrets database.
780 :param secretsdb: Ldb Handle to the secrets database
781 :param setup_path: Setup path function
782 :param machinepass: Machine password
784 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
786 "DNSDOMAIN": dnsdomain,
787 "DNS_KEYTAB": dns_keytab_path,
788 "DNSPASS_B64": b64encode(dnspass),
792 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
793 """Setup the secrets database.
795 :param path: Path to the secrets database.
796 :param setup_path: Get the path to a setup file.
797 :param session_info: Session info.
798 :param credentials: Credentials
799 :param lp: Loadparm context
800 :return: LDB handle for the created secrets database
802 if os.path.exists(path):
804 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
807 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
808 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
810 secrets_ldb.transaction_start()
811 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
813 if credentials is not None and credentials.authentication_requested():
814 if credentials.get_bind_dn() is not None:
815 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
816 "LDAPMANAGERDN": credentials.get_bind_dn(),
817 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
820 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
821 "LDAPADMINUSER": credentials.get_username(),
822 "LDAPADMINREALM": credentials.get_realm(),
823 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
828 def setup_privileges(path, setup_path, session_info, lp):
829 """Setup the privileges database.
831 :param path: Path to the privileges database.
832 :param setup_path: Get the path to a setup file.
833 :param session_info: Session info.
834 :param credentials: Credentials
835 :param lp: Loadparm context
836 :return: LDB handle for the created secrets database
838 if os.path.exists(path):
840 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
841 privilege_ldb.erase()
842 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
845 def setup_registry(path, setup_path, session_info, lp):
846 """Setup the registry.
848 :param path: Path to the registry database
849 :param setup_path: Function that returns the path to a setup.
850 :param session_info: Session information
851 :param credentials: Credentials
852 :param lp: Loadparm context
854 reg = registry.Registry()
855 hive = registry.open_ldb(path, session_info=session_info,
857 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
858 provision_reg = setup_path("provision.reg")
859 assert os.path.exists(provision_reg)
860 reg.diff_apply(provision_reg)
863 def setup_idmapdb(path, setup_path, session_info, lp):
864 """Setup the idmap database.
866 :param path: path to the idmap database
867 :param setup_path: Function that returns a path to a setup file
868 :param session_info: Session information
869 :param credentials: Credentials
870 :param lp: Loadparm context
872 if os.path.exists(path):
875 idmap_ldb = IDmapDB(path, session_info=session_info,
879 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
883 def setup_samdb_rootdse(samdb, setup_path, names):
884 """Setup the SamDB rootdse.
886 :param samdb: Sam Database handle
887 :param setup_path: Obtain setup path
889 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
890 "SCHEMADN": names.schemadn,
891 "NETBIOSNAME": names.netbiosname,
892 "DNSDOMAIN": names.dnsdomain,
893 "REALM": names.realm,
894 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
895 "DOMAINDN": names.domaindn,
896 "ROOTDN": names.rootdn,
897 "CONFIGDN": names.configdn,
898 "SERVERDN": names.serverdn,
902 def setup_self_join(samdb, names,
903 machinepass, dnspass,
904 domainsid, invocationid, setup_path,
905 policyguid, policyguid_dc, domainControllerFunctionality,
907 """Join a host to its own domain."""
908 assert isinstance(invocationid, str)
909 if ntdsguid is not None:
910 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
913 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
914 "CONFIGDN": names.configdn,
915 "SCHEMADN": names.schemadn,
916 "DOMAINDN": names.domaindn,
917 "SERVERDN": names.serverdn,
918 "INVOCATIONID": invocationid,
919 "NETBIOSNAME": names.netbiosname,
920 "DEFAULTSITE": names.sitename,
921 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
922 "MACHINEPASS_B64": b64encode(machinepass),
923 "DNSPASS_B64": b64encode(dnspass),
924 "REALM": names.realm,
925 "DOMAIN": names.domain,
926 "DNSDOMAIN": names.dnsdomain,
927 "SAMBA_VERSION_STRING": version,
928 "NTDSGUID": ntdsguid_line,
929 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
931 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
932 "POLICYGUID": policyguid,
933 "POLICYGUID_DC": policyguid_dc,
934 "DNSDOMAIN": names.dnsdomain,
935 "DOMAINSID": str(domainsid),
936 "DOMAINDN": names.domaindn})
938 # add the NTDSGUID based SPNs
939 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
940 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
941 expression="", scope=SCOPE_BASE)
942 assert isinstance(names.ntdsguid, str)
944 # Setup fSMORoleOwner entries to point at the newly created DC entry
945 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
946 "DOMAIN": names.domain,
947 "DNSDOMAIN": names.dnsdomain,
948 "DOMAINDN": names.domaindn,
949 "CONFIGDN": names.configdn,
950 "SCHEMADN": names.schemadn,
951 "DEFAULTSITE": names.sitename,
952 "SERVERDN": names.serverdn,
953 "NETBIOSNAME": names.netbiosname,
954 "NTDSGUID": names.ntdsguid
958 def setup_samdb(path, setup_path, session_info, credentials, lp,
960 domainsid, domainguid, policyguid, policyguid_dc,
961 fill, adminpass, krbtgtpass,
962 machinepass, invocationid, dnspass, ntdsguid,
963 serverrole, dom_for_fun_level=None,
964 schema=None, ldap_backend=None):
965 """Setup a complete SAM Database.
967 :note: This will wipe the main SAM database file!
970 # ATTENTION: Do NOT change these default values without discussion with the
971 # team and/or release manager. They have a big impact on the whole program!
972 domainControllerFunctionality = DS_DC_FUNCTION_2008
974 if dom_for_fun_level is None:
975 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
976 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
977 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
979 if dom_for_fun_level > domainControllerFunctionality:
980 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!")
982 domainFunctionality = dom_for_fun_level
983 forestFunctionality = dom_for_fun_level
985 # Also wipes the database
986 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
987 credentials=credentials, session_info=session_info,
988 names=names, ldap_backend=ldap_backend,
989 serverrole=serverrole)
992 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
993 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
995 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
996 samdb = Ldb(session_info=session_info,
997 credentials=credentials, lp=lp)
999 message("Pre-loading the Samba 4 and AD schema")
1001 # Load the schema from the one we computed earlier
1002 samdb.set_schema_from_ldb(schema.ldb)
1004 # And now we can connect to the DB - the schema won't be loaded from the DB
1008 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
1010 if fill == FILL_DRS:
1013 samdb.transaction_start()
1015 message("Erasing data from partitions")
1016 # Load the schema (again). This time it will force a reindex,
1017 # and will therefore make the erase_partitions() below
1018 # computationally sane
1019 samdb.set_schema_from_ldb(schema.ldb)
1020 samdb.erase_partitions()
1022 # Set the domain functionality levels onto the database.
1023 # Various module (the password_hash module in particular) need
1024 # to know what level of AD we are emulating.
1026 # These will be fixed into the database via the database
1027 # modifictions below, but we need them set from the start.
1028 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1029 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1030 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1032 samdb.set_domain_sid(str(domainsid))
1033 if serverrole == "domain controller":
1034 samdb.set_invocation_id(invocationid)
1036 message("Adding DomainDN: %s" % names.domaindn)
1038 #impersonate domain admin
1039 admin_session_info = admin_session(lp, str(domainsid))
1040 samdb.set_session_info(admin_session_info)
1041 if domainguid is not None:
1042 domainguid_line = "objectGUID: %s\n-" % domainguid
1044 domainguid_line = ""
1045 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1046 "DOMAINDN": names.domaindn,
1047 "DOMAINGUID": domainguid_line
1051 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1052 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1053 "DOMAINSID": str(domainsid),
1054 "SCHEMADN": names.schemadn,
1055 "NETBIOSNAME": names.netbiosname,
1056 "DEFAULTSITE": names.sitename,
1057 "CONFIGDN": names.configdn,
1058 "SERVERDN": names.serverdn,
1059 "POLICYGUID": policyguid,
1060 "DOMAINDN": names.domaindn,
1061 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1062 "SAMBA_VERSION_STRING": version
1065 message("Adding configuration container")
1066 descr = get_config_descriptor(domainsid);
1067 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1068 "CONFIGDN": names.configdn,
1069 "DESCRIPTOR": descr,
1071 message("Modifying configuration container")
1072 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1073 "CONFIGDN": names.configdn,
1074 "SCHEMADN": names.schemadn,
1077 # The LDIF here was created when the Schema object was constructed
1078 message("Setting up sam.ldb schema")
1079 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1080 samdb.modify_ldif(schema.schema_dn_modify)
1081 samdb.write_prefixes_from_schema()
1082 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1083 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1084 {"SCHEMADN": names.schemadn})
1086 message("Setting up sam.ldb configuration data")
1087 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1088 "CONFIGDN": names.configdn,
1089 "NETBIOSNAME": names.netbiosname,
1090 "DEFAULTSITE": names.sitename,
1091 "DNSDOMAIN": names.dnsdomain,
1092 "DOMAIN": names.domain,
1093 "SCHEMADN": names.schemadn,
1094 "DOMAINDN": names.domaindn,
1095 "SERVERDN": names.serverdn,
1096 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1099 message("Setting up display specifiers")
1100 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1101 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1102 check_all_substituted(display_specifiers_ldif)
1103 samdb.add_ldif(display_specifiers_ldif)
1105 message("Adding users container")
1106 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1107 "DOMAINDN": names.domaindn})
1108 message("Modifying users container")
1109 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1110 "DOMAINDN": names.domaindn})
1111 message("Adding computers container")
1112 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1113 "DOMAINDN": names.domaindn})
1114 message("Modifying computers container")
1115 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1116 "DOMAINDN": names.domaindn})
1117 message("Setting up sam.ldb data")
1118 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1119 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1120 "DOMAINDN": names.domaindn,
1121 "NETBIOSNAME": names.netbiosname,
1122 "DEFAULTSITE": names.sitename,
1123 "CONFIGDN": names.configdn,
1124 "SERVERDN": names.serverdn,
1125 "POLICYGUID_DC": policyguid_dc
1128 if fill == FILL_FULL:
1129 message("Setting up sam.ldb users and groups")
1130 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1131 "DOMAINDN": names.domaindn,
1132 "DOMAINSID": str(domainsid),
1133 "CONFIGDN": names.configdn,
1134 "ADMINPASS_B64": b64encode(adminpass),
1135 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1138 if serverrole == "domain controller":
1139 message("Setting up self join")
1140 setup_self_join(samdb, names=names, invocationid=invocationid,
1142 machinepass=machinepass,
1143 domainsid=domainsid, policyguid=policyguid,
1144 policyguid_dc=policyguid_dc,
1145 setup_path=setup_path,
1146 domainControllerFunctionality=domainControllerFunctionality,
1149 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1150 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1151 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1152 assert isinstance(names.ntdsguid, str)
1155 samdb.transaction_cancel()
1158 samdb.transaction_commit()
1163 FILL_NT4SYNC = "NT4SYNC"
1167 def provision(setup_dir, message, session_info,
1168 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1170 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1172 domain=None, hostname=None, hostip=None, hostip6=None,
1173 domainsid=None, adminpass=None, ldapadminpass=None,
1174 krbtgtpass=None, domainguid=None,
1175 policyguid=None, policyguid_dc=None, invocationid=None,
1176 machinepass=None, ntdsguid=None,
1177 dnspass=None, root=None, nobody=None, users=None,
1178 wheel=None, backup=None, aci=None, serverrole=None,
1179 dom_for_fun_level=None,
1180 ldap_backend_extra_port=None, ldap_backend_type=None,
1182 ol_mmr_urls=None, ol_olc=None,
1183 setup_ds_path=None, slapd_path=None, nosync=False,
1184 ldap_dryrun_mode=False):
1187 :note: caution, this wipes all existing data!
1190 def setup_path(file):
1191 return os.path.join(setup_dir, file)
1193 if domainsid is None:
1194 domainsid = security.random_sid()
1196 domainsid = security.dom_sid(domainsid)
1198 # create/adapt the group policy GUIDs
1199 if policyguid is None:
1200 policyguid = str(uuid.uuid4())
1201 policyguid = policyguid.upper()
1202 if policyguid_dc is None:
1203 policyguid_dc = str(uuid.uuid4())
1204 policyguid_dc = policyguid_dc.upper()
1206 if adminpass is None:
1207 adminpass = glue.generate_random_str(12)
1208 if krbtgtpass is None:
1209 krbtgtpass = glue.generate_random_str(12)
1210 if machinepass is None:
1211 machinepass = glue.generate_random_str(12)
1213 dnspass = glue.generate_random_str(12)
1214 if ldapadminpass is None:
1215 #Make a new, random password between Samba and it's LDAP server
1216 ldapadminpass=glue.generate_random_str(12)
1219 root_uid = findnss_uid([root or "root"])
1220 nobody_uid = findnss_uid([nobody or "nobody"])
1221 users_gid = findnss_gid([users or "users"])
1223 wheel_gid = findnss_gid(["wheel", "adm"])
1225 wheel_gid = findnss_gid([wheel])
1227 if targetdir is not None:
1228 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1229 os.makedirs(os.path.join(targetdir, "etc"))
1230 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1231 elif smbconf is None:
1232 smbconf = param.default_path()
1234 # only install a new smb.conf if there isn't one there already
1235 if not os.path.exists(smbconf):
1236 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1239 lp = param.LoadParm()
1242 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1243 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1244 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1247 paths = provision_paths_from_lp(lp, names.dnsdomain)
1251 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1252 except socket.gaierror, (socket.EAI_NODATA, msg):
1257 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1258 except socket.gaierror, (socket.EAI_NODATA, msg):
1261 if serverrole is None:
1262 serverrole = lp.get("server role")
1264 assert serverrole in ("domain controller", "member server", "standalone")
1265 if invocationid is None and serverrole == "domain controller":
1266 invocationid = str(uuid.uuid4())
1268 if not os.path.exists(paths.private_dir):
1269 os.mkdir(paths.private_dir)
1271 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1273 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1274 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1276 secrets_credentials = credentials
1277 provision_backend = None
1278 if ldap_backend_type:
1279 # We only support an LDAP backend over ldapi://
1281 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1282 lp=lp, credentials=credentials,
1284 message=message, hostname=hostname,
1285 root=root, schema=schema,
1286 ldap_backend_type=ldap_backend_type,
1287 ldapadminpass=ldapadminpass,
1288 ldap_backend_extra_port=ldap_backend_extra_port,
1289 ol_mmr_urls=ol_mmr_urls,
1290 slapd_path=slapd_path,
1291 setup_ds_path=setup_ds_path,
1292 ldap_dryrun_mode=ldap_dryrun_mode)
1294 # Now use the backend credentials to access the databases
1295 credentials = provision_backend.credentials
1296 secrets_credentials = provision_backend.adminCredentials
1297 ldapi_url = provision_backend.ldapi_uri
1299 # only install a new shares config db if there is none
1300 if not os.path.exists(paths.shareconf):
1301 message("Setting up share.ldb")
1302 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1303 credentials=credentials, lp=lp)
1304 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1307 message("Setting up secrets.ldb")
1308 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1309 session_info=session_info,
1310 credentials=secrets_credentials, lp=lp)
1312 message("Setting up the registry")
1313 setup_registry(paths.hklm, setup_path, session_info,
1316 message("Setting up the privileges database")
1317 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1319 message("Setting up idmap db")
1320 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1323 message("Setting up SAM db")
1324 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1325 credentials=credentials, lp=lp, names=names,
1327 domainsid=domainsid,
1328 schema=schema, domainguid=domainguid,
1329 policyguid=policyguid, policyguid_dc=policyguid_dc,
1331 adminpass=adminpass, krbtgtpass=krbtgtpass,
1332 invocationid=invocationid,
1333 machinepass=machinepass, dnspass=dnspass,
1334 ntdsguid=ntdsguid, serverrole=serverrole,
1335 dom_for_fun_level=dom_for_fun_level,
1336 ldap_backend=provision_backend)
1338 if serverrole == "domain controller":
1339 if paths.netlogon is None:
1340 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1341 message("Please either remove %s or see the template at %s" %
1342 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1343 assert(paths.netlogon is not None)
1345 if paths.sysvol is None:
1346 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1347 message("Please either remove %s or see the template at %s" %
1348 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1349 assert(paths.sysvol is not None)
1351 # Set up group policies (domain policy and domain controller policy)
1353 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1354 "{" + policyguid + "}")
1355 os.makedirs(policy_path, 0755)
1356 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1357 "[General]\r\nVersion=65543")
1358 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1359 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1361 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1362 "{" + policyguid_dc + "}")
1363 os.makedirs(policy_path_dc, 0755)
1364 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1365 "[General]\r\nVersion=2")
1366 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1367 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1369 if not os.path.isdir(paths.netlogon):
1370 os.makedirs(paths.netlogon, 0755)
1372 if samdb_fill == FILL_FULL:
1373 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1374 root_uid=root_uid, nobody_uid=nobody_uid,
1375 users_gid=users_gid, wheel_gid=wheel_gid)
1377 message("Setting up sam.ldb rootDSE marking as synchronized")
1378 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1380 # Only make a zone file on the first DC, it should be replicated with DNS replication
1381 if serverrole == "domain controller":
1382 secretsdb_self_join(secrets_ldb, domain=domain,
1384 dnsdomain=names.dnsdomain,
1385 netbiosname=names.netbiosname,
1386 domainsid=domainsid,
1387 machinepass=machinepass,
1388 secure_channel_type=SEC_CHAN_BDC)
1390 secretsdb_setup_dns(secrets_ldb, setup_path,
1391 realm=names.realm, dnsdomain=names.dnsdomain,
1392 dns_keytab_path=paths.dns_keytab,
1395 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1396 assert isinstance(domainguid, str)
1398 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1400 hostip6=hostip6, hostname=names.hostname,
1402 domainguid=domainguid, ntdsguid=names.ntdsguid)
1404 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1405 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1407 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1408 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1409 keytab_name=paths.dns_keytab)
1410 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1411 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1413 create_krb5_conf(paths.krb5conf, setup_path,
1414 dnsdomain=names.dnsdomain, hostname=names.hostname,
1416 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1418 #Now commit the secrets.ldb to disk
1419 secrets_ldb.transaction_commit()
1421 if provision_backend is not None:
1422 if ldap_backend_type == "fedora-ds":
1423 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1425 # delete default SASL mappings
1426 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1428 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1429 for i in range (0, len(res)):
1430 dn = str(res[i]["dn"])
1433 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1436 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1438 m.dn = ldb.Dn(1, names.domaindn)
1441 m.dn = ldb.Dn(1, names.configdn)
1444 m.dn = ldb.Dn(1, names.schemadn)
1447 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1448 if provision_backend.slapd.poll() is None:
1450 if hasattr(provision_backend.slapd, "terminate"):
1451 provision_backend.slapd.terminate()
1453 # Older python versions don't have .terminate()
1455 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1457 #and now wait for it to die
1458 provision_backend.slapd.communicate()
1460 # now display slapd_command_file.txt to show how slapd must be started next time
1461 message("Use later the following commandline to start slapd, then Samba:")
1462 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1463 message(slapd_command)
1464 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1466 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1467 "SLAPD_COMMAND" : slapd_command})
1470 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1473 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1475 message("Once the above files are installed, your Samba4 server will be ready to use")
1476 message("Server Role: %s" % serverrole)
1477 message("Hostname: %s" % names.hostname)
1478 message("NetBIOS Domain: %s" % names.domain)
1479 message("DNS Domain: %s" % names.dnsdomain)
1480 message("DOMAIN SID: %s" % str(domainsid))
1481 if samdb_fill == FILL_FULL:
1482 message("Admin password: %s" % adminpass)
1483 if provision_backend:
1484 if provision_backend.credentials.get_bind_dn() is not None:
1485 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1487 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1489 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1491 result = ProvisionResult()
1492 result.domaindn = domaindn
1493 result.paths = paths
1495 result.samdb = samdb
1500 def provision_become_dc(setup_dir=None,
1501 smbconf=None, targetdir=None, realm=None,
1502 rootdn=None, domaindn=None, schemadn=None,
1503 configdn=None, serverdn=None,
1504 domain=None, hostname=None, domainsid=None,
1505 adminpass=None, krbtgtpass=None, domainguid=None,
1506 policyguid=None, policyguid_dc=None, invocationid=None,
1508 dnspass=None, root=None, nobody=None, users=None,
1509 wheel=None, backup=None, serverrole=None,
1510 ldap_backend=None, ldap_backend_type=None,
1511 sitename=None, debuglevel=1):
1514 """print a message if quiet is not set."""
1517 glue.set_debug_level(debuglevel)
1519 return provision(setup_dir, message, system_session(), None,
1520 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1521 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1522 configdn=configdn, serverdn=serverdn, domain=domain,
1523 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1524 machinepass=machinepass, serverrole="domain controller",
1528 def setup_db_config(setup_path, dbdir):
1529 """Setup a Berkeley database.
1531 :param setup_path: Setup path function.
1532 :param dbdir: Database directory."""
1533 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1534 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1535 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1536 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1538 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1539 {"LDAPDBDIR": dbdir})
1541 class ProvisionBackend(object):
1542 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1543 names=None, message=None,
1544 hostname=None, root=None,
1545 schema=None, ldapadminpass=None,
1546 ldap_backend_type=None, ldap_backend_extra_port=None,
1548 setup_ds_path=None, slapd_path=None,
1549 nosync=False, ldap_dryrun_mode=False):
1550 """Provision an LDAP backend for samba4
1552 This works for OpenLDAP and Fedora DS
1555 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1557 if not os.path.isdir(paths.ldapdir):
1558 os.makedirs(paths.ldapdir, 0700)
1560 if ldap_backend_type == "existing":
1561 #Check to see that this 'existing' LDAP backend in fact exists
1562 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1563 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1564 expression="(objectClass=OpenLDAProotDSE)")
1566 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1567 # This caused them to be set into the long-term database later in the script.
1568 self.credentials = credentials
1569 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1572 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1573 # if another instance of slapd is already running
1575 ldapi_db = Ldb(self.ldapi_uri)
1576 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1577 expression="(objectClass=OpenLDAProotDSE)");
1579 f = open(paths.slapdpid, "r")
1582 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1586 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1591 # Try to print helpful messages when the user has not specified the path to slapd
1592 if slapd_path is None:
1593 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1594 if not os.path.exists(slapd_path):
1595 message (slapd_path)
1596 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1598 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1600 os.unlink(schemadb_path)
1605 # Put the LDIF of the schema into a database so we can search on
1606 # it to generate schema-dependent configurations in Fedora DS and
1608 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1609 schema.ldb.connect(schemadb_path)
1610 schema.ldb.transaction_start()
1612 # These bits of LDIF are supplied when the Schema object is created
1613 schema.ldb.add_ldif(schema.schema_dn_add)
1614 schema.ldb.modify_ldif(schema.schema_dn_modify)
1615 schema.ldb.add_ldif(schema.schema_data)
1616 schema.ldb.transaction_commit()
1618 self.credentials = Credentials()
1619 self.credentials.guess(lp)
1620 #Kerberos to an ldapi:// backend makes no sense
1621 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1623 self.adminCredentials = Credentials()
1624 self.adminCredentials.guess(lp)
1625 #Kerberos to an ldapi:// backend makes no sense
1626 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1628 self.ldap_backend_type = ldap_backend_type
1630 if ldap_backend_type == "fedora-ds":
1631 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1632 names=names, message=message,
1634 ldapadminpass=ldapadminpass, root=root,
1636 ldap_backend_extra_port=ldap_backend_extra_port,
1637 setup_ds_path=setup_ds_path,
1638 slapd_path=slapd_path,
1640 ldap_dryrun_mode=ldap_dryrun_mode)
1642 elif ldap_backend_type == "openldap":
1643 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1644 names=names, message=message,
1646 ldapadminpass=ldapadminpass, root=root,
1648 ldap_backend_extra_port=ldap_backend_extra_port,
1649 ol_mmr_urls=ol_mmr_urls,
1650 slapd_path=slapd_path,
1652 ldap_dryrun_mode=ldap_dryrun_mode)
1654 raise ProvisioningError("Unknown LDAP backend type selected")
1656 self.credentials.set_password(ldapadminpass)
1657 self.adminCredentials.set_username("samba-admin")
1658 self.adminCredentials.set_password(ldapadminpass)
1660 # Now start the slapd, so we can provision onto it. We keep the
1661 # subprocess context around, to kill this off at the successful
1663 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1665 while self.slapd.poll() is None:
1666 # Wait until the socket appears
1668 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1669 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1670 expression="(objectClass=OpenLDAProotDSE)")
1671 # If we have got here, then we must have a valid connection to the LDAP server!
1677 raise ProvisioningError("slapd died before we could make a connection to it")
1680 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1682 hostname=None, ldapadminpass=None, root=None,
1684 ldap_backend_extra_port=None,
1686 slapd_path=None, nosync=False,
1687 ldap_dryrun_mode=False):
1689 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1692 nosync_config = "dbnosync"
1694 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1695 refint_attributes = ""
1696 memberof_config = "# Generated from Samba4 schema\n"
1697 for att in lnkattr.keys():
1698 if lnkattr[att] is not None:
1699 refint_attributes = refint_attributes + " " + att
1701 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1702 { "MEMBER_ATTR" : att ,
1703 "MEMBEROF_ATTR" : lnkattr[att] })
1705 refint_config = read_and_sub_file(setup_path("refint.conf"),
1706 { "LINK_ATTRS" : refint_attributes})
1708 attrs = ["linkID", "lDAPDisplayName"]
1709 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1711 for i in range (0, len(res)):
1712 index_attr = res[i]["lDAPDisplayName"][0]
1713 if index_attr == "objectGUID":
1714 index_attr = "entryUUID"
1716 index_config += "index " + index_attr + " eq\n"
1718 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1720 mmr_replicator_acl = ""
1721 mmr_serverids_config = ""
1722 mmr_syncrepl_schema_config = ""
1723 mmr_syncrepl_config_config = ""
1724 mmr_syncrepl_user_config = ""
1727 if ol_mmr_urls is not None:
1728 # For now, make these equal
1729 mmr_pass = ldapadminpass
1731 url_list=filter(None,ol_mmr_urls.split(' '))
1732 if (len(url_list) == 1):
1733 url_list=filter(None,ol_mmr_urls.split(','))
1736 mmr_on_config = "MirrorMode On"
1737 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1739 for url in url_list:
1741 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1742 { "SERVERID" : str(serverid),
1743 "LDAPSERVER" : url })
1746 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1748 "MMRDN": names.schemadn,
1750 "MMR_PASSWORD": mmr_pass})
1753 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1755 "MMRDN": names.configdn,
1757 "MMR_PASSWORD": mmr_pass})
1760 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1762 "MMRDN": names.domaindn,
1764 "MMR_PASSWORD": mmr_pass })
1765 # OpenLDAP cn=config initialisation
1766 olc_syncrepl_config = ""
1768 # if mmr = yes, generate cn=config-replication directives
1769 # and olc_seed.lif for the other mmr-servers
1770 if ol_mmr_urls is not None:
1772 olc_serverids_config = ""
1773 olc_syncrepl_seed_config = ""
1774 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1776 for url in url_list:
1778 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1779 { "SERVERID" : str(serverid),
1780 "LDAPSERVER" : url })
1783 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1786 "MMR_PASSWORD": mmr_pass})
1788 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1790 "LDAPSERVER" : url})
1792 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1793 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1794 "OLC_PW": ldapadminpass,
1795 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1798 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1799 {"DNSDOMAIN": names.dnsdomain,
1800 "LDAPDIR": paths.ldapdir,
1801 "DOMAINDN": names.domaindn,
1802 "CONFIGDN": names.configdn,
1803 "SCHEMADN": names.schemadn,
1804 "MEMBEROF_CONFIG": memberof_config,
1805 "MIRRORMODE": mmr_on_config,
1806 "REPLICATOR_ACL": mmr_replicator_acl,
1807 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1808 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1809 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1810 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1811 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1812 "OLC_MMR_CONFIG": olc_mmr_config,
1813 "REFINT_CONFIG": refint_config,
1814 "INDEX_CONFIG": index_config,
1815 "NOSYNC": nosync_config})
1817 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1818 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1819 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1821 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1822 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1824 setup_file(setup_path("cn=samba.ldif"),
1825 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1826 { "UUID": str(uuid.uuid4()),
1827 "LDAPTIME": timestring(int(time.time()))} )
1828 setup_file(setup_path("cn=samba-admin.ldif"),
1829 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1830 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1831 "UUID": str(uuid.uuid4()),
1832 "LDAPTIME": timestring(int(time.time()))} )
1834 if ol_mmr_urls is not None:
1835 setup_file(setup_path("cn=replicator.ldif"),
1836 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1837 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1838 "UUID": str(uuid.uuid4()),
1839 "LDAPTIME": timestring(int(time.time()))} )
1842 mapping = "schema-map-openldap-2.3"
1843 backend_schema = "backend-schema.schema"
1845 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1846 assert backend_schema_data is not None
1847 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1849 # now we generate the needed strings to start slapd automatically,
1850 # first ldapi_uri...
1851 if ldap_backend_extra_port is not None:
1852 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1853 # specified there as part of it's clue as to it's own name,
1854 # and not to replicate to itself
1855 if ol_mmr_urls is None:
1856 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1858 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1860 server_port_string = ""
1862 # Prepare the 'result' information - the commands to return in particular
1863 result.slapd_provision_command = [slapd_path]
1865 result.slapd_provision_command.append("-F" + paths.olcdir)
1867 result.slapd_provision_command.append("-h")
1869 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1870 result.slapd_command = list(result.slapd_provision_command)
1872 result.slapd_provision_command.append(result.ldapi_uri)
1873 result.slapd_provision_command.append("-d0")
1875 uris = result.ldapi_uri
1876 if server_port_string is not "":
1877 uris = uris + " " + server_port_string
1879 result.slapd_command.append(uris)
1881 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1882 result.credentials.set_username("samba-admin")
1884 # If we were just looking for crashes up to this point, it's a
1885 # good time to exit before we realise we don't have OpenLDAP on
1887 if ldap_dryrun_mode:
1890 # Finally, convert the configuration into cn=config style!
1891 if not os.path.isdir(paths.olcdir):
1892 os.makedirs(paths.olcdir, 0770)
1894 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1896 # We can't do this, as OpenLDAP is strange. It gives an error
1897 # output to the above, but does the conversion sucessfully...
1900 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1902 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1903 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1905 # Don't confuse the admin by leaving the slapd.conf around
1906 os.remove(paths.slapdconf)
1909 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1911 hostname=None, ldapadminpass=None, root=None,
1913 ldap_backend_extra_port=None,
1917 ldap_dryrun_mode=False):
1919 if ldap_backend_extra_port is not None:
1920 serverport = "ServerPort=%d" % ldap_backend_extra_port
1924 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1926 "HOSTNAME": hostname,
1927 "DNSDOMAIN": names.dnsdomain,
1928 "LDAPDIR": paths.ldapdir,
1929 "DOMAINDN": names.domaindn,
1930 "LDAPMANAGERDN": names.ldapmanagerdn,
1931 "LDAPMANAGERPASS": ldapadminpass,
1932 "SERVERPORT": serverport})
1934 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1935 {"CONFIGDN": names.configdn,
1936 "SCHEMADN": names.schemadn,
1937 "SAMBADN": names.sambadn,
1940 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1941 {"SAMBADN": names.sambadn,
1944 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1946 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1948 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1949 memberof_config = ""
1953 for attr in lnkattr.keys():
1954 if lnkattr[attr] is not None:
1955 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1956 { "ARG_NUMBER" : str(argnum) ,
1957 "LINK_ATTR" : attr })
1958 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1959 { "MEMBER_ATTR" : attr ,
1960 "MEMBEROF_ATTR" : lnkattr[attr] })
1961 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1965 open(paths.fedoradsrefint, 'w').write(refint_config)
1966 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
1968 attrs = ["lDAPDisplayName"]
1969 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1971 for i in range (0, len(res)):
1972 attr = res[i]["lDAPDisplayName"][0]
1974 if attr == "objectGUID":
1977 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1980 open(paths.fedoradsindex, 'w').write(index_config)
1982 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1983 {"SAMBADN": names.sambadn,
1984 "LDAPADMINPASS": ldapadminpass
1987 mapping = "schema-map-fedora-ds-1.0"
1988 backend_schema = "99_ad.ldif"
1990 # Build a schema file in Fedora DS format
1991 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1992 assert backend_schema_data is not None
1993 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1995 result.credentials.set_bind_dn(names.ldapmanagerdn)
1997 # Destory the target directory, or else setup-ds.pl will complain
1998 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1999 shutil.rmtree(fedora_ds_dir, True)
2001 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2002 #In the 'provision' command line, stay in the foreground so we can easily kill it
2003 result.slapd_provision_command.append("-d0")
2005 #the command for the final run is the normal script
2006 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2008 # If we were just looking for crashes up to this point, it's a
2009 # good time to exit before we realise we don't have Fedora DS on
2010 if ldap_dryrun_mode:
2013 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2014 if setup_ds_path is None:
2015 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\"!")
2016 if not os.path.exists(setup_ds_path):
2017 message (setup_ds_path)
2018 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2020 # Run the Fedora DS setup utility
2021 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2023 raise ProvisioningError("setup-ds failed")
2026 retcode = subprocess.call([
2027 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2028 close_fds=True, shell=False)
2030 raise("ldib2db failed")
2032 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2033 """Create a PHP LDAP admin configuration file.
2035 :param path: Path to write the configuration to.
2036 :param setup_path: Function to generate setup paths.
2038 setup_file(setup_path("phpldapadmin-config.php"), path,
2039 {"S4_LDAPI_URI": ldapi_uri})
2042 def create_zone_file(path, setup_path, dnsdomain,
2043 hostip, hostip6, hostname, realm, domainguid,
2045 """Write out a DNS zone file, from the info in the current database.
2047 :param path: Path of the new zone file.
2048 :param setup_path: Setup path function.
2049 :param dnsdomain: DNS Domain name
2050 :param domaindn: DN of the Domain
2051 :param hostip: Local IPv4 IP
2052 :param hostip6: Local IPv6 IP
2053 :param hostname: Local hostname
2054 :param realm: Realm name
2055 :param domainguid: GUID of the domain.
2056 :param ntdsguid: GUID of the hosts nTDSDSA record.
2058 assert isinstance(domainguid, str)
2060 if hostip6 is not None:
2061 hostip6_base_line = " IN AAAA " + hostip6
2062 hostip6_host_line = hostname + " IN AAAA " + hostip6
2064 hostip6_base_line = ""
2065 hostip6_host_line = ""
2067 if hostip is not None:
2068 hostip_base_line = " IN A " + hostip
2069 hostip_host_line = hostname + " IN A " + hostip
2071 hostip_base_line = ""
2072 hostip_host_line = ""
2074 setup_file(setup_path("provision.zone"), path, {
2075 "HOSTNAME": hostname,
2076 "DNSDOMAIN": dnsdomain,
2078 "HOSTIP_BASE_LINE": hostip_base_line,
2079 "HOSTIP_HOST_LINE": hostip_host_line,
2080 "DOMAINGUID": domainguid,
2081 "DATESTRING": time.strftime("%Y%m%d%H"),
2082 "DEFAULTSITE": DEFAULTSITE,
2083 "NTDSGUID": ntdsguid,
2084 "HOSTIP6_BASE_LINE": hostip6_base_line,
2085 "HOSTIP6_HOST_LINE": hostip6_host_line,
2089 def create_named_conf(path, setup_path, realm, dnsdomain,
2091 """Write out a file containing zone statements suitable for inclusion in a
2092 named.conf file (including GSS-TSIG configuration).
2094 :param path: Path of the new named.conf file.
2095 :param setup_path: Setup path function.
2096 :param realm: Realm name
2097 :param dnsdomain: DNS Domain name
2098 :param private_dir: Path to private directory
2099 :param keytab_name: File name of DNS keytab file
2102 setup_file(setup_path("named.conf"), path, {
2103 "DNSDOMAIN": dnsdomain,
2105 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2106 "PRIVATE_DIR": private_dir
2109 def create_named_txt(path, setup_path, realm, dnsdomain,
2110 private_dir, keytab_name):
2111 """Write out a file containing zone statements suitable for inclusion in a
2112 named.conf file (including GSS-TSIG configuration).
2114 :param path: Path of the new named.conf file.
2115 :param setup_path: Setup path function.
2116 :param realm: Realm name
2117 :param dnsdomain: DNS Domain name
2118 :param private_dir: Path to private directory
2119 :param keytab_name: File name of DNS keytab file
2122 setup_file(setup_path("named.txt"), path, {
2123 "DNSDOMAIN": dnsdomain,
2125 "DNS_KEYTAB": keytab_name,
2126 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2127 "PRIVATE_DIR": private_dir
2130 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2131 """Write out a file containing zone statements suitable for inclusion in a
2132 named.conf file (including GSS-TSIG configuration).
2134 :param path: Path of the new named.conf file.
2135 :param setup_path: Setup path function.
2136 :param dnsdomain: DNS Domain name
2137 :param hostname: Local hostname
2138 :param realm: Realm name
2141 setup_file(setup_path("krb5.conf"), path, {
2142 "DNSDOMAIN": dnsdomain,
2143 "HOSTNAME": hostname,