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.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
373 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
374 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
375 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
376 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
377 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
378 paths.phpldapadminconfig = os.path.join(paths.private_dir,
379 "phpldapadmin-config.php")
380 paths.ldapdir = os.path.join(paths.private_dir,
382 paths.slapdconf = os.path.join(paths.ldapdir,
384 paths.slapdpid = os.path.join(paths.ldapdir,
386 paths.modulesconf = os.path.join(paths.ldapdir,
388 paths.memberofconf = os.path.join(paths.ldapdir,
390 paths.fedoradsinf = os.path.join(paths.ldapdir,
392 paths.fedoradspartitions = os.path.join(paths.ldapdir,
393 "fedorads-partitions.ldif")
394 paths.fedoradssasl = os.path.join(paths.ldapdir,
395 "fedorads-sasl.ldif")
396 paths.fedoradspam = os.path.join(paths.ldapdir,
398 paths.fedoradsrefint = os.path.join(paths.ldapdir,
399 "fedorads-refint.ldif")
400 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
401 "fedorads-linked-attributes.ldif")
402 paths.fedoradsindex = os.path.join(paths.ldapdir,
403 "fedorads-index.ldif")
404 paths.fedoradssamba = os.path.join(paths.ldapdir,
405 "fedorads-samba.ldif")
406 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
407 "mmr_serverids.conf")
408 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
410 paths.olcdir = os.path.join(paths.ldapdir,
412 paths.olcseedldif = os.path.join(paths.ldapdir,
414 paths.hklm = "hklm.ldb"
415 paths.hkcr = "hkcr.ldb"
416 paths.hkcu = "hkcu.ldb"
417 paths.hku = "hku.ldb"
418 paths.hkpd = "hkpd.ldb"
419 paths.hkpt = "hkpt.ldb"
421 paths.sysvol = lp.get("path", "sysvol")
423 paths.netlogon = lp.get("path", "netlogon")
425 paths.smbconf = lp.configfile
430 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
431 serverrole=None, rootdn=None, domaindn=None, configdn=None,
432 schemadn=None, serverdn=None, sitename=None, sambadn=None):
433 """Guess configuration settings to use."""
436 hostname = socket.gethostname().split(".")[0].lower()
438 netbiosname = hostname.upper()
439 if not valid_netbios_name(netbiosname):
440 raise InvalidNetbiosName(netbiosname)
442 hostname = hostname.lower()
444 if dnsdomain is None:
445 dnsdomain = lp.get("realm").lower()
447 if serverrole is None:
448 serverrole = lp.get("server role")
450 assert dnsdomain is not None
451 realm = dnsdomain.upper()
453 if lp.get("realm").upper() != realm:
454 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
455 (lp.get("realm"), lp.configfile, realm))
457 if serverrole == "domain controller":
459 domain = lp.get("workgroup")
461 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
462 if lp.get("workgroup").upper() != domain.upper():
463 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
464 lp.get("workgroup"), domain)
468 domaindn = "DC=" + netbiosname
470 assert domain is not None
471 domain = domain.upper()
473 if not valid_netbios_name(domain):
474 raise InvalidNetbiosName(domain)
476 if netbiosname.upper() == realm:
477 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
479 if hostname.upper() == realm:
480 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
482 if domain.upper() == realm:
483 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
489 configdn = "CN=Configuration," + rootdn
491 schemadn = "CN=Schema," + configdn
498 names = ProvisionNames()
499 names.rootdn = rootdn
500 names.domaindn = domaindn
501 names.configdn = configdn
502 names.schemadn = schemadn
503 names.sambadn = sambadn
504 names.ldapmanagerdn = "CN=Manager," + rootdn
505 names.dnsdomain = dnsdomain
506 names.domain = domain
508 names.netbiosname = netbiosname
509 names.hostname = hostname
510 names.sitename = sitename
511 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
516 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
518 """Create a new smb.conf file based on a couple of basic settings.
520 assert smbconf is not None
522 hostname = socket.gethostname().split(".")[0].lower()
524 if serverrole is None:
525 serverrole = "standalone"
527 assert serverrole in ("domain controller", "member server", "standalone")
528 if serverrole == "domain controller":
530 elif serverrole == "member server":
531 smbconfsuffix = "member"
532 elif serverrole == "standalone":
533 smbconfsuffix = "standalone"
535 assert domain is not None
536 assert realm is not None
538 default_lp = param.LoadParm()
539 #Load non-existant file
540 if os.path.exists(smbconf):
541 default_lp.load(smbconf)
543 if targetdir is not None:
544 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
545 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
547 default_lp.set("lock dir", os.path.abspath(targetdir))
552 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
553 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
555 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
557 "HOSTNAME": hostname,
560 "SERVERROLE": serverrole,
561 "NETLOGONPATH": netlogon,
562 "SYSVOLPATH": sysvol,
563 "PRIVATEDIR_LINE": privatedir_line,
564 "LOCKDIR_LINE": lockdir_line
568 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
569 users_gid, wheel_gid):
570 """setup reasonable name mappings for sam names to unix names.
572 :param samdb: SamDB object.
573 :param idmap: IDmap db object.
574 :param sid: The domain sid.
575 :param domaindn: The domain DN.
576 :param root_uid: uid of the UNIX root user.
577 :param nobody_uid: uid of the UNIX nobody user.
578 :param users_gid: gid of the UNIX users group.
579 :param wheel_gid: gid of the UNIX wheel group."""
581 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
582 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
584 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
585 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
587 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
589 serverrole, ldap_backend=None,
591 """Setup the partitions for the SAM database.
593 Alternatively, provision() may call this, and then populate the database.
595 :note: This will wipe the Sam Database!
597 :note: This function always removes the local SAM LDB file. The erase
598 parameter controls whether to erase the existing data, which
599 may not be stored locally but in LDAP.
601 assert session_info is not None
603 # We use options=["modules:"] to stop the modules loading - we
604 # just want to wipe and re-initialise the database, not start it up
607 samdb = Ldb(url=samdb_path, session_info=session_info,
608 credentials=credentials, lp=lp, options=["modules:"])
610 samdb.erase_except_schema_controlled()
612 os.unlink(samdb_path)
613 samdb = Ldb(url=samdb_path, session_info=session_info,
614 credentials=credentials, lp=lp, options=["modules:"])
616 samdb.erase_except_schema_controlled()
619 #Add modules to the list to activate them by default
620 #beware often order is important
622 # Some Known ordering constraints:
623 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
624 # - objectclass must be before password_hash, because password_hash checks
625 # that the objectclass is of type person (filled in by objectclass
626 # module when expanding the objectclass list)
627 # - partition must be last
628 # - each partition has its own module list then
629 modules_list = ["resolve_oids",
652 "extended_dn_out_ldb"]
653 modules_list2 = ["show_deleted",
656 domaindn_ldb = "users.ldb"
657 configdn_ldb = "configuration.ldb"
658 schemadn_ldb = "schema.ldb"
659 if ldap_backend is not None:
660 domaindn_ldb = ldap_backend.ldapi_uri
661 configdn_ldb = ldap_backend.ldapi_uri
662 schemadn_ldb = ldap_backend.ldapi_uri
664 if ldap_backend.ldap_backend_type == "fedora-ds":
665 backend_modules = ["nsuniqueid", "paged_searches"]
666 # We can handle linked attributes here, as we don't have directory-side subtree operations
667 tdb_modules_list = ["extended_dn_out_dereference"]
668 elif ldap_backend.ldap_backend_type == "openldap":
669 backend_modules = ["entryuuid", "paged_searches"]
670 # OpenLDAP handles subtree renames, so we don't want to do any of these things
671 tdb_modules_list = ["extended_dn_out_dereference"]
673 elif serverrole == "domain controller":
674 tdb_modules_list.insert(0, "repl_meta_data")
677 backend_modules = ["objectguid"]
679 if tdb_modules_list is None:
680 tdb_modules_list_as_string = ""
682 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
684 samdb.transaction_start()
686 message("Setting up sam.ldb partitions and settings")
687 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
688 "SCHEMADN": names.schemadn,
689 "SCHEMADN_LDB": schemadn_ldb,
690 "SCHEMADN_MOD2": ",objectguid",
691 "CONFIGDN": names.configdn,
692 "CONFIGDN_LDB": configdn_ldb,
693 "DOMAINDN": names.domaindn,
694 "DOMAINDN_LDB": domaindn_ldb,
695 "SCHEMADN_MOD": "schema_fsmo",
696 "CONFIGDN_MOD": "naming_fsmo",
697 "DOMAINDN_MOD": "pdc_fsmo",
698 "MODULES_LIST": ",".join(modules_list),
699 "TDB_MODULES_LIST": tdb_modules_list_as_string,
700 "MODULES_LIST2": ",".join(modules_list2),
701 "BACKEND_MOD": ",".join(backend_modules),
704 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
706 message("Setting up sam.ldb rootDSE")
707 setup_samdb_rootdse(samdb, setup_path, names)
710 samdb.transaction_cancel()
713 samdb.transaction_commit()
715 def secretsdb_self_join(secretsdb, domain,
716 netbiosname, domainsid, machinepass,
717 realm=None, dnsdomain=None,
719 key_version_number=1,
720 secure_channel_type=SEC_CHAN_WKSTA):
721 """Add domain join-specific bits to a secrets database.
723 :param secretsdb: Ldb Handle to the secrets database
724 :param machinepass: Machine password
726 attrs=["whenChanged",
734 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
735 msg["secureChannelType"] = str(secure_channel_type)
736 msg["flatname"] = [domain]
737 msg["objectClass"] = ["top", "primaryDomain"]
738 if realm is not None:
739 if dnsdomain is None:
740 dnsdomain = realm.lower()
741 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
743 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
744 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
745 msg["privateKeytab"] = ["secrets.keytab"];
748 msg["secret"] = [machinepass]
749 msg["samAccountName"] = ["%s$" % netbiosname]
750 msg["secureChannelType"] = [str(secure_channel_type)]
751 msg["objectSid"] = [ndr_pack(domainsid)]
753 res = secretsdb.search(base="cn=Primary Domains",
755 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
756 scope=SCOPE_ONELEVEL)
759 if del_msg.dn is not msg.dn:
760 secretsdb.delete(del_msg.dn)
762 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
765 msg["priorSecret"] = res[0]["secret"]
766 msg["priorWhenChanged"] = res[0]["whenChanged"]
768 if res["privateKeytab"] is not None:
769 msg["privateKeytab"] = res[0]["privateKeytab"]
771 if res["krb5Keytab"] is not None:
772 msg["krb5Keytab"] = res[0]["krb5Keytab"]
775 el.set_flags(ldb.FLAG_MOD_REPLACE)
776 secretsdb.modify(msg)
781 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
782 dns_keytab_path, dnspass):
783 """Add DNS specific bits to a secrets database.
785 :param secretsdb: Ldb Handle to the secrets database
786 :param setup_path: Setup path function
787 :param machinepass: Machine password
789 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
791 "DNSDOMAIN": dnsdomain,
792 "DNS_KEYTAB": dns_keytab_path,
793 "DNSPASS_B64": b64encode(dnspass),
797 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
798 """Setup the secrets database.
800 :param path: Path to the secrets database.
801 :param setup_path: Get the path to a setup file.
802 :param session_info: Session info.
803 :param credentials: Credentials
804 :param lp: Loadparm context
805 :return: LDB handle for the created secrets database
807 if os.path.exists(path):
809 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
812 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
813 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
815 secrets_ldb.transaction_start()
816 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
818 if credentials is not None and credentials.authentication_requested():
819 if credentials.get_bind_dn() is not None:
820 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
821 "LDAPMANAGERDN": credentials.get_bind_dn(),
822 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
825 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
826 "LDAPADMINUSER": credentials.get_username(),
827 "LDAPADMINREALM": credentials.get_realm(),
828 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
833 def setup_registry(path, setup_path, session_info, lp):
834 """Setup the registry.
836 :param path: Path to the registry database
837 :param setup_path: Function that returns the path to a setup.
838 :param session_info: Session information
839 :param credentials: Credentials
840 :param lp: Loadparm context
842 reg = registry.Registry()
843 hive = registry.open_ldb(path, session_info=session_info,
845 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
846 provision_reg = setup_path("provision.reg")
847 assert os.path.exists(provision_reg)
848 reg.diff_apply(provision_reg)
851 def setup_idmapdb(path, setup_path, session_info, lp):
852 """Setup the idmap database.
854 :param path: path to the idmap database
855 :param setup_path: Function that returns a path to a setup file
856 :param session_info: Session information
857 :param credentials: Credentials
858 :param lp: Loadparm context
860 if os.path.exists(path):
863 idmap_ldb = IDmapDB(path, session_info=session_info,
867 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
871 def setup_samdb_rootdse(samdb, setup_path, names):
872 """Setup the SamDB rootdse.
874 :param samdb: Sam Database handle
875 :param setup_path: Obtain setup path
877 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
878 "SCHEMADN": names.schemadn,
879 "NETBIOSNAME": names.netbiosname,
880 "DNSDOMAIN": names.dnsdomain,
881 "REALM": names.realm,
882 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
883 "DOMAINDN": names.domaindn,
884 "ROOTDN": names.rootdn,
885 "CONFIGDN": names.configdn,
886 "SERVERDN": names.serverdn,
890 def setup_self_join(samdb, names,
891 machinepass, dnspass,
892 domainsid, invocationid, setup_path,
893 policyguid, policyguid_dc, domainControllerFunctionality,
895 """Join a host to its own domain."""
896 assert isinstance(invocationid, str)
897 if ntdsguid is not None:
898 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
901 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
902 "CONFIGDN": names.configdn,
903 "SCHEMADN": names.schemadn,
904 "DOMAINDN": names.domaindn,
905 "SERVERDN": names.serverdn,
906 "INVOCATIONID": invocationid,
907 "NETBIOSNAME": names.netbiosname,
908 "DEFAULTSITE": names.sitename,
909 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
910 "MACHINEPASS_B64": b64encode(machinepass),
911 "DNSPASS_B64": b64encode(dnspass),
912 "REALM": names.realm,
913 "DOMAIN": names.domain,
914 "DNSDOMAIN": names.dnsdomain,
915 "SAMBA_VERSION_STRING": version,
916 "NTDSGUID": ntdsguid_line,
917 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
919 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
920 "POLICYGUID": policyguid,
921 "POLICYGUID_DC": policyguid_dc,
922 "DNSDOMAIN": names.dnsdomain,
923 "DOMAINSID": str(domainsid),
924 "DOMAINDN": names.domaindn})
926 # add the NTDSGUID based SPNs
927 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
928 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
929 expression="", scope=SCOPE_BASE)
930 assert isinstance(names.ntdsguid, str)
932 # Setup fSMORoleOwner entries to point at the newly created DC entry
933 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
934 "DOMAIN": names.domain,
935 "DNSDOMAIN": names.dnsdomain,
936 "DOMAINDN": names.domaindn,
937 "CONFIGDN": names.configdn,
938 "SCHEMADN": names.schemadn,
939 "DEFAULTSITE": names.sitename,
940 "SERVERDN": names.serverdn,
941 "NETBIOSNAME": names.netbiosname,
942 "NTDSGUID": names.ntdsguid
946 def setup_samdb(path, setup_path, session_info, credentials, lp,
948 domainsid, domainguid, policyguid, policyguid_dc,
949 fill, adminpass, krbtgtpass,
950 machinepass, invocationid, dnspass, ntdsguid,
951 serverrole, dom_for_fun_level=None,
952 schema=None, ldap_backend=None):
953 """Setup a complete SAM Database.
955 :note: This will wipe the main SAM database file!
958 # ATTENTION: Do NOT change these default values without discussion with the
959 # team and/or release manager. They have a big impact on the whole program!
960 domainControllerFunctionality = DS_DC_FUNCTION_2008
962 if dom_for_fun_level is None:
963 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
964 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
965 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
967 if dom_for_fun_level > domainControllerFunctionality:
968 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!")
970 domainFunctionality = dom_for_fun_level
971 forestFunctionality = dom_for_fun_level
973 # Also wipes the database
974 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
975 credentials=credentials, session_info=session_info,
976 names=names, ldap_backend=ldap_backend,
977 serverrole=serverrole)
980 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
981 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
983 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
984 samdb = Ldb(session_info=session_info,
985 credentials=credentials, lp=lp)
987 message("Pre-loading the Samba 4 and AD schema")
989 # Load the schema from the one we computed earlier
990 samdb.set_schema_from_ldb(schema.ldb)
992 # And now we can connect to the DB - the schema won't be loaded from the DB
996 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
1001 samdb.transaction_start()
1003 message("Erasing data from partitions")
1004 # Load the schema (again). This time it will force a reindex,
1005 # and will therefore make the erase_partitions() below
1006 # computationally sane
1007 samdb.set_schema_from_ldb(schema.ldb)
1008 samdb.erase_partitions()
1010 # Set the domain functionality levels onto the database.
1011 # Various module (the password_hash module in particular) need
1012 # to know what level of AD we are emulating.
1014 # These will be fixed into the database via the database
1015 # modifictions below, but we need them set from the start.
1016 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1017 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1018 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1020 samdb.set_domain_sid(str(domainsid))
1021 if serverrole == "domain controller":
1022 samdb.set_invocation_id(invocationid)
1024 message("Adding DomainDN: %s" % names.domaindn)
1026 #impersonate domain admin
1027 admin_session_info = admin_session(lp, str(domainsid))
1028 samdb.set_session_info(admin_session_info)
1029 if domainguid is not None:
1030 domainguid_line = "objectGUID: %s\n-" % domainguid
1032 domainguid_line = ""
1033 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1034 "DOMAINDN": names.domaindn,
1035 "DOMAINGUID": domainguid_line
1039 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1040 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1041 "DOMAINSID": str(domainsid),
1042 "SCHEMADN": names.schemadn,
1043 "NETBIOSNAME": names.netbiosname,
1044 "DEFAULTSITE": names.sitename,
1045 "CONFIGDN": names.configdn,
1046 "SERVERDN": names.serverdn,
1047 "POLICYGUID": policyguid,
1048 "DOMAINDN": names.domaindn,
1049 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1050 "SAMBA_VERSION_STRING": version
1053 message("Adding configuration container")
1054 descr = get_config_descriptor(domainsid);
1055 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1056 "CONFIGDN": names.configdn,
1057 "DESCRIPTOR": descr,
1059 message("Modifying configuration container")
1060 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1061 "CONFIGDN": names.configdn,
1062 "SCHEMADN": names.schemadn,
1065 # The LDIF here was created when the Schema object was constructed
1066 message("Setting up sam.ldb schema")
1067 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1068 samdb.modify_ldif(schema.schema_dn_modify)
1069 samdb.write_prefixes_from_schema()
1070 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1071 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1072 {"SCHEMADN": names.schemadn})
1074 message("Setting up sam.ldb configuration data")
1075 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1076 "CONFIGDN": names.configdn,
1077 "NETBIOSNAME": names.netbiosname,
1078 "DEFAULTSITE": names.sitename,
1079 "DNSDOMAIN": names.dnsdomain,
1080 "DOMAIN": names.domain,
1081 "SCHEMADN": names.schemadn,
1082 "DOMAINDN": names.domaindn,
1083 "SERVERDN": names.serverdn,
1084 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1087 message("Setting up display specifiers")
1088 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1089 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1090 check_all_substituted(display_specifiers_ldif)
1091 samdb.add_ldif(display_specifiers_ldif)
1093 message("Adding users container")
1094 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1095 "DOMAINDN": names.domaindn})
1096 message("Modifying users container")
1097 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1098 "DOMAINDN": names.domaindn})
1099 message("Adding computers container")
1100 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1101 "DOMAINDN": names.domaindn})
1102 message("Modifying computers container")
1103 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1104 "DOMAINDN": names.domaindn})
1105 message("Setting up sam.ldb data")
1106 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1107 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1108 "DOMAINDN": names.domaindn,
1109 "NETBIOSNAME": names.netbiosname,
1110 "DEFAULTSITE": names.sitename,
1111 "CONFIGDN": names.configdn,
1112 "SERVERDN": names.serverdn,
1113 "POLICYGUID_DC": policyguid_dc
1116 if fill == FILL_FULL:
1117 message("Setting up sam.ldb users and groups")
1118 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1119 "DOMAINDN": names.domaindn,
1120 "DOMAINSID": str(domainsid),
1121 "CONFIGDN": names.configdn,
1122 "ADMINPASS_B64": b64encode(adminpass),
1123 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1126 if serverrole == "domain controller":
1127 message("Setting up self join")
1128 setup_self_join(samdb, names=names, invocationid=invocationid,
1130 machinepass=machinepass,
1131 domainsid=domainsid, policyguid=policyguid,
1132 policyguid_dc=policyguid_dc,
1133 setup_path=setup_path,
1134 domainControllerFunctionality=domainControllerFunctionality,
1137 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1138 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1139 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1140 assert isinstance(names.ntdsguid, str)
1143 samdb.transaction_cancel()
1146 samdb.transaction_commit()
1151 FILL_NT4SYNC = "NT4SYNC"
1155 def provision(setup_dir, message, session_info,
1156 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1158 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1160 domain=None, hostname=None, hostip=None, hostip6=None,
1161 domainsid=None, adminpass=None, ldapadminpass=None,
1162 krbtgtpass=None, domainguid=None,
1163 policyguid=None, policyguid_dc=None, invocationid=None,
1164 machinepass=None, ntdsguid=None,
1165 dnspass=None, root=None, nobody=None, users=None,
1166 wheel=None, backup=None, aci=None, serverrole=None,
1167 dom_for_fun_level=None,
1168 ldap_backend_extra_port=None, ldap_backend_type=None,
1170 ol_mmr_urls=None, ol_olc=None,
1171 setup_ds_path=None, slapd_path=None, nosync=False,
1172 ldap_dryrun_mode=False):
1175 :note: caution, this wipes all existing data!
1178 def setup_path(file):
1179 return os.path.join(setup_dir, file)
1181 if domainsid is None:
1182 domainsid = security.random_sid()
1184 domainsid = security.dom_sid(domainsid)
1186 # create/adapt the group policy GUIDs
1187 if policyguid is None:
1188 policyguid = str(uuid.uuid4())
1189 policyguid = policyguid.upper()
1190 if policyguid_dc is None:
1191 policyguid_dc = str(uuid.uuid4())
1192 policyguid_dc = policyguid_dc.upper()
1194 if adminpass is None:
1195 adminpass = glue.generate_random_str(12)
1196 if krbtgtpass is None:
1197 krbtgtpass = glue.generate_random_str(12)
1198 if machinepass is None:
1199 machinepass = glue.generate_random_str(12)
1201 dnspass = glue.generate_random_str(12)
1202 if ldapadminpass is None:
1203 #Make a new, random password between Samba and it's LDAP server
1204 ldapadminpass=glue.generate_random_str(12)
1207 root_uid = findnss_uid([root or "root"])
1208 nobody_uid = findnss_uid([nobody or "nobody"])
1209 users_gid = findnss_gid([users or "users"])
1211 wheel_gid = findnss_gid(["wheel", "adm"])
1213 wheel_gid = findnss_gid([wheel])
1215 if targetdir is not None:
1216 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1217 os.makedirs(os.path.join(targetdir, "etc"))
1218 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1219 elif smbconf is None:
1220 smbconf = param.default_path()
1222 # only install a new smb.conf if there isn't one there already
1223 if not os.path.exists(smbconf):
1224 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1227 lp = param.LoadParm()
1230 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1231 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1232 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1235 paths = provision_paths_from_lp(lp, names.dnsdomain)
1239 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1240 except socket.gaierror, (socket.EAI_NODATA, msg):
1245 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1246 except socket.gaierror, (socket.EAI_NODATA, msg):
1249 if serverrole is None:
1250 serverrole = lp.get("server role")
1252 assert serverrole in ("domain controller", "member server", "standalone")
1253 if invocationid is None and serverrole == "domain controller":
1254 invocationid = str(uuid.uuid4())
1256 if not os.path.exists(paths.private_dir):
1257 os.mkdir(paths.private_dir)
1259 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1261 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1262 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1264 secrets_credentials = credentials
1265 provision_backend = None
1266 if ldap_backend_type:
1267 # We only support an LDAP backend over ldapi://
1269 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1270 lp=lp, credentials=credentials,
1272 message=message, hostname=hostname,
1273 root=root, schema=schema,
1274 ldap_backend_type=ldap_backend_type,
1275 ldapadminpass=ldapadminpass,
1276 ldap_backend_extra_port=ldap_backend_extra_port,
1277 ol_mmr_urls=ol_mmr_urls,
1278 slapd_path=slapd_path,
1279 setup_ds_path=setup_ds_path,
1280 ldap_dryrun_mode=ldap_dryrun_mode)
1282 # Now use the backend credentials to access the databases
1283 credentials = provision_backend.credentials
1284 secrets_credentials = provision_backend.adminCredentials
1285 ldapi_url = provision_backend.ldapi_uri
1287 # only install a new shares config db if there is none
1288 if not os.path.exists(paths.shareconf):
1289 message("Setting up share.ldb")
1290 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1291 credentials=credentials, lp=lp)
1292 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1295 message("Setting up secrets.ldb")
1296 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1297 session_info=session_info,
1298 credentials=secrets_credentials, lp=lp)
1300 message("Setting up the registry")
1301 setup_registry(paths.hklm, setup_path, session_info,
1304 message("Setting up idmap db")
1305 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1308 message("Setting up SAM db")
1309 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1310 credentials=credentials, lp=lp, names=names,
1312 domainsid=domainsid,
1313 schema=schema, domainguid=domainguid,
1314 policyguid=policyguid, policyguid_dc=policyguid_dc,
1316 adminpass=adminpass, krbtgtpass=krbtgtpass,
1317 invocationid=invocationid,
1318 machinepass=machinepass, dnspass=dnspass,
1319 ntdsguid=ntdsguid, serverrole=serverrole,
1320 dom_for_fun_level=dom_for_fun_level,
1321 ldap_backend=provision_backend)
1323 if serverrole == "domain controller":
1324 if paths.netlogon is None:
1325 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1326 message("Please either remove %s or see the template at %s" %
1327 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1328 assert(paths.netlogon is not None)
1330 if paths.sysvol is None:
1331 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1332 message("Please either remove %s or see the template at %s" %
1333 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1334 assert(paths.sysvol is not None)
1336 # Set up group policies (domain policy and domain controller policy)
1338 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1339 "{" + policyguid + "}")
1340 os.makedirs(policy_path, 0755)
1341 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1342 "[General]\r\nVersion=65543")
1343 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1344 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1346 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1347 "{" + policyguid_dc + "}")
1348 os.makedirs(policy_path_dc, 0755)
1349 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1350 "[General]\r\nVersion=2")
1351 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1352 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1354 if not os.path.isdir(paths.netlogon):
1355 os.makedirs(paths.netlogon, 0755)
1357 if samdb_fill == FILL_FULL:
1358 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1359 root_uid=root_uid, nobody_uid=nobody_uid,
1360 users_gid=users_gid, wheel_gid=wheel_gid)
1362 message("Setting up sam.ldb rootDSE marking as synchronized")
1363 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1365 # Only make a zone file on the first DC, it should be replicated with DNS replication
1366 if serverrole == "domain controller":
1367 secretsdb_self_join(secrets_ldb, domain=domain,
1369 dnsdomain=names.dnsdomain,
1370 netbiosname=names.netbiosname,
1371 domainsid=domainsid,
1372 machinepass=machinepass,
1373 secure_channel_type=SEC_CHAN_BDC)
1375 secretsdb_setup_dns(secrets_ldb, setup_path,
1376 realm=names.realm, dnsdomain=names.dnsdomain,
1377 dns_keytab_path=paths.dns_keytab,
1380 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1381 assert isinstance(domainguid, str)
1383 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1385 hostip6=hostip6, hostname=names.hostname,
1387 domainguid=domainguid, ntdsguid=names.ntdsguid)
1389 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1390 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1392 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1393 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1394 keytab_name=paths.dns_keytab)
1395 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1396 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1398 create_krb5_conf(paths.krb5conf, setup_path,
1399 dnsdomain=names.dnsdomain, hostname=names.hostname,
1401 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1403 #Now commit the secrets.ldb to disk
1404 secrets_ldb.transaction_commit()
1406 if provision_backend is not None:
1407 if ldap_backend_type == "fedora-ds":
1408 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1410 # delete default SASL mappings
1411 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1413 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1414 for i in range (0, len(res)):
1415 dn = str(res[i]["dn"])
1418 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1421 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1423 m.dn = ldb.Dn(1, names.domaindn)
1426 m.dn = ldb.Dn(1, names.configdn)
1429 m.dn = ldb.Dn(1, names.schemadn)
1432 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1433 if provision_backend.slapd.poll() is None:
1435 if hasattr(provision_backend.slapd, "terminate"):
1436 provision_backend.slapd.terminate()
1438 # Older python versions don't have .terminate()
1440 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1442 #and now wait for it to die
1443 provision_backend.slapd.communicate()
1445 # now display slapd_command_file.txt to show how slapd must be started next time
1446 message("Use later the following commandline to start slapd, then Samba:")
1447 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1448 message(slapd_command)
1449 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1451 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1452 "SLAPD_COMMAND" : slapd_command})
1455 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1458 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1460 message("Once the above files are installed, your Samba4 server will be ready to use")
1461 message("Server Role: %s" % serverrole)
1462 message("Hostname: %s" % names.hostname)
1463 message("NetBIOS Domain: %s" % names.domain)
1464 message("DNS Domain: %s" % names.dnsdomain)
1465 message("DOMAIN SID: %s" % str(domainsid))
1466 if samdb_fill == FILL_FULL:
1467 message("Admin password: %s" % adminpass)
1468 if provision_backend:
1469 if provision_backend.credentials.get_bind_dn() is not None:
1470 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1472 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1474 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1476 result = ProvisionResult()
1477 result.domaindn = domaindn
1478 result.paths = paths
1480 result.samdb = samdb
1485 def provision_become_dc(setup_dir=None,
1486 smbconf=None, targetdir=None, realm=None,
1487 rootdn=None, domaindn=None, schemadn=None,
1488 configdn=None, serverdn=None,
1489 domain=None, hostname=None, domainsid=None,
1490 adminpass=None, krbtgtpass=None, domainguid=None,
1491 policyguid=None, policyguid_dc=None, invocationid=None,
1493 dnspass=None, root=None, nobody=None, users=None,
1494 wheel=None, backup=None, serverrole=None,
1495 ldap_backend=None, ldap_backend_type=None,
1496 sitename=None, debuglevel=1):
1499 """print a message if quiet is not set."""
1502 glue.set_debug_level(debuglevel)
1504 return provision(setup_dir, message, system_session(), None,
1505 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1506 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1507 configdn=configdn, serverdn=serverdn, domain=domain,
1508 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1509 machinepass=machinepass, serverrole="domain controller",
1513 def setup_db_config(setup_path, dbdir):
1514 """Setup a Berkeley database.
1516 :param setup_path: Setup path function.
1517 :param dbdir: Database directory."""
1518 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1519 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1520 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1521 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1523 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1524 {"LDAPDBDIR": dbdir})
1526 class ProvisionBackend(object):
1527 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1528 names=None, message=None,
1529 hostname=None, root=None,
1530 schema=None, ldapadminpass=None,
1531 ldap_backend_type=None, ldap_backend_extra_port=None,
1533 setup_ds_path=None, slapd_path=None,
1534 nosync=False, ldap_dryrun_mode=False):
1535 """Provision an LDAP backend for samba4
1537 This works for OpenLDAP and Fedora DS
1540 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1542 if not os.path.isdir(paths.ldapdir):
1543 os.makedirs(paths.ldapdir, 0700)
1545 if ldap_backend_type == "existing":
1546 #Check to see that this 'existing' LDAP backend in fact exists
1547 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1548 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1549 expression="(objectClass=OpenLDAProotDSE)")
1551 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1552 # This caused them to be set into the long-term database later in the script.
1553 self.credentials = credentials
1554 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1557 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1558 # if another instance of slapd is already running
1560 ldapi_db = Ldb(self.ldapi_uri)
1561 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1562 expression="(objectClass=OpenLDAProotDSE)");
1564 f = open(paths.slapdpid, "r")
1567 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1571 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1576 # Try to print helpful messages when the user has not specified the path to slapd
1577 if slapd_path is None:
1578 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1579 if not os.path.exists(slapd_path):
1580 message (slapd_path)
1581 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1583 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1585 os.unlink(schemadb_path)
1590 # Put the LDIF of the schema into a database so we can search on
1591 # it to generate schema-dependent configurations in Fedora DS and
1593 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1594 schema.ldb.connect(schemadb_path)
1595 schema.ldb.transaction_start()
1597 # These bits of LDIF are supplied when the Schema object is created
1598 schema.ldb.add_ldif(schema.schema_dn_add)
1599 schema.ldb.modify_ldif(schema.schema_dn_modify)
1600 schema.ldb.add_ldif(schema.schema_data)
1601 schema.ldb.transaction_commit()
1603 self.credentials = Credentials()
1604 self.credentials.guess(lp)
1605 #Kerberos to an ldapi:// backend makes no sense
1606 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1608 self.adminCredentials = Credentials()
1609 self.adminCredentials.guess(lp)
1610 #Kerberos to an ldapi:// backend makes no sense
1611 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1613 self.ldap_backend_type = ldap_backend_type
1615 if ldap_backend_type == "fedora-ds":
1616 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1617 names=names, message=message,
1619 ldapadminpass=ldapadminpass, root=root,
1621 ldap_backend_extra_port=ldap_backend_extra_port,
1622 setup_ds_path=setup_ds_path,
1623 slapd_path=slapd_path,
1625 ldap_dryrun_mode=ldap_dryrun_mode)
1627 elif ldap_backend_type == "openldap":
1628 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1629 names=names, message=message,
1631 ldapadminpass=ldapadminpass, root=root,
1633 ldap_backend_extra_port=ldap_backend_extra_port,
1634 ol_mmr_urls=ol_mmr_urls,
1635 slapd_path=slapd_path,
1637 ldap_dryrun_mode=ldap_dryrun_mode)
1639 raise ProvisioningError("Unknown LDAP backend type selected")
1641 self.credentials.set_password(ldapadminpass)
1642 self.adminCredentials.set_username("samba-admin")
1643 self.adminCredentials.set_password(ldapadminpass)
1645 # Now start the slapd, so we can provision onto it. We keep the
1646 # subprocess context around, to kill this off at the successful
1648 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1650 while self.slapd.poll() is None:
1651 # Wait until the socket appears
1653 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1654 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1655 expression="(objectClass=OpenLDAProotDSE)")
1656 # If we have got here, then we must have a valid connection to the LDAP server!
1662 raise ProvisioningError("slapd died before we could make a connection to it")
1665 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1667 hostname=None, ldapadminpass=None, root=None,
1669 ldap_backend_extra_port=None,
1671 slapd_path=None, nosync=False,
1672 ldap_dryrun_mode=False):
1674 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1677 nosync_config = "dbnosync"
1679 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1680 refint_attributes = ""
1681 memberof_config = "# Generated from Samba4 schema\n"
1682 for att in lnkattr.keys():
1683 if lnkattr[att] is not None:
1684 refint_attributes = refint_attributes + " " + att
1686 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1687 { "MEMBER_ATTR" : att ,
1688 "MEMBEROF_ATTR" : lnkattr[att] })
1690 refint_config = read_and_sub_file(setup_path("refint.conf"),
1691 { "LINK_ATTRS" : refint_attributes})
1693 attrs = ["linkID", "lDAPDisplayName"]
1694 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1696 for i in range (0, len(res)):
1697 index_attr = res[i]["lDAPDisplayName"][0]
1698 if index_attr == "objectGUID":
1699 index_attr = "entryUUID"
1701 index_config += "index " + index_attr + " eq\n"
1703 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1705 mmr_replicator_acl = ""
1706 mmr_serverids_config = ""
1707 mmr_syncrepl_schema_config = ""
1708 mmr_syncrepl_config_config = ""
1709 mmr_syncrepl_user_config = ""
1712 if ol_mmr_urls is not None:
1713 # For now, make these equal
1714 mmr_pass = ldapadminpass
1716 url_list=filter(None,ol_mmr_urls.split(' '))
1717 if (len(url_list) == 1):
1718 url_list=filter(None,ol_mmr_urls.split(','))
1721 mmr_on_config = "MirrorMode On"
1722 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1724 for url in url_list:
1726 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1727 { "SERVERID" : str(serverid),
1728 "LDAPSERVER" : url })
1731 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1733 "MMRDN": names.schemadn,
1735 "MMR_PASSWORD": mmr_pass})
1738 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1740 "MMRDN": names.configdn,
1742 "MMR_PASSWORD": mmr_pass})
1745 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1747 "MMRDN": names.domaindn,
1749 "MMR_PASSWORD": mmr_pass })
1750 # OpenLDAP cn=config initialisation
1751 olc_syncrepl_config = ""
1753 # if mmr = yes, generate cn=config-replication directives
1754 # and olc_seed.lif for the other mmr-servers
1755 if ol_mmr_urls is not None:
1757 olc_serverids_config = ""
1758 olc_syncrepl_seed_config = ""
1759 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1761 for url in url_list:
1763 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1764 { "SERVERID" : str(serverid),
1765 "LDAPSERVER" : url })
1768 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1771 "MMR_PASSWORD": mmr_pass})
1773 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1775 "LDAPSERVER" : url})
1777 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1778 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1779 "OLC_PW": ldapadminpass,
1780 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1783 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1784 {"DNSDOMAIN": names.dnsdomain,
1785 "LDAPDIR": paths.ldapdir,
1786 "DOMAINDN": names.domaindn,
1787 "CONFIGDN": names.configdn,
1788 "SCHEMADN": names.schemadn,
1789 "MEMBEROF_CONFIG": memberof_config,
1790 "MIRRORMODE": mmr_on_config,
1791 "REPLICATOR_ACL": mmr_replicator_acl,
1792 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1793 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1794 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1795 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1796 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1797 "OLC_MMR_CONFIG": olc_mmr_config,
1798 "REFINT_CONFIG": refint_config,
1799 "INDEX_CONFIG": index_config,
1800 "NOSYNC": nosync_config})
1802 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1803 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1804 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1806 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1807 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1809 setup_file(setup_path("cn=samba.ldif"),
1810 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1811 { "UUID": str(uuid.uuid4()),
1812 "LDAPTIME": timestring(int(time.time()))} )
1813 setup_file(setup_path("cn=samba-admin.ldif"),
1814 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1815 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1816 "UUID": str(uuid.uuid4()),
1817 "LDAPTIME": timestring(int(time.time()))} )
1819 if ol_mmr_urls is not None:
1820 setup_file(setup_path("cn=replicator.ldif"),
1821 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1822 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1823 "UUID": str(uuid.uuid4()),
1824 "LDAPTIME": timestring(int(time.time()))} )
1827 mapping = "schema-map-openldap-2.3"
1828 backend_schema = "backend-schema.schema"
1830 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1831 assert backend_schema_data is not None
1832 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1834 # now we generate the needed strings to start slapd automatically,
1835 # first ldapi_uri...
1836 if ldap_backend_extra_port is not None:
1837 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1838 # specified there as part of it's clue as to it's own name,
1839 # and not to replicate to itself
1840 if ol_mmr_urls is None:
1841 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1843 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1845 server_port_string = ""
1847 # Prepare the 'result' information - the commands to return in particular
1848 result.slapd_provision_command = [slapd_path]
1850 result.slapd_provision_command.append("-F" + paths.olcdir)
1852 result.slapd_provision_command.append("-h")
1854 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1855 result.slapd_command = list(result.slapd_provision_command)
1857 result.slapd_provision_command.append(result.ldapi_uri)
1858 result.slapd_provision_command.append("-d0")
1860 uris = result.ldapi_uri
1861 if server_port_string is not "":
1862 uris = uris + " " + server_port_string
1864 result.slapd_command.append(uris)
1866 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1867 result.credentials.set_username("samba-admin")
1869 # If we were just looking for crashes up to this point, it's a
1870 # good time to exit before we realise we don't have OpenLDAP on
1872 if ldap_dryrun_mode:
1875 # Finally, convert the configuration into cn=config style!
1876 if not os.path.isdir(paths.olcdir):
1877 os.makedirs(paths.olcdir, 0770)
1879 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1881 # We can't do this, as OpenLDAP is strange. It gives an error
1882 # output to the above, but does the conversion sucessfully...
1885 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1887 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1888 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1890 # Don't confuse the admin by leaving the slapd.conf around
1891 os.remove(paths.slapdconf)
1894 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1896 hostname=None, ldapadminpass=None, root=None,
1898 ldap_backend_extra_port=None,
1902 ldap_dryrun_mode=False):
1904 if ldap_backend_extra_port is not None:
1905 serverport = "ServerPort=%d" % ldap_backend_extra_port
1909 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1911 "HOSTNAME": hostname,
1912 "DNSDOMAIN": names.dnsdomain,
1913 "LDAPDIR": paths.ldapdir,
1914 "DOMAINDN": names.domaindn,
1915 "LDAPMANAGERDN": names.ldapmanagerdn,
1916 "LDAPMANAGERPASS": ldapadminpass,
1917 "SERVERPORT": serverport})
1919 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1920 {"CONFIGDN": names.configdn,
1921 "SCHEMADN": names.schemadn,
1922 "SAMBADN": names.sambadn,
1925 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1926 {"SAMBADN": names.sambadn,
1929 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
1931 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1933 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
1934 memberof_config = ""
1938 for attr in lnkattr.keys():
1939 if lnkattr[attr] is not None:
1940 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
1941 { "ARG_NUMBER" : str(argnum) ,
1942 "LINK_ATTR" : attr })
1943 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
1944 { "MEMBER_ATTR" : attr ,
1945 "MEMBEROF_ATTR" : lnkattr[attr] })
1946 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1950 open(paths.fedoradsrefint, 'w').write(refint_config)
1951 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
1953 attrs = ["lDAPDisplayName"]
1954 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1956 for i in range (0, len(res)):
1957 attr = res[i]["lDAPDisplayName"][0]
1959 if attr == "objectGUID":
1962 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
1965 open(paths.fedoradsindex, 'w').write(index_config)
1967 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1968 {"SAMBADN": names.sambadn,
1969 "LDAPADMINPASS": ldapadminpass
1972 mapping = "schema-map-fedora-ds-1.0"
1973 backend_schema = "99_ad.ldif"
1975 # Build a schema file in Fedora DS format
1976 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1977 assert backend_schema_data is not None
1978 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1980 result.credentials.set_bind_dn(names.ldapmanagerdn)
1982 # Destory the target directory, or else setup-ds.pl will complain
1983 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1984 shutil.rmtree(fedora_ds_dir, True)
1986 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1987 #In the 'provision' command line, stay in the foreground so we can easily kill it
1988 result.slapd_provision_command.append("-d0")
1990 #the command for the final run is the normal script
1991 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1993 # If we were just looking for crashes up to this point, it's a
1994 # good time to exit before we realise we don't have Fedora DS on
1995 if ldap_dryrun_mode:
1998 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1999 if setup_ds_path is None:
2000 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\"!")
2001 if not os.path.exists(setup_ds_path):
2002 message (setup_ds_path)
2003 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2005 # Run the Fedora DS setup utility
2006 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2008 raise ProvisioningError("setup-ds failed")
2011 retcode = subprocess.call([
2012 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2013 close_fds=True, shell=False)
2015 raise("ldib2db failed")
2017 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2018 """Create a PHP LDAP admin configuration file.
2020 :param path: Path to write the configuration to.
2021 :param setup_path: Function to generate setup paths.
2023 setup_file(setup_path("phpldapadmin-config.php"), path,
2024 {"S4_LDAPI_URI": ldapi_uri})
2027 def create_zone_file(path, setup_path, dnsdomain,
2028 hostip, hostip6, hostname, realm, domainguid,
2030 """Write out a DNS zone file, from the info in the current database.
2032 :param path: Path of the new zone file.
2033 :param setup_path: Setup path function.
2034 :param dnsdomain: DNS Domain name
2035 :param domaindn: DN of the Domain
2036 :param hostip: Local IPv4 IP
2037 :param hostip6: Local IPv6 IP
2038 :param hostname: Local hostname
2039 :param realm: Realm name
2040 :param domainguid: GUID of the domain.
2041 :param ntdsguid: GUID of the hosts nTDSDSA record.
2043 assert isinstance(domainguid, str)
2045 if hostip6 is not None:
2046 hostip6_base_line = " IN AAAA " + hostip6
2047 hostip6_host_line = hostname + " IN AAAA " + hostip6
2049 hostip6_base_line = ""
2050 hostip6_host_line = ""
2052 if hostip is not None:
2053 hostip_base_line = " IN A " + hostip
2054 hostip_host_line = hostname + " IN A " + hostip
2056 hostip_base_line = ""
2057 hostip_host_line = ""
2059 setup_file(setup_path("provision.zone"), path, {
2060 "HOSTNAME": hostname,
2061 "DNSDOMAIN": dnsdomain,
2063 "HOSTIP_BASE_LINE": hostip_base_line,
2064 "HOSTIP_HOST_LINE": hostip_host_line,
2065 "DOMAINGUID": domainguid,
2066 "DATESTRING": time.strftime("%Y%m%d%H"),
2067 "DEFAULTSITE": DEFAULTSITE,
2068 "NTDSGUID": ntdsguid,
2069 "HOSTIP6_BASE_LINE": hostip6_base_line,
2070 "HOSTIP6_HOST_LINE": hostip6_host_line,
2074 def create_named_conf(path, setup_path, realm, dnsdomain,
2076 """Write out a file containing zone statements suitable for inclusion in a
2077 named.conf file (including GSS-TSIG configuration).
2079 :param path: Path of the new named.conf file.
2080 :param setup_path: Setup path function.
2081 :param realm: Realm name
2082 :param dnsdomain: DNS Domain name
2083 :param private_dir: Path to private directory
2084 :param keytab_name: File name of DNS keytab file
2087 setup_file(setup_path("named.conf"), path, {
2088 "DNSDOMAIN": dnsdomain,
2090 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2091 "PRIVATE_DIR": private_dir
2094 def create_named_txt(path, setup_path, realm, dnsdomain,
2095 private_dir, keytab_name):
2096 """Write out a file containing zone statements suitable for inclusion in a
2097 named.conf file (including GSS-TSIG configuration).
2099 :param path: Path of the new named.conf file.
2100 :param setup_path: Setup path function.
2101 :param realm: Realm name
2102 :param dnsdomain: DNS Domain name
2103 :param private_dir: Path to private directory
2104 :param keytab_name: File name of DNS keytab file
2107 setup_file(setup_path("named.txt"), path, {
2108 "DNSDOMAIN": dnsdomain,
2110 "DNS_KEYTAB": keytab_name,
2111 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2112 "PRIVATE_DIR": private_dir
2115 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2116 """Write out a file containing zone statements suitable for inclusion in a
2117 named.conf file (including GSS-TSIG configuration).
2119 :param path: Path of the new named.conf file.
2120 :param setup_path: Setup path function.
2121 :param dnsdomain: DNS Domain name
2122 :param hostname: Local hostname
2123 :param realm: Realm name
2126 setup_file(setup_path("krb5.conf"), path, {
2127 "DNSDOMAIN": dnsdomain,
2128 "HOSTNAME": hostname,