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.fedoradsdna = None
148 self.fedoradspam = None
149 self.fedoradsrefint = None
150 self.fedoradslinkedattributes = None
151 self.fedoradsindex = None
152 self.fedoradssamba = None
154 self.olmmrserveridsconf = None
155 self.olmmrsyncreplconf = None
158 self.olcseedldif = None
161 class ProvisionNames(object):
168 self.ldapmanagerdn = None
169 self.dnsdomain = None
171 self.netbiosname = None
178 class ProvisionResult(object):
185 class Schema(object):
186 def __init__(self, setup_path, domain_sid, schemadn=None,
187 serverdn=None, sambadn=None, ldap_backend_type=None):
188 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
190 :param samdb: Load a schema into a SamDB.
191 :param setup_path: Setup path function.
192 :param schemadn: DN of the schema
193 :param serverdn: DN of the server
195 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
199 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
200 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
201 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
202 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
203 check_all_substituted(self.schema_data)
205 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
206 {"SCHEMADN": schemadn,
207 "SERVERDN": serverdn,
210 descr = get_schema_descriptor(domain_sid)
211 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
212 {"SCHEMADN": schemadn,
216 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
217 prefixmap = b64encode(prefixmap)
221 # We don't actually add this ldif, just parse it
222 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
223 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
226 # Return a hash with the forward attribute as a key and the back as the value
227 def get_linked_attributes(schemadn,schemaldb):
228 attrs = ["linkID", "lDAPDisplayName"]
229 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)
231 for i in range (0, len(res)):
232 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
233 target = schemaldb.searchone(basedn=schemadn,
234 expression=expression,
235 attribute="lDAPDisplayName",
237 if target is not None:
238 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
242 def get_dnsyntax_attributes(schemadn,schemaldb):
243 attrs = ["linkID", "lDAPDisplayName"]
244 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
246 for i in range (0, len(res)):
247 attributes.append(str(res[i]["lDAPDisplayName"]))
252 def check_install(lp, session_info, credentials):
253 """Check whether the current install seems ok.
255 :param lp: Loadparm context
256 :param session_info: Session information
257 :param credentials: Credentials
259 if lp.get("realm") == "":
260 raise Exception("Realm empty")
261 ldb = Ldb(lp.get("sam database"), session_info=session_info,
262 credentials=credentials, lp=lp)
263 if len(ldb.search("(cn=Administrator)")) != 1:
264 raise ProvisioningError("No administrator account found")
267 def findnss(nssfn, names):
268 """Find a user or group from a list of possibilities.
270 :param nssfn: NSS Function to try (should raise KeyError if not found)
271 :param names: Names to check.
272 :return: Value return by first names list.
279 raise KeyError("Unable to find user/group %r" % names)
282 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
283 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
286 def read_and_sub_file(file, subst_vars):
287 """Read a file and sub in variables found in it
289 :param file: File to be read (typically from setup directory)
290 param subst_vars: Optional variables to subsitute in the file.
292 data = open(file, 'r').read()
293 if subst_vars is not None:
294 data = substitute_var(data, subst_vars)
295 check_all_substituted(data)
299 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
300 """Setup a ldb in the private dir.
302 :param ldb: LDB file to import data into
303 :param ldif_path: Path of the LDIF file to load
304 :param subst_vars: Optional variables to subsitute in LDIF.
305 :param nocontrols: Optional list of controls, can be None for no controls
307 assert isinstance(ldif_path, str)
308 data = read_and_sub_file(ldif_path, subst_vars)
309 ldb.add_ldif(data,controls)
312 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
313 """Modify a ldb in the private dir.
315 :param ldb: LDB object.
316 :param ldif_path: LDIF file path.
317 :param subst_vars: Optional dictionary with substitution variables.
319 data = read_and_sub_file(ldif_path, subst_vars)
321 ldb.modify_ldif(data)
324 def setup_ldb(ldb, ldif_path, subst_vars):
325 """Import a LDIF a file into a LDB handle, optionally substituting variables.
327 :note: Either all LDIF data will be added or none (using transactions).
329 :param ldb: LDB file to import into.
330 :param ldif_path: Path to the LDIF file.
331 :param subst_vars: Dictionary with substitution variables.
333 assert ldb is not None
334 ldb.transaction_start()
336 setup_add_ldif(ldb, ldif_path, subst_vars)
338 ldb.transaction_cancel()
340 ldb.transaction_commit()
343 def setup_file(template, fname, subst_vars=None):
344 """Setup a file in the private dir.
346 :param template: Path of the template file.
347 :param fname: Path of the file to create.
348 :param subst_vars: Substitution variables.
352 if os.path.exists(f):
355 data = read_and_sub_file(template, subst_vars)
356 open(f, 'w').write(data)
359 def provision_paths_from_lp(lp, dnsdomain):
360 """Set the default paths for provisioning.
362 :param lp: Loadparm context.
363 :param dnsdomain: DNS Domain name
365 paths = ProvisionPaths()
366 paths.private_dir = lp.get("private dir")
367 paths.dns_keytab = "dns.keytab"
369 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
370 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
371 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
372 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
373 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
374 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
375 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
376 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
377 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
378 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
379 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
380 paths.phpldapadminconfig = os.path.join(paths.private_dir,
381 "phpldapadmin-config.php")
382 paths.ldapdir = os.path.join(paths.private_dir,
384 paths.slapdconf = os.path.join(paths.ldapdir,
386 paths.slapdpid = os.path.join(paths.ldapdir,
388 paths.modulesconf = os.path.join(paths.ldapdir,
390 paths.memberofconf = os.path.join(paths.ldapdir,
392 paths.fedoradsinf = os.path.join(paths.ldapdir,
394 paths.fedoradspartitions = os.path.join(paths.ldapdir,
395 "fedorads-partitions.ldif")
396 paths.fedoradssasl = os.path.join(paths.ldapdir,
397 "fedorads-sasl.ldif")
398 paths.fedoradsdna = os.path.join(paths.ldapdir,
400 paths.fedoradspam = os.path.join(paths.ldapdir,
402 paths.fedoradsrefint = os.path.join(paths.ldapdir,
403 "fedorads-refint.ldif")
404 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
405 "fedorads-linked-attributes.ldif")
406 paths.fedoradsindex = os.path.join(paths.ldapdir,
407 "fedorads-index.ldif")
408 paths.fedoradssamba = os.path.join(paths.ldapdir,
409 "fedorads-samba.ldif")
410 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
411 "mmr_serverids.conf")
412 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
414 paths.olcdir = os.path.join(paths.ldapdir,
416 paths.olcseedldif = os.path.join(paths.ldapdir,
418 paths.hklm = "hklm.ldb"
419 paths.hkcr = "hkcr.ldb"
420 paths.hkcu = "hkcu.ldb"
421 paths.hku = "hku.ldb"
422 paths.hkpd = "hkpd.ldb"
423 paths.hkpt = "hkpt.ldb"
425 paths.sysvol = lp.get("path", "sysvol")
427 paths.netlogon = lp.get("path", "netlogon")
429 paths.smbconf = lp.configfile
434 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
435 serverrole=None, rootdn=None, domaindn=None, configdn=None,
436 schemadn=None, serverdn=None, sitename=None, sambadn=None):
437 """Guess configuration settings to use."""
440 hostname = socket.gethostname().split(".")[0]
442 netbiosname = lp.get("netbios name")
443 if netbiosname is None:
444 netbiosname = hostname
445 assert netbiosname is not None
446 netbiosname = netbiosname.upper()
447 if not valid_netbios_name(netbiosname):
448 raise InvalidNetbiosName(netbiosname)
450 if dnsdomain is None:
451 dnsdomain = lp.get("realm")
452 assert dnsdomain is not None
453 dnsdomain = dnsdomain.lower()
455 if serverrole is None:
456 serverrole = lp.get("server role")
457 assert serverrole is not None
458 serverrole = serverrole.lower()
460 realm = dnsdomain.upper()
462 if lp.get("realm").upper() != realm:
463 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
465 if serverrole == "domain controller":
467 domain = lp.get("workgroup")
468 assert domain is not None
469 domain = domain.upper()
471 if lp.get("workgroup").upper() != domain:
472 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
475 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
479 domaindn = "DC=" + netbiosname
481 if not valid_netbios_name(domain):
482 raise InvalidNetbiosName(domain)
484 if hostname.upper() == realm:
485 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
486 if netbiosname == realm:
487 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
489 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
495 configdn = "CN=Configuration," + rootdn
497 schemadn = "CN=Schema," + configdn
504 names = ProvisionNames()
505 names.rootdn = rootdn
506 names.domaindn = domaindn
507 names.configdn = configdn
508 names.schemadn = schemadn
509 names.sambadn = sambadn
510 names.ldapmanagerdn = "CN=Manager," + rootdn
511 names.dnsdomain = dnsdomain
512 names.domain = domain
514 names.netbiosname = netbiosname
515 names.hostname = hostname
516 names.sitename = sitename
517 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
522 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
523 targetdir, sid_generator):
524 """Create a new smb.conf file based on a couple of basic settings.
526 assert smbconf is not None
528 hostname = socket.gethostname().split(".")[0]
529 netbiosname = hostname.upper()
531 if serverrole is None:
532 serverrole = "standalone"
534 assert serverrole in ("domain controller", "member server", "standalone")
535 if serverrole == "domain controller":
537 elif serverrole == "member server":
538 smbconfsuffix = "member"
539 elif serverrole == "standalone":
540 smbconfsuffix = "standalone"
542 if sid_generator is None:
543 sid_generator = "internal"
545 assert domain is not None
546 domain = domain.upper()
548 assert realm is not None
549 realm = realm.upper()
551 default_lp = param.LoadParm()
552 #Load non-existant file
553 if os.path.exists(smbconf):
554 default_lp.load(smbconf)
556 if targetdir is not None:
557 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
558 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
560 default_lp.set("lock dir", os.path.abspath(targetdir))
565 if sid_generator == "internal":
566 sid_generator_line = ""
568 sid_generator_line = "sid generator = " + sid_generator
570 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
571 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
573 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
575 "NETBIOS_NAME": netbiosname,
578 "SERVERROLE": serverrole,
579 "NETLOGONPATH": netlogon,
580 "SYSVOLPATH": sysvol,
581 "SIDGENERATOR_LINE": sid_generator_line,
582 "PRIVATEDIR_LINE": privatedir_line,
583 "LOCKDIR_LINE": lockdir_line
587 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
588 users_gid, wheel_gid):
589 """setup reasonable name mappings for sam names to unix names.
591 :param samdb: SamDB object.
592 :param idmap: IDmap db object.
593 :param sid: The domain sid.
594 :param domaindn: The domain DN.
595 :param root_uid: uid of the UNIX root user.
596 :param nobody_uid: uid of the UNIX nobody user.
597 :param users_gid: gid of the UNIX users group.
598 :param wheel_gid: gid of the UNIX wheel group."""
600 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
601 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
603 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
604 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
606 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
607 credentials, names, schema,
608 serverrole, ldap_backend=None,
610 """Setup the partitions for the SAM database.
612 Alternatively, provision() may call this, and then populate the database.
614 :note: This will wipe the Sam Database!
616 :note: This function always removes the local SAM LDB file. The erase
617 parameter controls whether to erase the existing data, which
618 may not be stored locally but in LDAP.
621 assert session_info is not None
623 old_partitions = None
624 new_partitions = None
626 # We use options=["modules:"] to stop the modules loading - we
627 # just want to wipe and re-initialise the database, not start it up
630 samdb = Ldb(url=samdb_path, session_info=session_info,
631 credentials=credentials, lp=lp, options=["modules:"])
632 res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*")
635 old_partitions = res[0]["partition"]
639 if old_partitions is not None:
641 for old_partition in old_partitions:
642 new_partition = old_partition
643 if old_partition.endswith(".ldb"):
644 p = old_partition.split(":")[0]
645 dn = ldb.Dn(schema.ldb, p)
646 new_partition = dn.get_casefold()
647 new_partitions.append(new_partition)
650 samdb.erase_except_schema_controlled()
652 os.unlink(samdb_path)
653 samdb = Ldb(url=samdb_path, session_info=session_info,
654 credentials=credentials, lp=lp, options=["modules:"])
656 samdb.erase_except_schema_controlled()
658 #Add modules to the list to activate them by default
659 #beware often order is important
661 # Some Known ordering constraints:
662 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
663 # - objectclass must be before password_hash, because password_hash checks
664 # that the objectclass is of type person (filled in by objectclass
665 # module when expanding the objectclass list)
666 # - partition must be last
667 # - each partition has its own module list then
668 modules_list = ["resolve_oids",
691 "extended_dn_out_ldb"]
692 modules_list2 = ["show_deleted",
696 ldap_backend_line = "# No LDAP backend"
697 if ldap_backend is not None:
698 ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri
700 if ldap_backend.ldap_backend_type == "fedora-ds":
701 backend_modules = ["nsuniqueid", "paged_searches"]
702 # We can handle linked attributes here, as we don't have directory-side subtree operations
703 tdb_modules_list = ["extended_dn_out_fds"]
704 elif ldap_backend.ldap_backend_type == "openldap":
705 backend_modules = ["entryuuid", "paged_searches"]
706 # OpenLDAP handles subtree renames, so we don't want to do any of these things
707 tdb_modules_list = ["extended_dn_out_openldap"]
709 elif serverrole == "domain controller":
710 tdb_modules_list.insert(0, "repl_meta_data")
713 backend_modules = ["objectguid"]
715 if tdb_modules_list is None:
716 tdb_modules_list_as_string = ""
718 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
720 samdb.transaction_start()
722 message("Setting up sam.ldb partitions and settings")
723 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
724 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
725 "SCHEMADN_MOD2": ",objectguid",
726 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
727 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
728 "SCHEMADN_MOD": "schema_data",
729 "CONFIGDN_MOD": "naming_fsmo",
730 "DOMAINDN_MOD": "pdc_fsmo",
731 "MODULES_LIST": ",".join(modules_list),
732 "TDB_MODULES_LIST": tdb_modules_list_as_string,
733 "MODULES_LIST2": ",".join(modules_list2),
734 "BACKEND_MOD": ",".join(backend_modules),
735 "LDAP_BACKEND_LINE": ldap_backend_line,
739 if new_partitions is not None:
741 m.dn = ldb.Dn(samdb, "@PARTITION")
743 m["partition"] = ldb.MessageElement(new_partitions, ldb.FLAG_MOD_ADD, "partition")
746 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
748 message("Setting up sam.ldb rootDSE")
749 setup_samdb_rootdse(samdb, setup_path, names)
752 samdb.transaction_cancel()
755 samdb.transaction_commit()
758 def secretsdb_self_join(secretsdb, domain,
759 netbiosname, domainsid, machinepass,
760 realm=None, dnsdomain=None,
762 key_version_number=1,
763 secure_channel_type=SEC_CHAN_WKSTA):
764 """Add domain join-specific bits to a secrets database.
766 :param secretsdb: Ldb Handle to the secrets database
767 :param machinepass: Machine password
769 attrs=["whenChanged",
777 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
778 msg["secureChannelType"] = str(secure_channel_type)
779 msg["flatname"] = [domain]
780 msg["objectClass"] = ["top", "primaryDomain"]
781 if realm is not None:
782 if dnsdomain is None:
783 dnsdomain = realm.lower()
784 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
786 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
787 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
788 msg["privateKeytab"] = ["secrets.keytab"];
791 msg["secret"] = [machinepass]
792 msg["samAccountName"] = ["%s$" % netbiosname]
793 msg["secureChannelType"] = [str(secure_channel_type)]
794 msg["objectSid"] = [ndr_pack(domainsid)]
796 res = secretsdb.search(base="cn=Primary Domains",
798 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
799 scope=SCOPE_ONELEVEL)
802 if del_msg.dn is not msg.dn:
803 secretsdb.delete(del_msg.dn)
805 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
808 msg["priorSecret"] = res[0]["secret"]
809 msg["priorWhenChanged"] = res[0]["whenChanged"]
811 if res["privateKeytab"] is not None:
812 msg["privateKeytab"] = res[0]["privateKeytab"]
814 if res["krb5Keytab"] is not None:
815 msg["krb5Keytab"] = res[0]["krb5Keytab"]
818 el.set_flags(ldb.FLAG_MOD_REPLACE)
819 secretsdb.modify(msg)
824 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
825 dns_keytab_path, dnspass):
826 """Add DNS specific bits to a secrets database.
828 :param secretsdb: Ldb Handle to the secrets database
829 :param setup_path: Setup path function
830 :param machinepass: Machine password
832 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
834 "DNSDOMAIN": dnsdomain,
835 "DNS_KEYTAB": dns_keytab_path,
836 "DNSPASS_B64": b64encode(dnspass),
840 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
841 """Setup the secrets database.
843 :param path: Path to the secrets database.
844 :param setup_path: Get the path to a setup file.
845 :param session_info: Session info.
846 :param credentials: Credentials
847 :param lp: Loadparm context
848 :return: LDB handle for the created secrets database
850 if os.path.exists(path):
852 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
855 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
856 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
858 secrets_ldb.transaction_start()
859 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
861 if credentials is not None and credentials.authentication_requested():
862 if credentials.get_bind_dn() is not None:
863 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
864 "LDAPMANAGERDN": credentials.get_bind_dn(),
865 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
868 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
869 "LDAPADMINUSER": credentials.get_username(),
870 "LDAPADMINREALM": credentials.get_realm(),
871 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
876 def setup_privileges(path, setup_path, session_info, lp):
877 """Setup the privileges database.
879 :param path: Path to the privileges database.
880 :param setup_path: Get the path to a setup file.
881 :param session_info: Session info.
882 :param credentials: Credentials
883 :param lp: Loadparm context
884 :return: LDB handle for the created secrets database
886 if os.path.exists(path):
888 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
889 privilege_ldb.erase()
890 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
893 def setup_registry(path, setup_path, session_info, lp):
894 """Setup the registry.
896 :param path: Path to the registry database
897 :param setup_path: Function that returns the path to a setup.
898 :param session_info: Session information
899 :param credentials: Credentials
900 :param lp: Loadparm context
902 reg = registry.Registry()
903 hive = registry.open_ldb(path, session_info=session_info,
905 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
906 provision_reg = setup_path("provision.reg")
907 assert os.path.exists(provision_reg)
908 reg.diff_apply(provision_reg)
911 def setup_idmapdb(path, setup_path, session_info, lp):
912 """Setup the idmap database.
914 :param path: path to the idmap database
915 :param setup_path: Function that returns a path to a setup file
916 :param session_info: Session information
917 :param credentials: Credentials
918 :param lp: Loadparm context
920 if os.path.exists(path):
923 idmap_ldb = IDmapDB(path, session_info=session_info,
927 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
931 def setup_samdb_rootdse(samdb, setup_path, names):
932 """Setup the SamDB rootdse.
934 :param samdb: Sam Database handle
935 :param setup_path: Obtain setup path
937 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
938 "SCHEMADN": names.schemadn,
939 "NETBIOSNAME": names.netbiosname,
940 "DNSDOMAIN": names.dnsdomain,
941 "REALM": names.realm,
942 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
943 "DOMAINDN": names.domaindn,
944 "ROOTDN": names.rootdn,
945 "CONFIGDN": names.configdn,
946 "SERVERDN": names.serverdn,
950 def setup_self_join(samdb, names,
951 machinepass, dnspass,
952 domainsid, invocationid, setup_path,
953 policyguid, policyguid_dc, domainControllerFunctionality,
955 """Join a host to its own domain."""
956 assert isinstance(invocationid, str)
957 if ntdsguid is not None:
958 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
961 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
962 "CONFIGDN": names.configdn,
963 "SCHEMADN": names.schemadn,
964 "DOMAINDN": names.domaindn,
965 "SERVERDN": names.serverdn,
966 "INVOCATIONID": invocationid,
967 "NETBIOSNAME": names.netbiosname,
968 "DEFAULTSITE": names.sitename,
969 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
970 "MACHINEPASS_B64": b64encode(machinepass),
971 "DNSPASS_B64": b64encode(dnspass),
972 "REALM": names.realm,
973 "DOMAIN": names.domain,
974 "DNSDOMAIN": names.dnsdomain,
975 "SAMBA_VERSION_STRING": version,
976 "NTDSGUID": ntdsguid_line,
977 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
979 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
980 "POLICYGUID": policyguid,
981 "POLICYGUID_DC": policyguid_dc,
982 "DNSDOMAIN": names.dnsdomain,
983 "DOMAINSID": str(domainsid),
984 "DOMAINDN": names.domaindn})
986 # add the NTDSGUID based SPNs
987 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
988 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
989 expression="", scope=SCOPE_BASE)
990 assert isinstance(names.ntdsguid, str)
992 # Setup fSMORoleOwner entries to point at the newly created DC entry
993 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
994 "DOMAIN": names.domain,
995 "DNSDOMAIN": names.dnsdomain,
996 "DOMAINDN": names.domaindn,
997 "CONFIGDN": names.configdn,
998 "SCHEMADN": names.schemadn,
999 "DEFAULTSITE": names.sitename,
1000 "SERVERDN": names.serverdn,
1001 "NETBIOSNAME": names.netbiosname,
1002 "NTDSGUID": names.ntdsguid
1006 def setup_samdb(path, setup_path, session_info, credentials, lp,
1008 domainsid, domainguid, policyguid, policyguid_dc,
1009 fill, adminpass, krbtgtpass,
1010 machinepass, invocationid, dnspass, ntdsguid,
1011 serverrole, dom_for_fun_level=None,
1012 schema=None, ldap_backend=None):
1013 """Setup a complete SAM Database.
1015 :note: This will wipe the main SAM database file!
1018 # ATTENTION: Do NOT change these default values without discussion with the
1019 # team and/or release manager. They have a big impact on the whole program!
1020 domainControllerFunctionality = DS_DC_FUNCTION_2008
1022 if dom_for_fun_level is None:
1023 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1024 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
1025 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
1027 if dom_for_fun_level > domainControllerFunctionality:
1028 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!")
1030 domainFunctionality = dom_for_fun_level
1031 forestFunctionality = dom_for_fun_level
1033 # Also wipes the database
1034 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
1035 credentials=credentials, session_info=session_info,
1036 names=names, ldap_backend=ldap_backend,
1037 serverrole=serverrole, schema=schema)
1039 if (schema == None):
1040 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1041 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
1043 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1044 samdb = Ldb(session_info=session_info,
1045 credentials=credentials, lp=lp)
1047 message("Pre-loading the Samba 4 and AD schema")
1049 # Load the schema from the one we computed earlier
1050 samdb.set_schema_from_ldb(schema.ldb)
1052 # And now we can connect to the DB - the schema won't be loaded from the DB
1055 if fill == FILL_DRS:
1058 samdb.transaction_start()
1060 message("Erasing data from partitions")
1061 # Load the schema (again). This time it will force a reindex,
1062 # and will therefore make the erase_partitions() below
1063 # computationally sane
1064 samdb.set_schema_from_ldb(schema.ldb)
1065 samdb.erase_partitions()
1067 # Set the domain functionality levels onto the database.
1068 # Various module (the password_hash module in particular) need
1069 # to know what level of AD we are emulating.
1071 # These will be fixed into the database via the database
1072 # modifictions below, but we need them set from the start.
1073 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1074 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1075 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1077 samdb.set_domain_sid(str(domainsid))
1078 if serverrole == "domain controller":
1079 samdb.set_invocation_id(invocationid)
1081 message("Adding DomainDN: %s" % names.domaindn)
1083 #impersonate domain admin
1084 admin_session_info = admin_session(lp, str(domainsid))
1085 samdb.set_session_info(admin_session_info)
1086 if domainguid is not None:
1087 domainguid_line = "objectGUID: %s\n-" % domainguid
1089 domainguid_line = ""
1090 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1091 "DOMAINDN": names.domaindn,
1092 "DOMAINGUID": domainguid_line
1096 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1097 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1098 "DOMAINSID": str(domainsid),
1099 "SCHEMADN": names.schemadn,
1100 "NETBIOSNAME": names.netbiosname,
1101 "DEFAULTSITE": names.sitename,
1102 "CONFIGDN": names.configdn,
1103 "SERVERDN": names.serverdn,
1104 "POLICYGUID": policyguid,
1105 "DOMAINDN": names.domaindn,
1106 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1107 "SAMBA_VERSION_STRING": version
1110 message("Adding configuration container")
1111 descr = get_config_descriptor(domainsid);
1112 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1113 "CONFIGDN": names.configdn,
1114 "DESCRIPTOR": descr,
1116 message("Modifying configuration container")
1117 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1118 "CONFIGDN": names.configdn,
1119 "SCHEMADN": names.schemadn,
1122 # The LDIF here was created when the Schema object was constructed
1123 message("Setting up sam.ldb schema")
1124 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1125 samdb.modify_ldif(schema.schema_dn_modify)
1126 samdb.write_prefixes_from_schema()
1127 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1128 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1129 {"SCHEMADN": names.schemadn})
1131 message("Setting up sam.ldb configuration data")
1132 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1133 "CONFIGDN": names.configdn,
1134 "NETBIOSNAME": names.netbiosname,
1135 "DEFAULTSITE": names.sitename,
1136 "DNSDOMAIN": names.dnsdomain,
1137 "DOMAIN": names.domain,
1138 "SCHEMADN": names.schemadn,
1139 "DOMAINDN": names.domaindn,
1140 "SERVERDN": names.serverdn,
1141 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1144 message("Setting up display specifiers")
1145 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1146 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1147 check_all_substituted(display_specifiers_ldif)
1148 samdb.add_ldif(display_specifiers_ldif)
1150 message("Adding users container")
1151 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1152 "DOMAINDN": names.domaindn})
1153 message("Modifying users container")
1154 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1155 "DOMAINDN": names.domaindn})
1156 message("Adding computers container")
1157 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1158 "DOMAINDN": names.domaindn})
1159 message("Modifying computers container")
1160 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1161 "DOMAINDN": names.domaindn})
1162 message("Setting up sam.ldb data")
1163 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1164 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1165 "DOMAINDN": names.domaindn,
1166 "NETBIOSNAME": names.netbiosname,
1167 "DEFAULTSITE": names.sitename,
1168 "CONFIGDN": names.configdn,
1169 "SERVERDN": names.serverdn,
1170 "POLICYGUID_DC": policyguid_dc
1173 if fill == FILL_FULL:
1174 message("Setting up sam.ldb users and groups")
1175 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1176 "DOMAINDN": names.domaindn,
1177 "DOMAINSID": str(domainsid),
1178 "CONFIGDN": names.configdn,
1179 "ADMINPASS_B64": b64encode(adminpass),
1180 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1183 if serverrole == "domain controller":
1184 message("Setting up self join")
1185 setup_self_join(samdb, names=names, invocationid=invocationid,
1187 machinepass=machinepass,
1188 domainsid=domainsid, policyguid=policyguid,
1189 policyguid_dc=policyguid_dc,
1190 setup_path=setup_path,
1191 domainControllerFunctionality=domainControllerFunctionality,
1194 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1195 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1196 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1197 assert isinstance(names.ntdsguid, str)
1200 samdb.transaction_cancel()
1203 samdb.transaction_commit()
1208 FILL_NT4SYNC = "NT4SYNC"
1212 def provision(setup_dir, message, session_info,
1213 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1215 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1217 domain=None, hostname=None, hostip=None, hostip6=None,
1218 domainsid=None, adminpass=None, ldapadminpass=None,
1219 krbtgtpass=None, domainguid=None,
1220 policyguid=None, policyguid_dc=None, invocationid=None,
1221 machinepass=None, ntdsguid=None,
1222 dnspass=None, root=None, nobody=None, users=None,
1223 wheel=None, backup=None, aci=None, serverrole=None,
1224 dom_for_fun_level=None,
1225 ldap_backend_extra_port=None, ldap_backend_type=None,
1227 ol_mmr_urls=None, ol_olc=None,
1228 setup_ds_path=None, slapd_path=None, nosync=False,
1229 ldap_dryrun_mode=False):
1232 :note: caution, this wipes all existing data!
1235 def setup_path(file):
1236 return os.path.join(setup_dir, file)
1238 if domainsid is None:
1239 domainsid = security.random_sid()
1241 domainsid = security.dom_sid(domainsid)
1243 # create/adapt the group policy GUIDs
1244 if policyguid is None:
1245 policyguid = str(uuid.uuid4())
1246 policyguid = policyguid.upper()
1247 if policyguid_dc is None:
1248 policyguid_dc = str(uuid.uuid4())
1249 policyguid_dc = policyguid_dc.upper()
1251 if adminpass is None:
1252 adminpass = glue.generate_random_str(12)
1253 if krbtgtpass is None:
1254 krbtgtpass = glue.generate_random_str(12)
1255 if machinepass is None:
1256 machinepass = glue.generate_random_str(12)
1258 dnspass = glue.generate_random_str(12)
1259 if ldapadminpass is None:
1260 #Make a new, random password between Samba and it's LDAP server
1261 ldapadminpass=glue.generate_random_str(12)
1263 sid_generator = "internal"
1264 if ldap_backend_type == "fedora-ds":
1265 sid_generator = "backend"
1267 root_uid = findnss_uid([root or "root"])
1268 nobody_uid = findnss_uid([nobody or "nobody"])
1269 users_gid = findnss_gid([users or "users"])
1271 wheel_gid = findnss_gid(["wheel", "adm"])
1273 wheel_gid = findnss_gid([wheel])
1275 if targetdir is not None:
1276 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1277 os.makedirs(os.path.join(targetdir, "etc"))
1278 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1279 elif smbconf is None:
1280 smbconf = param.default_path()
1282 # only install a new smb.conf if there isn't one there already
1283 if not os.path.exists(smbconf):
1284 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1285 targetdir, sid_generator)
1287 lp = param.LoadParm()
1290 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1291 dnsdomain=realm, serverrole=serverrole,
1292 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1293 serverdn=serverdn, sitename=sitename)
1295 paths = provision_paths_from_lp(lp, names.dnsdomain)
1299 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1300 except socket.gaierror, (socket.EAI_NODATA, msg):
1305 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1306 except socket.gaierror, (socket.EAI_NODATA, msg):
1309 if serverrole is None:
1310 serverrole = lp.get("server role")
1312 assert serverrole in ("domain controller", "member server", "standalone")
1313 if invocationid is None and serverrole == "domain controller":
1314 invocationid = str(uuid.uuid4())
1316 if not os.path.exists(paths.private_dir):
1317 os.mkdir(paths.private_dir)
1319 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1321 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1322 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1324 secrets_credentials = credentials
1325 provision_backend = None
1326 if ldap_backend_type:
1327 # We only support an LDAP backend over ldapi://
1329 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1330 lp=lp, credentials=credentials,
1332 message=message, hostname=hostname,
1333 root=root, schema=schema,
1334 ldap_backend_type=ldap_backend_type,
1335 ldapadminpass=ldapadminpass,
1336 ldap_backend_extra_port=ldap_backend_extra_port,
1337 ol_mmr_urls=ol_mmr_urls,
1338 slapd_path=slapd_path,
1339 setup_ds_path=setup_ds_path,
1340 ldap_dryrun_mode=ldap_dryrun_mode,
1341 domainsid=domainsid)
1343 # Now use the backend credentials to access the databases
1344 credentials = provision_backend.credentials
1345 secrets_credentials = provision_backend.adminCredentials
1346 ldapi_url = provision_backend.ldapi_uri
1348 # only install a new shares config db if there is none
1349 if not os.path.exists(paths.shareconf):
1350 message("Setting up share.ldb")
1351 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1352 credentials=credentials, lp=lp)
1353 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1356 message("Setting up secrets.ldb")
1357 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1358 session_info=session_info,
1359 credentials=secrets_credentials, lp=lp)
1361 message("Setting up the registry")
1362 setup_registry(paths.hklm, setup_path, session_info,
1365 message("Setting up the privileges database")
1366 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1368 message("Setting up idmap db")
1369 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1372 message("Setting up SAM db")
1373 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1374 credentials=credentials, lp=lp, names=names,
1376 domainsid=domainsid,
1377 schema=schema, domainguid=domainguid,
1378 policyguid=policyguid, policyguid_dc=policyguid_dc,
1380 adminpass=adminpass, krbtgtpass=krbtgtpass,
1381 invocationid=invocationid,
1382 machinepass=machinepass, dnspass=dnspass,
1383 ntdsguid=ntdsguid, serverrole=serverrole,
1384 dom_for_fun_level=dom_for_fun_level,
1385 ldap_backend=provision_backend)
1387 if serverrole == "domain controller":
1388 if paths.netlogon is None:
1389 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1390 message("Please either remove %s or see the template at %s" %
1391 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1392 assert(paths.netlogon is not None)
1394 if paths.sysvol is None:
1395 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1396 message("Please either remove %s or see the template at %s" %
1397 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1398 assert(paths.sysvol is not None)
1400 # Set up group policies (domain policy and domain controller policy)
1402 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1403 "{" + policyguid + "}")
1404 os.makedirs(policy_path, 0755)
1405 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1406 "[General]\r\nVersion=65543")
1407 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1408 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1410 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1411 "{" + policyguid_dc + "}")
1412 os.makedirs(policy_path_dc, 0755)
1413 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1414 "[General]\r\nVersion=2")
1415 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1416 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1418 if not os.path.isdir(paths.netlogon):
1419 os.makedirs(paths.netlogon, 0755)
1421 if samdb_fill == FILL_FULL:
1422 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1423 root_uid=root_uid, nobody_uid=nobody_uid,
1424 users_gid=users_gid, wheel_gid=wheel_gid)
1426 message("Setting up sam.ldb rootDSE marking as synchronized")
1427 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1429 # Only make a zone file on the first DC, it should be replicated with DNS replication
1430 if serverrole == "domain controller":
1431 secretsdb_self_join(secrets_ldb, domain=domain,
1433 dnsdomain=names.dnsdomain,
1434 netbiosname=names.netbiosname,
1435 domainsid=domainsid,
1436 machinepass=machinepass,
1437 secure_channel_type=SEC_CHAN_BDC)
1439 secretsdb_setup_dns(secrets_ldb, setup_path,
1440 realm=names.realm, dnsdomain=names.dnsdomain,
1441 dns_keytab_path=paths.dns_keytab,
1444 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1445 assert isinstance(domainguid, str)
1447 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1449 hostip6=hostip6, hostname=names.hostname,
1451 domainguid=domainguid, ntdsguid=names.ntdsguid)
1453 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1454 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1456 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1457 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1458 keytab_name=paths.dns_keytab)
1459 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1460 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1462 create_krb5_conf(paths.krb5conf, setup_path,
1463 dnsdomain=names.dnsdomain, hostname=names.hostname,
1465 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1467 #Now commit the secrets.ldb to disk
1468 secrets_ldb.transaction_commit()
1470 if provision_backend is not None:
1471 if ldap_backend_type == "fedora-ds":
1472 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1474 # delete default SASL mappings
1475 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1477 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1478 for i in range (0, len(res)):
1479 dn = str(res[i]["dn"])
1482 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1485 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1487 m.dn = ldb.Dn(1, names.domaindn)
1490 m.dn = ldb.Dn(1, names.configdn)
1493 m.dn = ldb.Dn(1, names.schemadn)
1496 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1497 if provision_backend.slapd.poll() is None:
1499 if hasattr(provision_backend.slapd, "terminate"):
1500 provision_backend.slapd.terminate()
1502 # Older python versions don't have .terminate()
1504 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1506 #and now wait for it to die
1507 provision_backend.slapd.communicate()
1509 # now display slapd_command_file.txt to show how slapd must be started next time
1510 message("Use later the following commandline to start slapd, then Samba:")
1511 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1512 message(slapd_command)
1513 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1515 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1516 "SLAPD_COMMAND" : slapd_command})
1519 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1522 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1524 message("Once the above files are installed, your Samba4 server will be ready to use")
1525 message("Server Role: %s" % serverrole)
1526 message("Hostname: %s" % names.hostname)
1527 message("NetBIOS Domain: %s" % names.domain)
1528 message("DNS Domain: %s" % names.dnsdomain)
1529 message("DOMAIN SID: %s" % str(domainsid))
1530 if samdb_fill == FILL_FULL:
1531 message("Admin password: %s" % adminpass)
1532 if provision_backend:
1533 if provision_backend.credentials.get_bind_dn() is not None:
1534 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1536 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1538 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1540 result = ProvisionResult()
1541 result.domaindn = domaindn
1542 result.paths = paths
1544 result.samdb = samdb
1549 def provision_become_dc(setup_dir=None,
1550 smbconf=None, targetdir=None, realm=None,
1551 rootdn=None, domaindn=None, schemadn=None,
1552 configdn=None, serverdn=None,
1553 domain=None, hostname=None, domainsid=None,
1554 adminpass=None, krbtgtpass=None, domainguid=None,
1555 policyguid=None, policyguid_dc=None, invocationid=None,
1557 dnspass=None, root=None, nobody=None, users=None,
1558 wheel=None, backup=None, serverrole=None,
1559 ldap_backend=None, ldap_backend_type=None,
1560 sitename=None, debuglevel=1):
1563 """print a message if quiet is not set."""
1566 glue.set_debug_level(debuglevel)
1568 return provision(setup_dir, message, system_session(), None,
1569 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1570 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1571 configdn=configdn, serverdn=serverdn, domain=domain,
1572 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1573 machinepass=machinepass, serverrole="domain controller",
1577 def setup_db_config(setup_path, dbdir):
1578 """Setup a Berkeley database.
1580 :param setup_path: Setup path function.
1581 :param dbdir: Database directory."""
1582 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1583 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1584 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1585 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1587 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1588 {"LDAPDBDIR": dbdir})
1590 class ProvisionBackend(object):
1591 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1592 names=None, message=None,
1593 hostname=None, root=None,
1594 schema=None, ldapadminpass=None,
1595 ldap_backend_type=None, ldap_backend_extra_port=None,
1597 setup_ds_path=None, slapd_path=None,
1598 nosync=False, ldap_dryrun_mode=False,
1600 """Provision an LDAP backend for samba4
1602 This works for OpenLDAP and Fedora DS
1605 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1607 if not os.path.isdir(paths.ldapdir):
1608 os.makedirs(paths.ldapdir, 0700)
1610 if ldap_backend_type == "existing":
1611 #Check to see that this 'existing' LDAP backend in fact exists
1612 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1613 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1614 expression="(objectClass=OpenLDAProotDSE)")
1616 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1617 # This caused them to be set into the long-term database later in the script.
1618 self.credentials = credentials
1619 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1622 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1623 # if another instance of slapd is already running
1625 ldapi_db = Ldb(self.ldapi_uri)
1626 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1627 expression="(objectClass=OpenLDAProotDSE)");
1629 f = open(paths.slapdpid, "r")
1632 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1636 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1641 # Try to print helpful messages when the user has not specified the path to slapd
1642 if slapd_path is None:
1643 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1644 if not os.path.exists(slapd_path):
1645 message (slapd_path)
1646 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1648 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1650 os.unlink(schemadb_path)
1655 # Put the LDIF of the schema into a database so we can search on
1656 # it to generate schema-dependent configurations in Fedora DS and
1658 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1659 schema.ldb.connect(schemadb_path)
1660 schema.ldb.transaction_start()
1662 # These bits of LDIF are supplied when the Schema object is created
1663 schema.ldb.add_ldif(schema.schema_dn_add)
1664 schema.ldb.modify_ldif(schema.schema_dn_modify)
1665 schema.ldb.add_ldif(schema.schema_data)
1666 schema.ldb.transaction_commit()
1668 self.credentials = Credentials()
1669 self.credentials.guess(lp)
1670 #Kerberos to an ldapi:// backend makes no sense
1671 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1673 self.adminCredentials = Credentials()
1674 self.adminCredentials.guess(lp)
1675 #Kerberos to an ldapi:// backend makes no sense
1676 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1678 self.ldap_backend_type = ldap_backend_type
1680 if ldap_backend_type == "fedora-ds":
1681 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1682 names=names, message=message,
1684 ldapadminpass=ldapadminpass, root=root,
1686 ldap_backend_extra_port=ldap_backend_extra_port,
1687 setup_ds_path=setup_ds_path,
1688 slapd_path=slapd_path,
1690 ldap_dryrun_mode=ldap_dryrun_mode,
1691 domainsid=domainsid)
1693 elif ldap_backend_type == "openldap":
1694 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1695 names=names, message=message,
1697 ldapadminpass=ldapadminpass, root=root,
1699 ldap_backend_extra_port=ldap_backend_extra_port,
1700 ol_mmr_urls=ol_mmr_urls,
1701 slapd_path=slapd_path,
1703 ldap_dryrun_mode=ldap_dryrun_mode)
1705 raise ProvisioningError("Unknown LDAP backend type selected")
1707 self.credentials.set_password(ldapadminpass)
1708 self.adminCredentials.set_username("samba-admin")
1709 self.adminCredentials.set_password(ldapadminpass)
1711 # Now start the slapd, so we can provision onto it. We keep the
1712 # subprocess context around, to kill this off at the successful
1714 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1716 while self.slapd.poll() is None:
1717 # Wait until the socket appears
1719 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1720 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1721 expression="(objectClass=OpenLDAProotDSE)")
1722 # If we have got here, then we must have a valid connection to the LDAP server!
1728 raise ProvisioningError("slapd died before we could make a connection to it")
1731 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1733 hostname=None, ldapadminpass=None, root=None,
1735 ldap_backend_extra_port=None,
1737 slapd_path=None, nosync=False,
1738 ldap_dryrun_mode=False):
1740 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1743 nosync_config = "dbnosync"
1745 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1746 refint_attributes = ""
1747 memberof_config = "# Generated from Samba4 schema\n"
1748 for att in lnkattr.keys():
1749 if lnkattr[att] is not None:
1750 refint_attributes = refint_attributes + " " + att
1752 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1753 { "MEMBER_ATTR" : att ,
1754 "MEMBEROF_ATTR" : lnkattr[att] })
1756 refint_config = read_and_sub_file(setup_path("refint.conf"),
1757 { "LINK_ATTRS" : refint_attributes})
1759 attrs = ["linkID", "lDAPDisplayName"]
1760 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1762 for i in range (0, len(res)):
1763 index_attr = res[i]["lDAPDisplayName"][0]
1764 if index_attr == "objectGUID":
1765 index_attr = "entryUUID"
1767 index_config += "index " + index_attr + " eq\n"
1769 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1771 mmr_replicator_acl = ""
1772 mmr_serverids_config = ""
1773 mmr_syncrepl_schema_config = ""
1774 mmr_syncrepl_config_config = ""
1775 mmr_syncrepl_user_config = ""
1778 if ol_mmr_urls is not None:
1779 # For now, make these equal
1780 mmr_pass = ldapadminpass
1782 url_list=filter(None,ol_mmr_urls.split(' '))
1783 if (len(url_list) == 1):
1784 url_list=filter(None,ol_mmr_urls.split(','))
1787 mmr_on_config = "MirrorMode On"
1788 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1790 for url in url_list:
1792 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1793 { "SERVERID" : str(serverid),
1794 "LDAPSERVER" : url })
1797 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1799 "MMRDN": names.schemadn,
1801 "MMR_PASSWORD": mmr_pass})
1804 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1806 "MMRDN": names.configdn,
1808 "MMR_PASSWORD": mmr_pass})
1811 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1813 "MMRDN": names.domaindn,
1815 "MMR_PASSWORD": mmr_pass })
1816 # OpenLDAP cn=config initialisation
1817 olc_syncrepl_config = ""
1819 # if mmr = yes, generate cn=config-replication directives
1820 # and olc_seed.lif for the other mmr-servers
1821 if ol_mmr_urls is not None:
1823 olc_serverids_config = ""
1824 olc_syncrepl_seed_config = ""
1825 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1827 for url in url_list:
1829 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1830 { "SERVERID" : str(serverid),
1831 "LDAPSERVER" : url })
1834 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1837 "MMR_PASSWORD": mmr_pass})
1839 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1841 "LDAPSERVER" : url})
1843 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1844 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1845 "OLC_PW": ldapadminpass,
1846 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1849 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1850 {"DNSDOMAIN": names.dnsdomain,
1851 "LDAPDIR": paths.ldapdir,
1852 "DOMAINDN": names.domaindn,
1853 "CONFIGDN": names.configdn,
1854 "SCHEMADN": names.schemadn,
1855 "MEMBEROF_CONFIG": memberof_config,
1856 "MIRRORMODE": mmr_on_config,
1857 "REPLICATOR_ACL": mmr_replicator_acl,
1858 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1859 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1860 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1861 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1862 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1863 "OLC_MMR_CONFIG": olc_mmr_config,
1864 "REFINT_CONFIG": refint_config,
1865 "INDEX_CONFIG": index_config,
1866 "NOSYNC": nosync_config})
1868 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1869 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1870 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1872 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1873 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1875 setup_file(setup_path("cn=samba.ldif"),
1876 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1877 { "UUID": str(uuid.uuid4()),
1878 "LDAPTIME": timestring(int(time.time()))} )
1879 setup_file(setup_path("cn=samba-admin.ldif"),
1880 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1881 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1882 "UUID": str(uuid.uuid4()),
1883 "LDAPTIME": timestring(int(time.time()))} )
1885 if ol_mmr_urls is not None:
1886 setup_file(setup_path("cn=replicator.ldif"),
1887 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1888 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1889 "UUID": str(uuid.uuid4()),
1890 "LDAPTIME": timestring(int(time.time()))} )
1893 mapping = "schema-map-openldap-2.3"
1894 backend_schema = "backend-schema.schema"
1896 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1897 assert backend_schema_data is not None
1898 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1900 # now we generate the needed strings to start slapd automatically,
1901 # first ldapi_uri...
1902 if ldap_backend_extra_port is not None:
1903 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1904 # specified there as part of it's clue as to it's own name,
1905 # and not to replicate to itself
1906 if ol_mmr_urls is None:
1907 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1909 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1911 server_port_string = ""
1913 # Prepare the 'result' information - the commands to return in particular
1914 result.slapd_provision_command = [slapd_path]
1916 result.slapd_provision_command.append("-F" + paths.olcdir)
1918 result.slapd_provision_command.append("-h")
1920 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1921 result.slapd_command = list(result.slapd_provision_command)
1923 result.slapd_provision_command.append(result.ldapi_uri)
1924 result.slapd_provision_command.append("-d0")
1926 uris = result.ldapi_uri
1927 if server_port_string is not "":
1928 uris = uris + " " + server_port_string
1930 result.slapd_command.append(uris)
1932 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1933 result.credentials.set_username("samba-admin")
1935 # If we were just looking for crashes up to this point, it's a
1936 # good time to exit before we realise we don't have OpenLDAP on
1938 if ldap_dryrun_mode:
1941 # Finally, convert the configuration into cn=config style!
1942 if not os.path.isdir(paths.olcdir):
1943 os.makedirs(paths.olcdir, 0770)
1945 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1947 # We can't do this, as OpenLDAP is strange. It gives an error
1948 # output to the above, but does the conversion sucessfully...
1951 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1953 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1954 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1956 # Don't confuse the admin by leaving the slapd.conf around
1957 os.remove(paths.slapdconf)
1960 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1962 hostname=None, ldapadminpass=None, root=None,
1964 ldap_backend_extra_port=None,
1968 ldap_dryrun_mode=False,
1971 if ldap_backend_extra_port is not None:
1972 serverport = "ServerPort=%d" % ldap_backend_extra_port
1976 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1978 "HOSTNAME": hostname,
1979 "DNSDOMAIN": names.dnsdomain,
1980 "LDAPDIR": paths.ldapdir,
1981 "DOMAINDN": names.domaindn,
1982 "LDAPMANAGERDN": names.ldapmanagerdn,
1983 "LDAPMANAGERPASS": ldapadminpass,
1984 "SERVERPORT": serverport})
1986 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1987 {"CONFIGDN": names.configdn,
1988 "SCHEMADN": names.schemadn,
1989 "SAMBADN": names.sambadn,
1992 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1993 {"SAMBADN": names.sambadn,
1996 setup_file(setup_path("fedorads-dna.ldif"), paths.fedoradsdna,
1997 {"DOMAINDN": names.domaindn,
1998 "SAMBADN": names.sambadn,
1999 "DOMAINSID": str(domainsid),
2002 setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
2004 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
2006 refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
2007 memberof_config = ""
2011 for attr in lnkattr.keys():
2012 if lnkattr[attr] is not None:
2013 refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
2014 { "ARG_NUMBER" : str(argnum) ,
2015 "LINK_ATTR" : attr })
2016 memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
2017 { "MEMBER_ATTR" : attr ,
2018 "MEMBEROF_ATTR" : lnkattr[attr] })
2019 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2023 open(paths.fedoradsrefint, 'w').write(refint_config)
2024 open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
2026 attrs = ["lDAPDisplayName"]
2027 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
2029 for i in range (0, len(res)):
2030 attr = res[i]["lDAPDisplayName"][0]
2032 if attr == "objectGUID":
2035 index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2038 open(paths.fedoradsindex, 'w').write(index_config)
2040 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
2041 {"SAMBADN": names.sambadn,
2042 "LDAPADMINPASS": ldapadminpass
2045 mapping = "schema-map-fedora-ds-1.0"
2046 backend_schema = "99_ad.ldif"
2048 # Build a schema file in Fedora DS format
2049 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
2050 assert backend_schema_data is not None
2051 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2053 result.credentials.set_bind_dn(names.ldapmanagerdn)
2055 # Destory the target directory, or else setup-ds.pl will complain
2056 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
2057 shutil.rmtree(fedora_ds_dir, True)
2059 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
2060 #In the 'provision' command line, stay in the foreground so we can easily kill it
2061 result.slapd_provision_command.append("-d0")
2063 #the command for the final run is the normal script
2064 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
2066 # If we were just looking for crashes up to this point, it's a
2067 # good time to exit before we realise we don't have Fedora DS on
2068 if ldap_dryrun_mode:
2071 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2072 if setup_ds_path is None:
2073 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\"!")
2074 if not os.path.exists(setup_ds_path):
2075 message (setup_ds_path)
2076 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2078 # Run the Fedora DS setup utility
2079 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
2081 raise ProvisioningError("setup-ds failed")
2084 retcode = subprocess.call([
2085 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
2086 close_fds=True, shell=False)
2088 raise("ldib2db failed")
2090 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2091 """Create a PHP LDAP admin configuration file.
2093 :param path: Path to write the configuration to.
2094 :param setup_path: Function to generate setup paths.
2096 setup_file(setup_path("phpldapadmin-config.php"), path,
2097 {"S4_LDAPI_URI": ldapi_uri})
2100 def create_zone_file(path, setup_path, dnsdomain,
2101 hostip, hostip6, hostname, realm, domainguid,
2103 """Write out a DNS zone file, from the info in the current database.
2105 :param path: Path of the new zone file.
2106 :param setup_path: Setup path function.
2107 :param dnsdomain: DNS Domain name
2108 :param domaindn: DN of the Domain
2109 :param hostip: Local IPv4 IP
2110 :param hostip6: Local IPv6 IP
2111 :param hostname: Local hostname
2112 :param realm: Realm name
2113 :param domainguid: GUID of the domain.
2114 :param ntdsguid: GUID of the hosts nTDSDSA record.
2116 assert isinstance(domainguid, str)
2118 if hostip6 is not None:
2119 hostip6_base_line = " IN AAAA " + hostip6
2120 hostip6_host_line = hostname + " IN AAAA " + hostip6
2122 hostip6_base_line = ""
2123 hostip6_host_line = ""
2125 if hostip is not None:
2126 hostip_base_line = " IN A " + hostip
2127 hostip_host_line = hostname + " IN A " + hostip
2129 hostip_base_line = ""
2130 hostip_host_line = ""
2132 setup_file(setup_path("provision.zone"), path, {
2133 "HOSTNAME": hostname,
2134 "DNSDOMAIN": dnsdomain,
2136 "HOSTIP_BASE_LINE": hostip_base_line,
2137 "HOSTIP_HOST_LINE": hostip_host_line,
2138 "DOMAINGUID": domainguid,
2139 "DATESTRING": time.strftime("%Y%m%d%H"),
2140 "DEFAULTSITE": DEFAULTSITE,
2141 "NTDSGUID": ntdsguid,
2142 "HOSTIP6_BASE_LINE": hostip6_base_line,
2143 "HOSTIP6_HOST_LINE": hostip6_host_line,
2147 def create_named_conf(path, setup_path, realm, dnsdomain,
2149 """Write out a file containing zone statements suitable for inclusion in a
2150 named.conf file (including GSS-TSIG configuration).
2152 :param path: Path of the new named.conf file.
2153 :param setup_path: Setup path function.
2154 :param realm: Realm name
2155 :param dnsdomain: DNS Domain name
2156 :param private_dir: Path to private directory
2157 :param keytab_name: File name of DNS keytab file
2160 setup_file(setup_path("named.conf"), path, {
2161 "DNSDOMAIN": dnsdomain,
2163 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2164 "PRIVATE_DIR": private_dir
2167 def create_named_txt(path, setup_path, realm, dnsdomain,
2168 private_dir, keytab_name):
2169 """Write out a file containing zone statements suitable for inclusion in a
2170 named.conf file (including GSS-TSIG configuration).
2172 :param path: Path of the new named.conf file.
2173 :param setup_path: Setup path function.
2174 :param realm: Realm name
2175 :param dnsdomain: DNS Domain name
2176 :param private_dir: Path to private directory
2177 :param keytab_name: File name of DNS keytab file
2180 setup_file(setup_path("named.txt"), path, {
2181 "DNSDOMAIN": dnsdomain,
2183 "DNS_KEYTAB": keytab_name,
2184 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2185 "PRIVATE_DIR": private_dir
2188 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2189 """Write out a file containing zone statements suitable for inclusion in a
2190 named.conf file (including GSS-TSIG configuration).
2192 :param path: Path of the new named.conf file.
2193 :param setup_path: Setup path function.
2194 :param dnsdomain: DNS Domain name
2195 :param hostname: Local hostname
2196 :param realm: Realm name
2199 setup_file(setup_path("krb5.conf"), path, {
2200 "DNSDOMAIN": dnsdomain,
2201 "HOSTNAME": hostname,