s4/domain behaviour flags: Fix them up in various locations
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blobcbb747b64f2b515bcdd3384815ad04c69af4cd75
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
29 import os
30 import sys
31 import pwd
32 import grp
33 import time
34 import uuid, glue
35 import socket
36 import param
37 import registry
38 import samba
39 import subprocess
40 import ldb
42 import shutil
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_2008_R2, DS_DC_FUNCTION_2008_R2
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 import urllib
52 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
53 from ms_schema import read_ms_schema
54 from ms_display_specifiers import read_ms_ldif
55 from signal import SIGTERM
57 __docformat__ = "restructuredText"
60 class ProvisioningError(ValueError):
61 pass
64 def find_setup_dir():
65 """Find the setup directory used by provision."""
66 dirname = os.path.dirname(__file__)
67 if "/site-packages/" in dirname:
68 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
69 for suffix in ["share/setup", "share/samba/setup", "setup"]:
70 ret = os.path.join(prefix, suffix)
71 if os.path.isdir(ret):
72 return ret
73 # In source tree
74 ret = os.path.join(dirname, "../../../setup")
75 if os.path.isdir(ret):
76 return ret
77 raise Exception("Unable to find setup directory.")
80 DEFAULTSITE = "Default-First-Site-Name"
82 class InvalidNetbiosName(Exception):
83 """A specified name was not a valid NetBIOS name."""
84 def __init__(self, name):
85 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
88 class ProvisionPaths(object):
89 def __init__(self):
90 self.shareconf = None
91 self.hklm = None
92 self.hkcu = None
93 self.hkcr = None
94 self.hku = None
95 self.hkpd = None
96 self.hkpt = None
97 self.samdb = None
98 self.idmapdb = None
99 self.secrets = None
100 self.keytab = None
101 self.dns_keytab = None
102 self.dns = None
103 self.winsdb = None
104 self.private_dir = None
105 self.ldapdir = None
106 self.slapdconf = None
107 self.modulesconf = None
108 self.memberofconf = None
109 self.fedoradsinf = None
110 self.fedoradspartitions = None
111 self.fedoradssasl = None
112 self.olmmron = None
113 self.olmmrserveridsconf = None
114 self.olmmrsyncreplconf = None
115 self.olcdir = None
116 self.olslapd = None
117 self.olcseedldif = None
120 class ProvisionNames(object):
121 def __init__(self):
122 self.rootdn = None
123 self.domaindn = None
124 self.configdn = None
125 self.schemadn = None
126 self.sambadn = None
127 self.ldapmanagerdn = None
128 self.dnsdomain = None
129 self.realm = None
130 self.netbiosname = None
131 self.domain = None
132 self.hostname = None
133 self.sitename = None
134 self.smbconf = None
137 class ProvisionResult(object):
138 def __init__(self):
139 self.paths = None
140 self.domaindn = None
141 self.lp = None
142 self.samdb = None
144 class Schema(object):
145 def __init__(self, setup_path, schemadn=None,
146 serverdn=None, sambadn=None, ldap_backend_type=None):
147 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
149 :param samdb: Load a schema into a SamDB.
150 :param setup_path: Setup path function.
151 :param schemadn: DN of the schema
152 :param serverdn: DN of the server
154 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
157 self.ldb = Ldb()
158 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
159 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
160 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
161 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
162 check_all_substituted(self.schema_data)
164 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
165 {"SCHEMADN": schemadn,
166 "SERVERDN": serverdn,
168 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
169 {"SCHEMADN": schemadn
172 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
173 prefixmap = b64encode(prefixmap)
175 # We don't actually add this ldif, just parse it
176 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
177 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
180 # Return a hash with the forward attribute as a key and the back as the value
181 def get_linked_attributes(schemadn,schemaldb):
182 attrs = ["linkID", "lDAPDisplayName"]
183 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)
184 attributes = {}
185 for i in range (0, len(res)):
186 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
187 target = schemaldb.searchone(basedn=schemadn,
188 expression=expression,
189 attribute="lDAPDisplayName",
190 scope=SCOPE_SUBTREE)
191 if target is not None:
192 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
194 return attributes
196 def get_dnsyntax_attributes(schemadn,schemaldb):
197 attrs = ["linkID", "lDAPDisplayName"]
198 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
199 attributes = []
200 for i in range (0, len(res)):
201 attributes.append(str(res[i]["lDAPDisplayName"]))
203 return attributes
206 def check_install(lp, session_info, credentials):
207 """Check whether the current install seems ok.
209 :param lp: Loadparm context
210 :param session_info: Session information
211 :param credentials: Credentials
213 if lp.get("realm") == "":
214 raise Exception("Realm empty")
215 ldb = Ldb(lp.get("sam database"), session_info=session_info,
216 credentials=credentials, lp=lp)
217 if len(ldb.search("(cn=Administrator)")) != 1:
218 raise ProvisioningError("No administrator account found")
221 def findnss(nssfn, names):
222 """Find a user or group from a list of possibilities.
224 :param nssfn: NSS Function to try (should raise KeyError if not found)
225 :param names: Names to check.
226 :return: Value return by first names list.
228 for name in names:
229 try:
230 return nssfn(name)
231 except KeyError:
232 pass
233 raise KeyError("Unable to find user/group %r" % names)
236 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
237 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
240 def read_and_sub_file(file, subst_vars):
241 """Read a file and sub in variables found in it
243 :param file: File to be read (typically from setup directory)
244 param subst_vars: Optional variables to subsitute in the file.
246 data = open(file, 'r').read()
247 if subst_vars is not None:
248 data = substitute_var(data, subst_vars)
249 check_all_substituted(data)
250 return data
253 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
254 """Setup a ldb in the private dir.
256 :param ldb: LDB file to import data into
257 :param ldif_path: Path of the LDIF file to load
258 :param subst_vars: Optional variables to subsitute in LDIF.
260 assert isinstance(ldif_path, str)
262 data = read_and_sub_file(ldif_path, subst_vars)
263 ldb.add_ldif(data)
266 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
267 """Modify a ldb in the private dir.
269 :param ldb: LDB object.
270 :param ldif_path: LDIF file path.
271 :param subst_vars: Optional dictionary with substitution variables.
273 data = read_and_sub_file(ldif_path, subst_vars)
275 ldb.modify_ldif(data)
278 def setup_ldb(ldb, ldif_path, subst_vars):
279 """Import a LDIF a file into a LDB handle, optionally substituting variables.
281 :note: Either all LDIF data will be added or none (using transactions).
283 :param ldb: LDB file to import into.
284 :param ldif_path: Path to the LDIF file.
285 :param subst_vars: Dictionary with substitution variables.
287 assert ldb is not None
288 ldb.transaction_start()
289 try:
290 setup_add_ldif(ldb, ldif_path, subst_vars)
291 except:
292 ldb.transaction_cancel()
293 raise
294 ldb.transaction_commit()
297 def setup_file(template, fname, subst_vars):
298 """Setup a file in the private dir.
300 :param template: Path of the template file.
301 :param fname: Path of the file to create.
302 :param subst_vars: Substitution variables.
304 f = fname
306 if os.path.exists(f):
307 os.unlink(f)
309 data = read_and_sub_file(template, subst_vars)
310 open(f, 'w').write(data)
313 def provision_paths_from_lp(lp, dnsdomain):
314 """Set the default paths for provisioning.
316 :param lp: Loadparm context.
317 :param dnsdomain: DNS Domain name
319 paths = ProvisionPaths()
320 paths.private_dir = lp.get("private dir")
321 paths.keytab = "secrets.keytab"
322 paths.dns_keytab = "dns.keytab"
324 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
325 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
326 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
327 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
328 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
329 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
330 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
331 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
332 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
333 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
334 paths.phpldapadminconfig = os.path.join(paths.private_dir,
335 "phpldapadmin-config.php")
336 paths.ldapdir = os.path.join(paths.private_dir,
337 "ldap")
338 paths.slapdconf = os.path.join(paths.ldapdir,
339 "slapd.conf")
340 paths.slapdpid = os.path.join(paths.ldapdir,
341 "slapd.pid")
342 paths.modulesconf = os.path.join(paths.ldapdir,
343 "modules.conf")
344 paths.memberofconf = os.path.join(paths.ldapdir,
345 "memberof.conf")
346 paths.fedoradsinf = os.path.join(paths.ldapdir,
347 "fedorads.inf")
348 paths.fedoradspartitions = os.path.join(paths.ldapdir,
349 "fedorads-partitions.ldif")
350 paths.fedoradssasl = os.path.join(paths.ldapdir,
351 "fedorads-sasl.ldif")
352 paths.fedoradssamba = os.path.join(paths.ldapdir,
353 "fedorads-samba.ldif")
354 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
355 "mmr_serverids.conf")
356 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
357 "mmr_syncrepl.conf")
358 paths.olcdir = os.path.join(paths.ldapdir,
359 "slapd.d")
360 paths.olcseedldif = os.path.join(paths.ldapdir,
361 "olc_seed.ldif")
362 paths.hklm = "hklm.ldb"
363 paths.hkcr = "hkcr.ldb"
364 paths.hkcu = "hkcu.ldb"
365 paths.hku = "hku.ldb"
366 paths.hkpd = "hkpd.ldb"
367 paths.hkpt = "hkpt.ldb"
369 paths.sysvol = lp.get("path", "sysvol")
371 paths.netlogon = lp.get("path", "netlogon")
373 paths.smbconf = lp.configfile
375 return paths
378 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
379 serverrole=None, rootdn=None, domaindn=None, configdn=None,
380 schemadn=None, serverdn=None, sitename=None, sambadn=None):
381 """Guess configuration settings to use."""
383 if hostname is None:
384 hostname = socket.gethostname().split(".")[0].lower()
386 netbiosname = hostname.upper()
387 if not valid_netbios_name(netbiosname):
388 raise InvalidNetbiosName(netbiosname)
390 hostname = hostname.lower()
392 if dnsdomain is None:
393 dnsdomain = lp.get("realm")
395 if serverrole is None:
396 serverrole = lp.get("server role")
398 assert dnsdomain is not None
399 realm = dnsdomain.upper()
401 if lp.get("realm").upper() != realm:
402 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
403 (lp.get("realm"), lp.configfile, realm))
405 dnsdomain = dnsdomain.lower()
407 if serverrole == "domain controller":
408 if domain is None:
409 domain = lp.get("workgroup")
410 if domaindn is None:
411 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
412 if lp.get("workgroup").upper() != domain.upper():
413 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
414 lp.get("workgroup"), domain)
415 else:
416 domain = netbiosname
417 if domaindn is None:
418 domaindn = "CN=" + netbiosname
420 assert domain is not None
421 domain = domain.upper()
422 if not valid_netbios_name(domain):
423 raise InvalidNetbiosName(domain)
425 if netbiosname.upper() == realm.upper():
426 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
428 if hostname.upper() == realm.upper():
429 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
431 if domain.upper() == realm.upper():
432 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
434 if rootdn is None:
435 rootdn = domaindn
437 if configdn is None:
438 configdn = "CN=Configuration," + rootdn
439 if schemadn is None:
440 schemadn = "CN=Schema," + configdn
441 if sambadn is None:
442 sambadn = "CN=Samba"
444 if sitename is None:
445 sitename=DEFAULTSITE
447 names = ProvisionNames()
448 names.rootdn = rootdn
449 names.domaindn = domaindn
450 names.configdn = configdn
451 names.schemadn = schemadn
452 names.sambadn = sambadn
453 names.ldapmanagerdn = "CN=Manager," + rootdn
454 names.dnsdomain = dnsdomain
455 names.domain = domain
456 names.realm = realm
457 names.netbiosname = netbiosname
458 names.hostname = hostname
459 names.sitename = sitename
460 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
462 return names
465 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
466 targetdir):
467 """Create a new smb.conf file based on a couple of basic settings.
469 assert smbconf is not None
470 if hostname is None:
471 hostname = socket.gethostname().split(".")[0].lower()
473 if serverrole is None:
474 serverrole = "standalone"
476 assert serverrole in ("domain controller", "member server", "standalone")
477 if serverrole == "domain controller":
478 smbconfsuffix = "dc"
479 elif serverrole == "member server":
480 smbconfsuffix = "member"
481 elif serverrole == "standalone":
482 smbconfsuffix = "standalone"
484 assert domain is not None
485 assert realm is not None
487 default_lp = param.LoadParm()
488 #Load non-existant file
489 if os.path.exists(smbconf):
490 default_lp.load(smbconf)
492 if targetdir is not None:
493 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
494 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
496 default_lp.set("lock dir", os.path.abspath(targetdir))
497 else:
498 privatedir_line = ""
499 lockdir_line = ""
501 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
502 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
504 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
505 smbconf, {
506 "HOSTNAME": hostname,
507 "DOMAIN": domain,
508 "REALM": realm,
509 "SERVERROLE": serverrole,
510 "NETLOGONPATH": netlogon,
511 "SYSVOLPATH": sysvol,
512 "PRIVATEDIR_LINE": privatedir_line,
513 "LOCKDIR_LINE": lockdir_line
517 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
518 users_gid, wheel_gid):
519 """setup reasonable name mappings for sam names to unix names.
521 :param samdb: SamDB object.
522 :param idmap: IDmap db object.
523 :param sid: The domain sid.
524 :param domaindn: The domain DN.
525 :param root_uid: uid of the UNIX root user.
526 :param nobody_uid: uid of the UNIX nobody user.
527 :param users_gid: gid of the UNIX users group.
528 :param wheel_gid: gid of the UNIX wheel group."""
530 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
531 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
533 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
534 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
536 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
537 credentials, names,
538 serverrole, ldap_backend=None,
539 erase=False):
540 """Setup the partitions for the SAM database.
542 Alternatively, provision() may call this, and then populate the database.
544 :note: This will wipe the Sam Database!
546 :note: This function always removes the local SAM LDB file. The erase
547 parameter controls whether to erase the existing data, which
548 may not be stored locally but in LDAP.
550 assert session_info is not None
552 # We use options=["modules:"] to stop the modules loading - we
553 # just want to wipe and re-initialise the database, not start it up
555 try:
556 samdb = Ldb(url=samdb_path, session_info=session_info,
557 credentials=credentials, lp=lp, options=["modules:"])
558 # Wipes the database
559 samdb.erase_except_schema_controlled()
560 except LdbError:
561 os.unlink(samdb_path)
562 samdb = Ldb(url=samdb_path, session_info=session_info,
563 credentials=credentials, lp=lp, options=["modules:"])
564 # Wipes the database
565 samdb.erase_except_schema_controlled()
568 #Add modules to the list to activate them by default
569 #beware often order is important
571 # Some Known ordering constraints:
572 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
573 # - objectclass must be before password_hash, because password_hash checks
574 # that the objectclass is of type person (filled in by objectclass
575 # module when expanding the objectclass list)
576 # - partition must be last
577 # - each partition has its own module list then
578 modules_list = ["rootdse",
579 "paged_results",
580 "ranged_results",
581 "anr",
582 "server_sort",
583 "asq",
584 "extended_dn_store",
585 "extended_dn_in",
586 "rdn_name",
587 "objectclass",
588 "descriptor",
589 "samldb",
590 "password_hash",
591 "operational",
592 "kludge_acl"]
593 tdb_modules_list = [
594 "subtree_rename",
595 "subtree_delete",
596 "linked_attributes",
597 "extended_dn_out_ldb"]
598 modules_list2 = ["show_deleted",
599 "partition"]
601 domaindn_ldb = "users.ldb"
602 configdn_ldb = "configuration.ldb"
603 schemadn_ldb = "schema.ldb"
604 if ldap_backend is not None:
605 domaindn_ldb = ldap_backend.ldapi_uri
606 configdn_ldb = ldap_backend.ldapi_uri
607 schemadn_ldb = ldap_backend.ldapi_uri
609 if ldap_backend.ldap_backend_type == "fedora-ds":
610 backend_modules = ["nsuniqueid", "paged_searches"]
611 # We can handle linked attributes here, as we don't have directory-side subtree operations
612 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
613 elif ldap_backend.ldap_backend_type == "openldap":
614 backend_modules = ["entryuuid", "paged_searches"]
615 # OpenLDAP handles subtree renames, so we don't want to do any of these things
616 tdb_modules_list = ["extended_dn_out_dereference"]
618 elif serverrole == "domain controller":
619 tdb_modules_list.insert(0, "repl_meta_data")
620 backend_modules = []
621 else:
622 backend_modules = ["objectguid"]
624 if tdb_modules_list is None:
625 tdb_modules_list_as_string = ""
626 else:
627 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
629 samdb.transaction_start()
630 try:
631 message("Setting up sam.ldb partitions and settings")
632 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
633 "SCHEMADN": names.schemadn,
634 "SCHEMADN_LDB": schemadn_ldb,
635 "SCHEMADN_MOD2": ",objectguid",
636 "CONFIGDN": names.configdn,
637 "CONFIGDN_LDB": configdn_ldb,
638 "DOMAINDN": names.domaindn,
639 "DOMAINDN_LDB": domaindn_ldb,
640 "SCHEMADN_MOD": "schema_fsmo,instancetype",
641 "CONFIGDN_MOD": "naming_fsmo,instancetype",
642 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
643 "MODULES_LIST": ",".join(modules_list),
644 "TDB_MODULES_LIST": tdb_modules_list_as_string,
645 "MODULES_LIST2": ",".join(modules_list2),
646 "BACKEND_MOD": ",".join(backend_modules),
649 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
651 message("Setting up sam.ldb rootDSE")
652 setup_samdb_rootdse(samdb, setup_path, names)
654 except:
655 samdb.transaction_cancel()
656 raise
658 samdb.transaction_commit()
662 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
663 netbiosname, domainsid, keytab_path, samdb_url,
664 dns_keytab_path, dnspass, machinepass):
665 """Add DC-specific bits to a secrets database.
667 :param secretsdb: Ldb Handle to the secrets database
668 :param setup_path: Setup path function
669 :param machinepass: Machine password
671 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
672 "MACHINEPASS_B64": b64encode(machinepass),
673 "DOMAIN": domain,
674 "REALM": realm,
675 "DNSDOMAIN": dnsdomain,
676 "DOMAINSID": str(domainsid),
677 "SECRETS_KEYTAB": keytab_path,
678 "NETBIOSNAME": netbiosname,
679 "SAM_LDB": samdb_url,
680 "DNS_KEYTAB": dns_keytab_path,
681 "DNSPASS_B64": b64encode(dnspass),
685 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
686 """Setup the secrets database.
688 :param path: Path to the secrets database.
689 :param setup_path: Get the path to a setup file.
690 :param session_info: Session info.
691 :param credentials: Credentials
692 :param lp: Loadparm context
693 :return: LDB handle for the created secrets database
695 if os.path.exists(path):
696 os.unlink(path)
697 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
698 lp=lp)
699 secrets_ldb.erase()
700 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
701 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
702 lp=lp)
703 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
705 if credentials is not None and credentials.authentication_requested():
706 if credentials.get_bind_dn() is not None:
707 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
708 "LDAPMANAGERDN": credentials.get_bind_dn(),
709 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
711 else:
712 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
713 "LDAPADMINUSER": credentials.get_username(),
714 "LDAPADMINREALM": credentials.get_realm(),
715 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
718 return secrets_ldb
720 def setup_registry(path, setup_path, session_info, lp):
721 """Setup the registry.
723 :param path: Path to the registry database
724 :param setup_path: Function that returns the path to a setup.
725 :param session_info: Session information
726 :param credentials: Credentials
727 :param lp: Loadparm context
729 reg = registry.Registry()
730 hive = registry.open_ldb(path, session_info=session_info,
731 lp_ctx=lp)
732 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
733 provision_reg = setup_path("provision.reg")
734 assert os.path.exists(provision_reg)
735 reg.diff_apply(provision_reg)
738 def setup_idmapdb(path, setup_path, session_info, lp):
739 """Setup the idmap database.
741 :param path: path to the idmap database
742 :param setup_path: Function that returns a path to a setup file
743 :param session_info: Session information
744 :param credentials: Credentials
745 :param lp: Loadparm context
747 if os.path.exists(path):
748 os.unlink(path)
750 idmap_ldb = IDmapDB(path, session_info=session_info,
751 lp=lp)
753 idmap_ldb.erase()
754 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
755 return idmap_ldb
758 def setup_samdb_rootdse(samdb, setup_path, names):
759 """Setup the SamDB rootdse.
761 :param samdb: Sam Database handle
762 :param setup_path: Obtain setup path
764 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
765 "SCHEMADN": names.schemadn,
766 "NETBIOSNAME": names.netbiosname,
767 "DNSDOMAIN": names.dnsdomain,
768 "REALM": names.realm,
769 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
770 "DOMAINDN": names.domaindn,
771 "ROOTDN": names.rootdn,
772 "CONFIGDN": names.configdn,
773 "SERVERDN": names.serverdn,
777 def setup_self_join(samdb, names,
778 machinepass, dnspass,
779 domainsid, invocationid, setup_path,
780 policyguid, policyguid_dc, domainControllerFunctionality):
781 """Join a host to its own domain."""
782 assert isinstance(invocationid, str)
783 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
784 "CONFIGDN": names.configdn,
785 "SCHEMADN": names.schemadn,
786 "DOMAINDN": names.domaindn,
787 "SERVERDN": names.serverdn,
788 "INVOCATIONID": invocationid,
789 "NETBIOSNAME": names.netbiosname,
790 "DEFAULTSITE": names.sitename,
791 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
792 "MACHINEPASS_B64": b64encode(machinepass),
793 "DNSPASS_B64": b64encode(dnspass),
794 "REALM": names.realm,
795 "DOMAIN": names.domain,
796 "DNSDOMAIN": names.dnsdomain,
797 "SAMBA_VERSION_STRING": version,
798 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
800 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
801 "POLICYGUID": policyguid,
802 "POLICYGUID_DC": policyguid_dc,
803 "DNSDOMAIN": names.dnsdomain,
804 "DOMAINSID": str(domainsid),
805 "DOMAINDN": names.domaindn})
807 # add the NTDSGUID based SPNs
808 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
809 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
810 expression="", scope=SCOPE_BASE)
811 assert isinstance(names.ntdsguid, str)
813 # Setup fSMORoleOwner entries to point at the newly created DC entry
814 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
815 "DOMAIN": names.domain,
816 "DNSDOMAIN": names.dnsdomain,
817 "DOMAINDN": names.domaindn,
818 "CONFIGDN": names.configdn,
819 "SCHEMADN": names.schemadn,
820 "DEFAULTSITE": names.sitename,
821 "SERVERDN": names.serverdn,
822 "NETBIOSNAME": names.netbiosname,
823 "NTDSGUID": names.ntdsguid
827 def setup_samdb(path, setup_path, session_info, credentials, lp,
828 names, message,
829 domainsid, domainguid, policyguid, policyguid_dc,
830 fill, adminpass, krbtgtpass,
831 machinepass, invocationid, dnspass,
832 serverrole, schema=None, ldap_backend=None):
833 """Setup a complete SAM Database.
835 :note: This will wipe the main SAM database file!
838 domainFunctionality = DS_DOMAIN_FUNCTION_2008_R2
839 forestFunctionality = DS_DOMAIN_FUNCTION_2008_R2
840 domainControllerFunctionality = DS_DC_FUNCTION_2008_R2
842 # Also wipes the database
843 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
844 credentials=credentials, session_info=session_info,
845 names=names,
846 ldap_backend=ldap_backend, serverrole=serverrole)
848 if (schema == None):
849 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
850 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
852 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
853 samdb = Ldb(session_info=session_info,
854 credentials=credentials, lp=lp)
856 message("Pre-loading the Samba 4 and AD schema")
858 # Load the schema from the one we computed earlier
859 samdb.set_schema_from_ldb(schema.ldb)
861 # And now we can connect to the DB - the schema won't be loaded from the DB
862 samdb.connect(path)
864 # Load @OPTIONS
865 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
867 if fill == FILL_DRS:
868 return samdb
870 samdb.transaction_start()
871 try:
872 message("Erasing data from partitions")
873 # Load the schema (again). This time it will force a reindex,
874 # and will therefore make the erase_partitions() below
875 # computationally sane
876 samdb.set_schema_from_ldb(schema.ldb)
877 samdb.erase_partitions()
879 # Set the domain functionality levels onto the database.
880 # Various module (the password_hash module in particular) need
881 # to know what level of AD we are emulating.
883 # These will be fixed into the database via the database
884 # modifictions below, but we need them set from the start.
885 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
886 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
887 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
889 samdb.set_domain_sid(str(domainsid))
890 if serverrole == "domain controller":
891 samdb.set_invocation_id(invocationid)
893 message("Adding DomainDN: %s" % names.domaindn)
894 if serverrole == "domain controller":
895 domain_oc = "domainDNS"
896 else:
897 domain_oc = "samba4LocalDomain"
899 #impersonate domain admin
900 admin_session_info = admin_session(lp, str(domainsid))
901 samdb.set_session_info(admin_session_info)
903 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
904 "DOMAINDN": names.domaindn,
905 "DOMAIN_OC": domain_oc
908 message("Modifying DomainDN: " + names.domaindn + "")
909 if domainguid is not None:
910 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
911 else:
912 domainguid_mod = ""
914 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
915 "LDAPTIME": timestring(int(time.time())),
916 "DOMAINSID": str(domainsid),
917 "SCHEMADN": names.schemadn,
918 "NETBIOSNAME": names.netbiosname,
919 "DEFAULTSITE": names.sitename,
920 "CONFIGDN": names.configdn,
921 "SERVERDN": names.serverdn,
922 "POLICYGUID": policyguid,
923 "DOMAINDN": names.domaindn,
924 "DOMAINGUID_MOD": domainguid_mod,
925 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
928 message("Adding configuration container")
929 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
930 "CONFIGDN": names.configdn,
932 message("Modifying configuration container")
933 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
934 "CONFIGDN": names.configdn,
935 "SCHEMADN": names.schemadn,
938 # The LDIF here was created when the Schema object was constructed
939 message("Setting up sam.ldb schema")
940 samdb.add_ldif(schema.schema_dn_add)
941 samdb.modify_ldif(schema.schema_dn_modify)
942 samdb.write_prefixes_from_schema()
943 samdb.add_ldif(schema.schema_data)
944 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
945 {"SCHEMADN": names.schemadn})
947 message("Setting up sam.ldb configuration data")
948 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
949 "CONFIGDN": names.configdn,
950 "NETBIOSNAME": names.netbiosname,
951 "DEFAULTSITE": names.sitename,
952 "DNSDOMAIN": names.dnsdomain,
953 "DOMAIN": names.domain,
954 "SCHEMADN": names.schemadn,
955 "DOMAINDN": names.domaindn,
956 "SERVERDN": names.serverdn,
957 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
960 message("Setting up display specifiers")
961 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
962 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
963 check_all_substituted(display_specifiers_ldif)
964 samdb.add_ldif(display_specifiers_ldif)
966 message("Adding users container")
967 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
968 "DOMAINDN": names.domaindn})
969 message("Modifying users container")
970 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
971 "DOMAINDN": names.domaindn})
972 message("Adding computers container")
973 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
974 "DOMAINDN": names.domaindn})
975 message("Modifying computers container")
976 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
977 "DOMAINDN": names.domaindn})
978 message("Setting up sam.ldb data")
979 setup_add_ldif(samdb, setup_path("provision.ldif"), {
980 "DOMAINDN": names.domaindn,
981 "NETBIOSNAME": names.netbiosname,
982 "DEFAULTSITE": names.sitename,
983 "CONFIGDN": names.configdn,
984 "SERVERDN": names.serverdn,
985 "POLICYGUID_DC": policyguid_dc
988 if fill == FILL_FULL:
989 message("Setting up sam.ldb users and groups")
990 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
991 "DOMAINDN": names.domaindn,
992 "DOMAINSID": str(domainsid),
993 "CONFIGDN": names.configdn,
994 "ADMINPASS_B64": b64encode(adminpass),
995 "KRBTGTPASS_B64": b64encode(krbtgtpass),
998 if serverrole == "domain controller":
999 message("Setting up self join")
1000 setup_self_join(samdb, names=names, invocationid=invocationid,
1001 dnspass=dnspass,
1002 machinepass=machinepass,
1003 domainsid=domainsid, policyguid=policyguid,
1004 policyguid_dc=policyguid_dc,
1005 setup_path=setup_path,
1006 domainControllerFunctionality=domainControllerFunctionality)
1007 # add the NTDSGUID based SPNs
1008 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1009 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1010 expression="", scope=SCOPE_BASE)
1011 assert isinstance(names.ntdsguid, str)
1013 except:
1014 samdb.transaction_cancel()
1015 raise
1017 samdb.transaction_commit()
1018 return samdb
1021 FILL_FULL = "FULL"
1022 FILL_NT4SYNC = "NT4SYNC"
1023 FILL_DRS = "DRS"
1026 def provision(setup_dir, message, session_info,
1027 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1028 realm=None,
1029 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1030 serverdn=None,
1031 domain=None, hostname=None, hostip=None, hostip6=None,
1032 domainsid=None, adminpass=None, ldapadminpass=None,
1033 krbtgtpass=None, domainguid=None,
1034 policyguid=None, policyguid_dc=None, invocationid=None,
1035 machinepass=None,
1036 dnspass=None, root=None, nobody=None, users=None,
1037 wheel=None, backup=None, aci=None, serverrole=None,
1038 ldap_backend_extra_port=None, ldap_backend_type=None,
1039 sitename=None,
1040 ol_mmr_urls=None, ol_olc=None,
1041 setup_ds_path=None, slapd_path=None, nosync=False,
1042 ldap_dryrun_mode=False):
1043 """Provision samba4
1045 :note: caution, this wipes all existing data!
1048 def setup_path(file):
1049 return os.path.join(setup_dir, file)
1051 if domainsid is None:
1052 domainsid = security.random_sid()
1054 # create/adapt the group policy GUIDs
1055 if policyguid is None:
1056 policyguid = str(uuid.uuid4())
1057 policyguid = policyguid.upper()
1058 if policyguid_dc is None:
1059 policyguid_dc = str(uuid.uuid4())
1060 policyguid_dc = policyguid_dc.upper()
1062 if adminpass is None:
1063 adminpass = glue.generate_random_str(12)
1064 if krbtgtpass is None:
1065 krbtgtpass = glue.generate_random_str(12)
1066 if machinepass is None:
1067 machinepass = glue.generate_random_str(12)
1068 if dnspass is None:
1069 dnspass = glue.generate_random_str(12)
1070 if ldapadminpass is None:
1071 #Make a new, random password between Samba and it's LDAP server
1072 ldapadminpass=glue.generate_random_str(12)
1075 root_uid = findnss_uid([root or "root"])
1076 nobody_uid = findnss_uid([nobody or "nobody"])
1077 users_gid = findnss_gid([users or "users"])
1078 if wheel is None:
1079 wheel_gid = findnss_gid(["wheel", "adm"])
1080 else:
1081 wheel_gid = findnss_gid([wheel])
1083 if targetdir is not None:
1084 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1085 os.makedirs(os.path.join(targetdir, "etc"))
1086 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1087 elif smbconf is None:
1088 smbconf = param.default_path()
1090 # only install a new smb.conf if there isn't one there already
1091 if not os.path.exists(smbconf):
1092 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1093 targetdir)
1095 lp = param.LoadParm()
1096 lp.load(smbconf)
1098 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1099 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1100 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1101 serverdn=serverdn)
1103 paths = provision_paths_from_lp(lp, names.dnsdomain)
1105 if hostip is None:
1106 try:
1107 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1108 except socket.gaierror, (socket.EAI_NODATA, msg):
1109 hostip = None
1111 if hostip6 is None:
1112 try:
1113 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1114 except socket.gaierror, (socket.EAI_NODATA, msg):
1115 hostip6 = None
1117 if serverrole is None:
1118 serverrole = lp.get("server role")
1120 assert serverrole in ("domain controller", "member server", "standalone")
1121 if invocationid is None and serverrole == "domain controller":
1122 invocationid = str(uuid.uuid4())
1124 if not os.path.exists(paths.private_dir):
1125 os.mkdir(paths.private_dir)
1127 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1129 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1130 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1132 secrets_credentials = credentials
1133 provision_backend = None
1134 if ldap_backend_type:
1135 # We only support an LDAP backend over ldapi://
1137 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1138 lp=lp, credentials=credentials,
1139 names=names,
1140 message=message, hostname=hostname,
1141 root=root, schema=schema,
1142 ldap_backend_type=ldap_backend_type,
1143 ldapadminpass=ldapadminpass,
1144 ldap_backend_extra_port=ldap_backend_extra_port,
1145 ol_mmr_urls=ol_mmr_urls,
1146 slapd_path=slapd_path,
1147 setup_ds_path=setup_ds_path,
1148 ldap_dryrun_mode=ldap_dryrun_mode)
1150 # Now use the backend credentials to access the databases
1151 credentials = provision_backend.credentials
1152 secrets_credentials = provision_backend.adminCredentials
1153 ldapi_url = provision_backend.ldapi_uri
1155 # only install a new shares config db if there is none
1156 if not os.path.exists(paths.shareconf):
1157 message("Setting up share.ldb")
1158 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1159 credentials=credentials, lp=lp)
1160 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1163 message("Setting up secrets.ldb")
1164 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1165 session_info=session_info,
1166 credentials=secrets_credentials, lp=lp)
1168 message("Setting up the registry")
1169 setup_registry(paths.hklm, setup_path, session_info,
1170 lp=lp)
1172 message("Setting up idmap db")
1173 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1174 lp=lp)
1176 message("Setting up SAM db")
1177 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1178 credentials=credentials, lp=lp, names=names,
1179 message=message,
1180 domainsid=domainsid,
1181 schema=schema, domainguid=domainguid,
1182 policyguid=policyguid, policyguid_dc=policyguid_dc,
1183 fill=samdb_fill,
1184 adminpass=adminpass, krbtgtpass=krbtgtpass,
1185 invocationid=invocationid,
1186 machinepass=machinepass, dnspass=dnspass,
1187 serverrole=serverrole, ldap_backend=provision_backend)
1189 if serverrole == "domain controller":
1190 if paths.netlogon is None:
1191 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1192 message("Please either remove %s or see the template at %s" %
1193 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1194 assert(paths.netlogon is not None)
1196 if paths.sysvol is None:
1197 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1198 message("Please either remove %s or see the template at %s" %
1199 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1200 assert(paths.sysvol is not None)
1202 # Set up group policies (domain policy and domain controller policy)
1204 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1205 "{" + policyguid + "}")
1206 os.makedirs(policy_path, 0755)
1207 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1208 "[General]\r\nVersion=65544")
1209 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1210 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1212 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1213 "{" + policyguid_dc + "}")
1214 os.makedirs(policy_path_dc, 0755)
1215 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1216 "[General]\r\nVersion=2")
1217 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1218 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1220 if not os.path.isdir(paths.netlogon):
1221 os.makedirs(paths.netlogon, 0755)
1223 if samdb_fill == FILL_FULL:
1224 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1225 root_uid=root_uid, nobody_uid=nobody_uid,
1226 users_gid=users_gid, wheel_gid=wheel_gid)
1228 message("Setting up sam.ldb rootDSE marking as synchronized")
1229 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1231 # Only make a zone file on the first DC, it should be replicated with DNS replication
1232 if serverrole == "domain controller":
1233 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1234 credentials=credentials, lp=lp)
1235 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1236 realm=names.realm,
1237 netbiosname=names.netbiosname,
1238 domainsid=domainsid,
1239 keytab_path=paths.keytab, samdb_url=paths.samdb,
1240 dns_keytab_path=paths.dns_keytab,
1241 dnspass=dnspass, machinepass=machinepass,
1242 dnsdomain=names.dnsdomain)
1244 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1245 assert isinstance(domainguid, str)
1247 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1248 domaindn=names.domaindn, hostip=hostip,
1249 hostip6=hostip6, hostname=names.hostname,
1250 dnspass=dnspass, realm=names.realm,
1251 domainguid=domainguid, ntdsguid=names.ntdsguid)
1253 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1254 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1256 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1257 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1258 keytab_name=paths.dns_keytab)
1259 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1260 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1262 create_krb5_conf(paths.krb5conf, setup_path,
1263 dnsdomain=names.dnsdomain, hostname=names.hostname,
1264 realm=names.realm)
1265 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1268 if provision_backend is not None:
1269 if ldap_backend_type == "fedora-ds":
1270 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1272 # delete default SASL mappings
1273 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1275 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1276 for i in range (0, len(res)):
1277 dn = str(res[i]["dn"])
1278 ldapi_db.delete(dn)
1280 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1282 m = ldb.Message()
1283 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1285 m.dn = ldb.Dn(1, names.domaindn)
1286 ldapi_db.modify(m)
1288 m.dn = ldb.Dn(1, names.configdn)
1289 ldapi_db.modify(m)
1291 m.dn = ldb.Dn(1, names.schemadn)
1292 ldapi_db.modify(m)
1294 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1295 if provision_backend.slapd.poll() is None:
1296 #Kill the slapd
1297 if hasattr(provision_backend.slapd, "terminate"):
1298 provision_backend.slapd.terminate()
1299 else:
1300 # Older python versions don't have .terminate()
1301 import signal
1302 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1304 #and now wait for it to die
1305 provision_backend.slapd.communicate()
1307 # now display slapd_command_file.txt to show how slapd must be started next time
1308 message("Use later the following commandline to start slapd, then Samba:")
1309 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1310 message(slapd_command)
1311 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1313 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1314 "SLAPD_COMMAND" : slapd_command})
1317 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1318 ldapi_url)
1320 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1322 message("Once the above files are installed, your Samba4 server will be ready to use")
1323 message("Server Role: %s" % serverrole)
1324 message("Hostname: %s" % names.hostname)
1325 message("NetBIOS Domain: %s" % names.domain)
1326 message("DNS Domain: %s" % names.dnsdomain)
1327 message("DOMAIN SID: %s" % str(domainsid))
1328 if samdb_fill == FILL_FULL:
1329 message("Admin password: %s" % adminpass)
1330 if provision_backend:
1331 if provision_backend.credentials.get_bind_dn() is not None:
1332 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1333 else:
1334 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1336 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1338 result = ProvisionResult()
1339 result.domaindn = domaindn
1340 result.paths = paths
1341 result.lp = lp
1342 result.samdb = samdb
1343 return result
1347 def provision_become_dc(setup_dir=None,
1348 smbconf=None, targetdir=None, realm=None,
1349 rootdn=None, domaindn=None, schemadn=None,
1350 configdn=None, serverdn=None,
1351 domain=None, hostname=None, domainsid=None,
1352 adminpass=None, krbtgtpass=None, domainguid=None,
1353 policyguid=None, policyguid_dc=None, invocationid=None,
1354 machinepass=None,
1355 dnspass=None, root=None, nobody=None, users=None,
1356 wheel=None, backup=None, serverrole=None,
1357 ldap_backend=None, ldap_backend_type=None,
1358 sitename=None, debuglevel=1):
1360 def message(text):
1361 """print a message if quiet is not set."""
1362 print text
1364 glue.set_debug_level(debuglevel)
1366 return provision(setup_dir, message, system_session(), None,
1367 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1368 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1369 configdn=configdn, serverdn=serverdn, domain=domain,
1370 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1371 machinepass=machinepass, serverrole="domain controller",
1372 sitename=sitename)
1375 def setup_db_config(setup_path, dbdir):
1376 """Setup a Berkeley database.
1378 :param setup_path: Setup path function.
1379 :param dbdir: Database directory."""
1380 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1381 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1382 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1383 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1385 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1386 {"LDAPDBDIR": dbdir})
1388 class ProvisionBackend(object):
1389 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1390 names=None, message=None,
1391 hostname=None, root=None,
1392 schema=None, ldapadminpass=None,
1393 ldap_backend_type=None, ldap_backend_extra_port=None,
1394 ol_mmr_urls=None,
1395 setup_ds_path=None, slapd_path=None,
1396 nosync=False, ldap_dryrun_mode=False):
1397 """Provision an LDAP backend for samba4
1399 This works for OpenLDAP and Fedora DS
1402 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1404 if not os.path.isdir(paths.ldapdir):
1405 os.makedirs(paths.ldapdir, 0700)
1407 if ldap_backend_type == "existing":
1408 #Check to see that this 'existing' LDAP backend in fact exists
1409 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1410 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1411 expression="(objectClass=OpenLDAProotDSE)")
1413 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1414 # This caused them to be set into the long-term database later in the script.
1415 self.credentials = credentials
1416 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1417 return
1419 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1420 # if another instance of slapd is already running
1421 try:
1422 ldapi_db = Ldb(self.ldapi_uri)
1423 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1424 expression="(objectClass=OpenLDAProotDSE)");
1425 try:
1426 f = open(paths.slapdpid, "r")
1427 p = f.read()
1428 f.close()
1429 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1430 except:
1431 pass
1433 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1435 except LdbError, e:
1436 pass
1438 # Try to print helpful messages when the user has not specified the path to slapd
1439 if slapd_path is None:
1440 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1441 if not os.path.exists(slapd_path):
1442 message (slapd_path)
1443 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1445 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1446 try:
1447 os.unlink(schemadb_path)
1448 except OSError:
1449 pass
1452 # Put the LDIF of the schema into a database so we can search on
1453 # it to generate schema-dependent configurations in Fedora DS and
1454 # OpenLDAP
1455 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1456 schema.ldb.connect(schemadb_path)
1457 schema.ldb.transaction_start()
1459 # These bits of LDIF are supplied when the Schema object is created
1460 schema.ldb.add_ldif(schema.schema_dn_add)
1461 schema.ldb.modify_ldif(schema.schema_dn_modify)
1462 schema.ldb.add_ldif(schema.schema_data)
1463 schema.ldb.transaction_commit()
1465 self.credentials = Credentials()
1466 self.credentials.guess(lp)
1467 #Kerberos to an ldapi:// backend makes no sense
1468 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1470 self.adminCredentials = Credentials()
1471 self.adminCredentials.guess(lp)
1472 #Kerberos to an ldapi:// backend makes no sense
1473 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1475 self.ldap_backend_type = ldap_backend_type
1477 if ldap_backend_type == "fedora-ds":
1478 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1479 names=names, message=message,
1480 hostname=hostname,
1481 ldapadminpass=ldapadminpass, root=root,
1482 schema=schema,
1483 ldap_backend_extra_port=ldap_backend_extra_port,
1484 setup_ds_path=setup_ds_path,
1485 slapd_path=slapd_path,
1486 nosync=nosync,
1487 ldap_dryrun_mode=ldap_dryrun_mode)
1489 elif ldap_backend_type == "openldap":
1490 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1491 names=names, message=message,
1492 hostname=hostname,
1493 ldapadminpass=ldapadminpass, root=root,
1494 schema=schema,
1495 ldap_backend_extra_port=ldap_backend_extra_port,
1496 ol_mmr_urls=ol_mmr_urls,
1497 slapd_path=slapd_path,
1498 nosync=nosync,
1499 ldap_dryrun_mode=ldap_dryrun_mode)
1500 else:
1501 raise ProvisioningError("Unknown LDAP backend type selected")
1503 self.credentials.set_password(ldapadminpass)
1504 self.adminCredentials.set_username("samba-admin")
1505 self.adminCredentials.set_password(ldapadminpass)
1507 # Now start the slapd, so we can provision onto it. We keep the
1508 # subprocess context around, to kill this off at the successful
1509 # end of the script
1510 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1512 while self.slapd.poll() is None:
1513 # Wait until the socket appears
1514 try:
1515 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1516 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1517 expression="(objectClass=OpenLDAProotDSE)")
1518 # If we have got here, then we must have a valid connection to the LDAP server!
1519 return
1520 except LdbError, e:
1521 time.sleep(1)
1522 pass
1524 raise ProvisioningError("slapd died before we could make a connection to it")
1527 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1528 message=None,
1529 hostname=None, ldapadminpass=None, root=None,
1530 schema=None,
1531 ldap_backend_extra_port=None,
1532 ol_mmr_urls=None,
1533 slapd_path=None, nosync=False,
1534 ldap_dryrun_mode=False):
1536 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1537 nosync_config = ""
1538 if nosync:
1539 nosync_config = "dbnosync"
1541 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1542 refint_attributes = ""
1543 memberof_config = "# Generated from Samba4 schema\n"
1544 for att in lnkattr.keys():
1545 if lnkattr[att] is not None:
1546 refint_attributes = refint_attributes + " " + att
1548 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1549 { "MEMBER_ATTR" : att ,
1550 "MEMBEROF_ATTR" : lnkattr[att] })
1552 refint_config = read_and_sub_file(setup_path("refint.conf"),
1553 { "LINK_ATTRS" : refint_attributes})
1555 attrs = ["linkID", "lDAPDisplayName"]
1556 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1557 index_config = ""
1558 for i in range (0, len(res)):
1559 index_attr = res[i]["lDAPDisplayName"][0]
1560 if index_attr == "objectGUID":
1561 index_attr = "entryUUID"
1563 index_config += "index " + index_attr + " eq\n"
1565 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1566 mmr_on_config = ""
1567 mmr_replicator_acl = ""
1568 mmr_serverids_config = ""
1569 mmr_syncrepl_schema_config = ""
1570 mmr_syncrepl_config_config = ""
1571 mmr_syncrepl_user_config = ""
1574 if ol_mmr_urls is not None:
1575 # For now, make these equal
1576 mmr_pass = ldapadminpass
1578 url_list=filter(None,ol_mmr_urls.split(' '))
1579 if (len(url_list) == 1):
1580 url_list=filter(None,ol_mmr_urls.split(','))
1583 mmr_on_config = "MirrorMode On"
1584 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1585 serverid=0
1586 for url in url_list:
1587 serverid=serverid+1
1588 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1589 { "SERVERID" : str(serverid),
1590 "LDAPSERVER" : url })
1591 rid=serverid*10
1592 rid=rid+1
1593 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1594 { "RID" : str(rid),
1595 "MMRDN": names.schemadn,
1596 "LDAPSERVER" : url,
1597 "MMR_PASSWORD": mmr_pass})
1599 rid=rid+1
1600 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1601 { "RID" : str(rid),
1602 "MMRDN": names.configdn,
1603 "LDAPSERVER" : url,
1604 "MMR_PASSWORD": mmr_pass})
1606 rid=rid+1
1607 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1608 { "RID" : str(rid),
1609 "MMRDN": names.domaindn,
1610 "LDAPSERVER" : url,
1611 "MMR_PASSWORD": mmr_pass })
1612 # OpenLDAP cn=config initialisation
1613 olc_syncrepl_config = ""
1614 olc_mmr_config = ""
1615 # if mmr = yes, generate cn=config-replication directives
1616 # and olc_seed.lif for the other mmr-servers
1617 if ol_mmr_urls is not None:
1618 serverid=0
1619 olc_serverids_config = ""
1620 olc_syncrepl_seed_config = ""
1621 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1622 rid=1000
1623 for url in url_list:
1624 serverid=serverid+1
1625 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1626 { "SERVERID" : str(serverid),
1627 "LDAPSERVER" : url })
1629 rid=rid+1
1630 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1631 { "RID" : str(rid),
1632 "LDAPSERVER" : url,
1633 "MMR_PASSWORD": mmr_pass})
1635 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1636 { "RID" : str(rid),
1637 "LDAPSERVER" : url})
1639 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1640 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1641 "OLC_PW": ldapadminpass,
1642 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1643 # end olc
1645 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1646 {"DNSDOMAIN": names.dnsdomain,
1647 "LDAPDIR": paths.ldapdir,
1648 "DOMAINDN": names.domaindn,
1649 "CONFIGDN": names.configdn,
1650 "SCHEMADN": names.schemadn,
1651 "MEMBEROF_CONFIG": memberof_config,
1652 "MIRRORMODE": mmr_on_config,
1653 "REPLICATOR_ACL": mmr_replicator_acl,
1654 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1655 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1656 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1657 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1658 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1659 "OLC_MMR_CONFIG": olc_mmr_config,
1660 "REFINT_CONFIG": refint_config,
1661 "INDEX_CONFIG": index_config,
1662 "NOSYNC": nosync_config})
1664 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1665 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1666 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1668 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1669 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1671 setup_file(setup_path("cn=samba.ldif"),
1672 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1673 { "UUID": str(uuid.uuid4()),
1674 "LDAPTIME": timestring(int(time.time()))} )
1675 setup_file(setup_path("cn=samba-admin.ldif"),
1676 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1677 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1678 "UUID": str(uuid.uuid4()),
1679 "LDAPTIME": timestring(int(time.time()))} )
1681 if ol_mmr_urls is not None:
1682 setup_file(setup_path("cn=replicator.ldif"),
1683 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1684 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1685 "UUID": str(uuid.uuid4()),
1686 "LDAPTIME": timestring(int(time.time()))} )
1689 mapping = "schema-map-openldap-2.3"
1690 backend_schema = "backend-schema.schema"
1692 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1693 assert backend_schema_data is not None
1694 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1696 # now we generate the needed strings to start slapd automatically,
1697 # first ldapi_uri...
1698 if ldap_backend_extra_port is not None:
1699 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1700 # specified there as part of it's clue as to it's own name,
1701 # and not to replicate to itself
1702 if ol_mmr_urls is None:
1703 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1704 else:
1705 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1706 else:
1707 server_port_string = ""
1709 # Prepare the 'result' information - the commands to return in particular
1710 result.slapd_provision_command = [slapd_path]
1712 result.slapd_provision_command.append("-F" + paths.olcdir)
1714 result.slapd_provision_command.append("-h")
1716 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1717 result.slapd_command = list(result.slapd_provision_command)
1719 result.slapd_provision_command.append(result.ldapi_uri)
1720 result.slapd_provision_command.append("-d0")
1722 uris = result.ldapi_uri
1723 if server_port_string is not "":
1724 uris = uris + " " + server_port_string
1726 result.slapd_command.append(uris)
1728 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1729 result.credentials.set_username("samba-admin")
1731 # If we were just looking for crashes up to this point, it's a
1732 # good time to exit before we realise we don't have OpenLDAP on
1733 # this system
1734 if ldap_dryrun_mode:
1735 sys.exit(0)
1737 # Finally, convert the configuration into cn=config style!
1738 if not os.path.isdir(paths.olcdir):
1739 os.makedirs(paths.olcdir, 0770)
1741 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1743 # We can't do this, as OpenLDAP is strange. It gives an error
1744 # output to the above, but does the conversion sucessfully...
1746 # if retcode != 0:
1747 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1749 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1750 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1752 # Don't confuse the admin by leaving the slapd.conf around
1753 os.remove(paths.slapdconf)
1756 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1757 message=None,
1758 hostname=None, ldapadminpass=None, root=None,
1759 schema=None,
1760 ldap_backend_extra_port=None,
1761 setup_ds_path=None,
1762 slapd_path=None,
1763 nosync=False,
1764 ldap_dryrun_mode=False):
1766 if ldap_backend_extra_port is not None:
1767 serverport = "ServerPort=%d" % ldap_backend_extra_port
1768 else:
1769 serverport = ""
1771 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1772 {"ROOT": root,
1773 "HOSTNAME": hostname,
1774 "DNSDOMAIN": names.dnsdomain,
1775 "LDAPDIR": paths.ldapdir,
1776 "DOMAINDN": names.domaindn,
1777 "LDAPMANAGERDN": names.ldapmanagerdn,
1778 "LDAPMANAGERPASS": ldapadminpass,
1779 "SERVERPORT": serverport})
1781 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1782 {"CONFIGDN": names.configdn,
1783 "SCHEMADN": names.schemadn,
1784 "SAMBADN": names.sambadn,
1787 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1788 {"SAMBADN": names.sambadn,
1791 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1792 {"SAMBADN": names.sambadn,
1793 "LDAPADMINPASS": ldapadminpass
1796 mapping = "schema-map-fedora-ds-1.0"
1797 backend_schema = "99_ad.ldif"
1799 # Build a schema file in Fedora DS format
1800 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1801 assert backend_schema_data is not None
1802 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1804 result.credentials.set_bind_dn(names.ldapmanagerdn)
1806 # Destory the target directory, or else setup-ds.pl will complain
1807 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1808 shutil.rmtree(fedora_ds_dir, True)
1810 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1811 #In the 'provision' command line, stay in the foreground so we can easily kill it
1812 result.slapd_provision_command.append("-d0")
1814 #the command for the final run is the normal script
1815 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1817 # If we were just looking for crashes up to this point, it's a
1818 # good time to exit before we realise we don't have Fedora DS on
1819 if ldap_dryrun_mode:
1820 sys.exit(0)
1822 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1823 if setup_ds_path is None:
1824 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\"!")
1825 if not os.path.exists(setup_ds_path):
1826 message (setup_ds_path)
1827 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1829 # Run the Fedora DS setup utility
1830 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1831 if retcode != 0:
1832 raise ProvisioningError("setup-ds failed")
1834 # Load samba-admin
1835 retcode = subprocess.call([
1836 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1837 close_fds=True, shell=False)
1838 if retcode != 0:
1839 raise("ldib2db failed")
1841 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1842 """Create a PHP LDAP admin configuration file.
1844 :param path: Path to write the configuration to.
1845 :param setup_path: Function to generate setup paths.
1847 setup_file(setup_path("phpldapadmin-config.php"), path,
1848 {"S4_LDAPI_URI": ldapi_uri})
1851 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1852 hostip, hostip6, hostname, dnspass, realm, domainguid,
1853 ntdsguid):
1854 """Write out a DNS zone file, from the info in the current database.
1856 :param path: Path of the new zone file.
1857 :param setup_path: Setup path function.
1858 :param dnsdomain: DNS Domain name
1859 :param domaindn: DN of the Domain
1860 :param hostip: Local IPv4 IP
1861 :param hostip6: Local IPv6 IP
1862 :param hostname: Local hostname
1863 :param dnspass: Password for DNS
1864 :param realm: Realm name
1865 :param domainguid: GUID of the domain.
1866 :param ntdsguid: GUID of the hosts nTDSDSA record.
1868 assert isinstance(domainguid, str)
1870 if hostip6 is not None:
1871 hostip6_base_line = " IN AAAA " + hostip6
1872 hostip6_host_line = hostname + " IN AAAA " + hostip6
1873 else:
1874 hostip6_base_line = ""
1875 hostip6_host_line = ""
1877 if hostip is not None:
1878 hostip_base_line = " IN A " + hostip
1879 hostip_host_line = hostname + " IN A " + hostip
1880 else:
1881 hostip_base_line = ""
1882 hostip_host_line = ""
1884 setup_file(setup_path("provision.zone"), path, {
1885 "DNSPASS_B64": b64encode(dnspass),
1886 "HOSTNAME": hostname,
1887 "DNSDOMAIN": dnsdomain,
1888 "REALM": realm,
1889 "HOSTIP_BASE_LINE": hostip_base_line,
1890 "HOSTIP_HOST_LINE": hostip_host_line,
1891 "DOMAINGUID": domainguid,
1892 "DATESTRING": time.strftime("%Y%m%d%H"),
1893 "DEFAULTSITE": DEFAULTSITE,
1894 "NTDSGUID": ntdsguid,
1895 "HOSTIP6_BASE_LINE": hostip6_base_line,
1896 "HOSTIP6_HOST_LINE": hostip6_host_line,
1900 def create_named_conf(path, setup_path, realm, dnsdomain,
1901 private_dir):
1902 """Write out a file containing zone statements suitable for inclusion in a
1903 named.conf file (including GSS-TSIG configuration).
1905 :param path: Path of the new named.conf file.
1906 :param setup_path: Setup path function.
1907 :param realm: Realm name
1908 :param dnsdomain: DNS Domain name
1909 :param private_dir: Path to private directory
1910 :param keytab_name: File name of DNS keytab file
1913 setup_file(setup_path("named.conf"), path, {
1914 "DNSDOMAIN": dnsdomain,
1915 "REALM": realm,
1916 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1917 "PRIVATE_DIR": private_dir
1920 def create_named_txt(path, setup_path, realm, dnsdomain,
1921 private_dir, keytab_name):
1922 """Write out a file containing zone statements suitable for inclusion in a
1923 named.conf file (including GSS-TSIG configuration).
1925 :param path: Path of the new named.conf file.
1926 :param setup_path: Setup path function.
1927 :param realm: Realm name
1928 :param dnsdomain: DNS Domain name
1929 :param private_dir: Path to private directory
1930 :param keytab_name: File name of DNS keytab file
1933 setup_file(setup_path("named.txt"), path, {
1934 "DNSDOMAIN": dnsdomain,
1935 "REALM": realm,
1936 "DNS_KEYTAB": keytab_name,
1937 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1938 "PRIVATE_DIR": private_dir
1941 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1942 """Write out a file containing zone statements suitable for inclusion in a
1943 named.conf file (including GSS-TSIG configuration).
1945 :param path: Path of the new named.conf file.
1946 :param setup_path: Setup path function.
1947 :param dnsdomain: DNS Domain name
1948 :param hostname: Local hostname
1949 :param realm: Realm name
1952 setup_file(setup_path("krb5.conf"), path, {
1953 "DNSDOMAIN": dnsdomain,
1954 "HOSTNAME": hostname,
1955 "REALM": realm,