s4:group policies - upcase directory names of default group policies
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision.py
blobe21a3cbee17e26730fd1ddc491c18aa89c297994
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, check_all_substituted, \
46 DS_BEHAVIOR_WIN2008
47 from samba.samdb import SamDB
48 from samba.idmap import IDmapDB
49 from samba.dcerpc import security
50 import urllib
51 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
52 from ms_schema import read_ms_schema
53 from ms_display_specifiers import read_ms_ldif
54 from signal import SIGTERM
56 __docformat__ = "restructuredText"
59 class ProvisioningError(ValueError):
60 pass
63 def find_setup_dir():
64 """Find the setup directory used by provision."""
65 dirname = os.path.dirname(__file__)
66 if "/site-packages/" in dirname:
67 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
68 for suffix in ["share/setup", "share/samba/setup", "setup"]:
69 ret = os.path.join(prefix, suffix)
70 if os.path.isdir(ret):
71 return ret
72 # In source tree
73 ret = os.path.join(dirname, "../../../setup")
74 if os.path.isdir(ret):
75 return ret
76 raise Exception("Unable to find setup directory.")
79 DEFAULTSITE = "Default-First-Site-Name"
81 class InvalidNetbiosName(Exception):
82 """A specified name was not a valid NetBIOS name."""
83 def __init__(self, name):
84 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
87 class ProvisionPaths(object):
88 def __init__(self):
89 self.shareconf = None
90 self.hklm = None
91 self.hkcu = None
92 self.hkcr = None
93 self.hku = None
94 self.hkpd = None
95 self.hkpt = None
96 self.samdb = None
97 self.idmapdb = None
98 self.secrets = None
99 self.keytab = None
100 self.dns_keytab = None
101 self.dns = None
102 self.winsdb = None
103 self.private_dir = None
104 self.ldapdir = None
105 self.slapdconf = None
106 self.modulesconf = None
107 self.memberofconf = None
108 self.fedoradsinf = None
109 self.fedoradspartitions = None
110 self.fedoradssasl = None
111 self.olmmron = None
112 self.olmmrserveridsconf = None
113 self.olmmrsyncreplconf = None
114 self.olcdir = None
115 self.olslapd = None
116 self.olcseedldif = None
119 class ProvisionNames(object):
120 def __init__(self):
121 self.rootdn = None
122 self.domaindn = None
123 self.configdn = None
124 self.schemadn = None
125 self.sambadn = None
126 self.ldapmanagerdn = None
127 self.dnsdomain = None
128 self.realm = None
129 self.netbiosname = None
130 self.domain = None
131 self.hostname = None
132 self.sitename = None
133 self.smbconf = None
136 class ProvisionResult(object):
137 def __init__(self):
138 self.paths = None
139 self.domaindn = None
140 self.lp = None
141 self.samdb = None
143 class Schema(object):
144 def __init__(self, setup_path, schemadn=None,
145 serverdn=None, sambadn=None, ldap_backend_type=None):
146 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
148 :param samdb: Load a schema into a SamDB.
149 :param setup_path: Setup path function.
150 :param schemadn: DN of the schema
151 :param serverdn: DN of the server
153 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
156 self.ldb = Ldb()
157 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
158 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
159 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
160 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
161 check_all_substituted(self.schema_data)
163 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
164 {"SCHEMADN": schemadn,
165 "SERVERDN": serverdn,
167 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
168 {"SCHEMADN": schemadn
171 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
172 prefixmap = b64encode(prefixmap)
174 # We don't actually add this ldif, just parse it
175 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
176 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
179 # Return a hash with the forward attribute as a key and the back as the value
180 def get_linked_attributes(schemadn,schemaldb):
181 attrs = ["linkID", "lDAPDisplayName"]
182 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)
183 attributes = {}
184 for i in range (0, len(res)):
185 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
186 target = schemaldb.searchone(basedn=schemadn,
187 expression=expression,
188 attribute="lDAPDisplayName",
189 scope=SCOPE_SUBTREE)
190 if target is not None:
191 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
193 return attributes
195 def get_dnsyntax_attributes(schemadn,schemaldb):
196 attrs = ["linkID", "lDAPDisplayName"]
197 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
198 attributes = []
199 for i in range (0, len(res)):
200 attributes.append(str(res[i]["lDAPDisplayName"]))
202 return attributes
205 def check_install(lp, session_info, credentials):
206 """Check whether the current install seems ok.
208 :param lp: Loadparm context
209 :param session_info: Session information
210 :param credentials: Credentials
212 if lp.get("realm") == "":
213 raise Exception("Realm empty")
214 ldb = Ldb(lp.get("sam database"), session_info=session_info,
215 credentials=credentials, lp=lp)
216 if len(ldb.search("(cn=Administrator)")) != 1:
217 raise ProvisioningError("No administrator account found")
220 def findnss(nssfn, names):
221 """Find a user or group from a list of possibilities.
223 :param nssfn: NSS Function to try (should raise KeyError if not found)
224 :param names: Names to check.
225 :return: Value return by first names list.
227 for name in names:
228 try:
229 return nssfn(name)
230 except KeyError:
231 pass
232 raise KeyError("Unable to find user/group %r" % names)
235 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
236 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
239 def read_and_sub_file(file, subst_vars):
240 """Read a file and sub in variables found in it
242 :param file: File to be read (typically from setup directory)
243 param subst_vars: Optional variables to subsitute in the file.
245 data = open(file, 'r').read()
246 if subst_vars is not None:
247 data = substitute_var(data, subst_vars)
248 check_all_substituted(data)
249 return data
252 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
253 """Setup a ldb in the private dir.
255 :param ldb: LDB file to import data into
256 :param ldif_path: Path of the LDIF file to load
257 :param subst_vars: Optional variables to subsitute in LDIF.
259 assert isinstance(ldif_path, str)
261 data = read_and_sub_file(ldif_path, subst_vars)
262 ldb.add_ldif(data)
265 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
266 """Modify a ldb in the private dir.
268 :param ldb: LDB object.
269 :param ldif_path: LDIF file path.
270 :param subst_vars: Optional dictionary with substitution variables.
272 data = read_and_sub_file(ldif_path, subst_vars)
274 ldb.modify_ldif(data)
277 def setup_ldb(ldb, ldif_path, subst_vars):
278 """Import a LDIF a file into a LDB handle, optionally substituting variables.
280 :note: Either all LDIF data will be added or none (using transactions).
282 :param ldb: LDB file to import into.
283 :param ldif_path: Path to the LDIF file.
284 :param subst_vars: Dictionary with substitution variables.
286 assert ldb is not None
287 ldb.transaction_start()
288 try:
289 setup_add_ldif(ldb, ldif_path, subst_vars)
290 except:
291 ldb.transaction_cancel()
292 raise
293 ldb.transaction_commit()
296 def setup_file(template, fname, subst_vars):
297 """Setup a file in the private dir.
299 :param template: Path of the template file.
300 :param fname: Path of the file to create.
301 :param subst_vars: Substitution variables.
303 f = fname
305 if os.path.exists(f):
306 os.unlink(f)
308 data = read_and_sub_file(template, subst_vars)
309 open(f, 'w').write(data)
312 def provision_paths_from_lp(lp, dnsdomain):
313 """Set the default paths for provisioning.
315 :param lp: Loadparm context.
316 :param dnsdomain: DNS Domain name
318 paths = ProvisionPaths()
319 paths.private_dir = lp.get("private dir")
320 paths.keytab = "secrets.keytab"
321 paths.dns_keytab = "dns.keytab"
323 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
324 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
325 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
326 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
327 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
328 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
329 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
330 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
331 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
332 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
333 paths.phpldapadminconfig = os.path.join(paths.private_dir,
334 "phpldapadmin-config.php")
335 paths.ldapdir = os.path.join(paths.private_dir,
336 "ldap")
337 paths.slapdconf = os.path.join(paths.ldapdir,
338 "slapd.conf")
339 paths.slapdpid = os.path.join(paths.ldapdir,
340 "slapd.pid")
341 paths.modulesconf = os.path.join(paths.ldapdir,
342 "modules.conf")
343 paths.memberofconf = os.path.join(paths.ldapdir,
344 "memberof.conf")
345 paths.fedoradsinf = os.path.join(paths.ldapdir,
346 "fedorads.inf")
347 paths.fedoradspartitions = os.path.join(paths.ldapdir,
348 "fedorads-partitions.ldif")
349 paths.fedoradssasl = os.path.join(paths.ldapdir,
350 "fedorads-sasl.ldif")
351 paths.fedoradssamba = os.path.join(paths.ldapdir,
352 "fedorads-samba.ldif")
353 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
354 "mmr_serverids.conf")
355 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
356 "mmr_syncrepl.conf")
357 paths.olcdir = os.path.join(paths.ldapdir,
358 "slapd.d")
359 paths.olcseedldif = os.path.join(paths.ldapdir,
360 "olc_seed.ldif")
361 paths.hklm = "hklm.ldb"
362 paths.hkcr = "hkcr.ldb"
363 paths.hkcu = "hkcu.ldb"
364 paths.hku = "hku.ldb"
365 paths.hkpd = "hkpd.ldb"
366 paths.hkpt = "hkpt.ldb"
368 paths.sysvol = lp.get("path", "sysvol")
370 paths.netlogon = lp.get("path", "netlogon")
372 paths.smbconf = lp.configfile
374 return paths
377 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
378 serverrole=None, rootdn=None, domaindn=None, configdn=None,
379 schemadn=None, serverdn=None, sitename=None, sambadn=None):
380 """Guess configuration settings to use."""
382 if hostname is None:
383 hostname = socket.gethostname().split(".")[0].lower()
385 netbiosname = hostname.upper()
386 if not valid_netbios_name(netbiosname):
387 raise InvalidNetbiosName(netbiosname)
389 hostname = hostname.lower()
391 if dnsdomain is None:
392 dnsdomain = lp.get("realm")
394 if serverrole is None:
395 serverrole = lp.get("server role")
397 assert dnsdomain is not None
398 realm = dnsdomain.upper()
400 if lp.get("realm").upper() != realm:
401 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
402 (lp.get("realm"), lp.configfile, realm))
404 dnsdomain = dnsdomain.lower()
406 if serverrole == "domain controller":
407 if domain is None:
408 domain = lp.get("workgroup")
409 if domaindn is None:
410 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
411 if lp.get("workgroup").upper() != domain.upper():
412 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
413 lp.get("workgroup"), domain)
414 else:
415 domain = netbiosname
416 if domaindn is None:
417 domaindn = "CN=" + netbiosname
419 assert domain is not None
420 domain = domain.upper()
421 if not valid_netbios_name(domain):
422 raise InvalidNetbiosName(domain)
424 if rootdn is None:
425 rootdn = domaindn
427 if configdn is None:
428 configdn = "CN=Configuration," + rootdn
429 if schemadn is None:
430 schemadn = "CN=Schema," + configdn
431 if sambadn is None:
432 sambadn = "CN=Samba"
434 if sitename is None:
435 sitename=DEFAULTSITE
437 names = ProvisionNames()
438 names.rootdn = rootdn
439 names.domaindn = domaindn
440 names.configdn = configdn
441 names.schemadn = schemadn
442 names.sambadn = sambadn
443 names.ldapmanagerdn = "CN=Manager," + rootdn
444 names.dnsdomain = dnsdomain
445 names.domain = domain
446 names.realm = realm
447 names.netbiosname = netbiosname
448 names.hostname = hostname
449 names.sitename = sitename
450 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
452 return names
455 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
456 targetdir):
457 """Create a new smb.conf file based on a couple of basic settings.
459 assert smbconf is not None
460 if hostname is None:
461 hostname = socket.gethostname().split(".")[0].lower()
463 if serverrole is None:
464 serverrole = "standalone"
466 assert serverrole in ("domain controller", "member server", "standalone")
467 if serverrole == "domain controller":
468 smbconfsuffix = "dc"
469 elif serverrole == "member server":
470 smbconfsuffix = "member"
471 elif serverrole == "standalone":
472 smbconfsuffix = "standalone"
474 assert domain is not None
475 assert realm is not None
477 default_lp = param.LoadParm()
478 #Load non-existant file
479 if os.path.exists(smbconf):
480 default_lp.load(smbconf)
482 if targetdir is not None:
483 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
484 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
486 default_lp.set("lock dir", os.path.abspath(targetdir))
487 else:
488 privatedir_line = ""
489 lockdir_line = ""
491 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
492 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
494 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
495 smbconf, {
496 "HOSTNAME": hostname,
497 "DOMAIN": domain,
498 "REALM": realm,
499 "SERVERROLE": serverrole,
500 "NETLOGONPATH": netlogon,
501 "SYSVOLPATH": sysvol,
502 "PRIVATEDIR_LINE": privatedir_line,
503 "LOCKDIR_LINE": lockdir_line
507 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
508 users_gid, wheel_gid):
509 """setup reasonable name mappings for sam names to unix names.
511 :param samdb: SamDB object.
512 :param idmap: IDmap db object.
513 :param sid: The domain sid.
514 :param domaindn: The domain DN.
515 :param root_uid: uid of the UNIX root user.
516 :param nobody_uid: uid of the UNIX nobody user.
517 :param users_gid: gid of the UNIX users group.
518 :param wheel_gid: gid of the UNIX wheel group."""
520 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
521 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
523 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
524 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
526 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
527 credentials, names,
528 serverrole, ldap_backend=None,
529 erase=False):
530 """Setup the partitions for the SAM database.
532 Alternatively, provision() may call this, and then populate the database.
534 :note: This will wipe the Sam Database!
536 :note: This function always removes the local SAM LDB file. The erase
537 parameter controls whether to erase the existing data, which
538 may not be stored locally but in LDAP.
540 assert session_info is not None
542 # We use options=["modules:"] to stop the modules loading - we
543 # just want to wipe and re-initialise the database, not start it up
545 try:
546 samdb = Ldb(url=samdb_path, session_info=session_info,
547 credentials=credentials, lp=lp, options=["modules:"])
548 # Wipes the database
549 samdb.erase_except_schema_controlled()
550 except LdbError:
551 os.unlink(samdb_path)
552 samdb = Ldb(url=samdb_path, session_info=session_info,
553 credentials=credentials, lp=lp, options=["modules:"])
554 # Wipes the database
555 samdb.erase_except_schema_controlled()
558 #Add modules to the list to activate them by default
559 #beware often order is important
561 # Some Known ordering constraints:
562 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
563 # - objectclass must be before password_hash, because password_hash checks
564 # that the objectclass is of type person (filled in by objectclass
565 # module when expanding the objectclass list)
566 # - partition must be last
567 # - each partition has its own module list then
568 modules_list = ["rootdse",
569 "paged_results",
570 "ranged_results",
571 "anr",
572 "server_sort",
573 "asq",
574 "extended_dn_store",
575 "extended_dn_in",
576 "rdn_name",
577 "objectclass",
578 "samldb",
579 "password_hash",
580 "operational",
581 "kludge_acl"]
582 tdb_modules_list = [
583 "subtree_rename",
584 "subtree_delete",
585 "linked_attributes",
586 "extended_dn_out_ldb"]
587 modules_list2 = ["show_deleted",
588 "partition"]
590 domaindn_ldb = "users.ldb"
591 configdn_ldb = "configuration.ldb"
592 schemadn_ldb = "schema.ldb"
593 if ldap_backend is not None:
594 domaindn_ldb = ldap_backend.ldapi_uri
595 configdn_ldb = ldap_backend.ldapi_uri
596 schemadn_ldb = ldap_backend.ldapi_uri
598 if ldap_backend.ldap_backend_type == "fedora-ds":
599 backend_modules = ["nsuniqueid", "paged_searches"]
600 # We can handle linked attributes here, as we don't have directory-side subtree operations
601 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
602 elif ldap_backend.ldap_backend_type == "openldap":
603 backend_modules = ["entryuuid", "paged_searches"]
604 # OpenLDAP handles subtree renames, so we don't want to do any of these things
605 tdb_modules_list = ["extended_dn_out_dereference"]
607 elif serverrole == "domain controller":
608 tdb_modules_list.insert(0, "repl_meta_data")
609 backend_modules = []
610 else:
611 backend_modules = ["objectguid"]
613 if tdb_modules_list is None:
614 tdb_modules_list_as_string = ""
615 else:
616 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
618 samdb.transaction_start()
619 try:
620 message("Setting up sam.ldb partitions and settings")
621 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
622 "SCHEMADN": names.schemadn,
623 "SCHEMADN_LDB": schemadn_ldb,
624 "SCHEMADN_MOD2": ",objectguid",
625 "CONFIGDN": names.configdn,
626 "CONFIGDN_LDB": configdn_ldb,
627 "DOMAINDN": names.domaindn,
628 "DOMAINDN_LDB": domaindn_ldb,
629 "SCHEMADN_MOD": "schema_fsmo,instancetype",
630 "CONFIGDN_MOD": "naming_fsmo,instancetype",
631 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
632 "MODULES_LIST": ",".join(modules_list),
633 "TDB_MODULES_LIST": tdb_modules_list_as_string,
634 "MODULES_LIST2": ",".join(modules_list2),
635 "BACKEND_MOD": ",".join(backend_modules),
638 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
640 message("Setting up sam.ldb rootDSE")
641 setup_samdb_rootdse(samdb, setup_path, names)
643 except:
644 samdb.transaction_cancel()
645 raise
647 samdb.transaction_commit()
651 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
652 netbiosname, domainsid, keytab_path, samdb_url,
653 dns_keytab_path, dnspass, machinepass):
654 """Add DC-specific bits to a secrets database.
656 :param secretsdb: Ldb Handle to the secrets database
657 :param setup_path: Setup path function
658 :param machinepass: Machine password
660 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
661 "MACHINEPASS_B64": b64encode(machinepass),
662 "DOMAIN": domain,
663 "REALM": realm,
664 "DNSDOMAIN": dnsdomain,
665 "DOMAINSID": str(domainsid),
666 "SECRETS_KEYTAB": keytab_path,
667 "NETBIOSNAME": netbiosname,
668 "SAM_LDB": samdb_url,
669 "DNS_KEYTAB": dns_keytab_path,
670 "DNSPASS_B64": b64encode(dnspass),
674 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
675 """Setup the secrets database.
677 :param path: Path to the secrets database.
678 :param setup_path: Get the path to a setup file.
679 :param session_info: Session info.
680 :param credentials: Credentials
681 :param lp: Loadparm context
682 :return: LDB handle for the created secrets database
684 if os.path.exists(path):
685 os.unlink(path)
686 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
687 lp=lp)
688 secrets_ldb.erase()
689 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
690 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
691 lp=lp)
692 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
694 if credentials is not None and credentials.authentication_requested():
695 if credentials.get_bind_dn() is not None:
696 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
697 "LDAPMANAGERDN": credentials.get_bind_dn(),
698 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
700 else:
701 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
702 "LDAPADMINUSER": credentials.get_username(),
703 "LDAPADMINREALM": credentials.get_realm(),
704 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
707 return secrets_ldb
709 def setup_registry(path, setup_path, session_info, lp):
710 """Setup the registry.
712 :param path: Path to the registry database
713 :param setup_path: Function that returns the path to a setup.
714 :param session_info: Session information
715 :param credentials: Credentials
716 :param lp: Loadparm context
718 reg = registry.Registry()
719 hive = registry.open_ldb(path, session_info=session_info,
720 lp_ctx=lp)
721 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
722 provision_reg = setup_path("provision.reg")
723 assert os.path.exists(provision_reg)
724 reg.diff_apply(provision_reg)
727 def setup_idmapdb(path, setup_path, session_info, lp):
728 """Setup the idmap database.
730 :param path: path to the idmap database
731 :param setup_path: Function that returns a path to a setup file
732 :param session_info: Session information
733 :param credentials: Credentials
734 :param lp: Loadparm context
736 if os.path.exists(path):
737 os.unlink(path)
739 idmap_ldb = IDmapDB(path, session_info=session_info,
740 lp=lp)
742 idmap_ldb.erase()
743 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
744 return idmap_ldb
747 def setup_samdb_rootdse(samdb, setup_path, names):
748 """Setup the SamDB rootdse.
750 :param samdb: Sam Database handle
751 :param setup_path: Obtain setup path
753 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
754 "SCHEMADN": names.schemadn,
755 "NETBIOSNAME": names.netbiosname,
756 "DNSDOMAIN": names.dnsdomain,
757 "REALM": names.realm,
758 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
759 "DOMAINDN": names.domaindn,
760 "ROOTDN": names.rootdn,
761 "CONFIGDN": names.configdn,
762 "SERVERDN": names.serverdn,
766 def setup_self_join(samdb, names,
767 machinepass, dnspass,
768 domainsid, invocationid, setup_path,
769 policyguid, policyguid_dc, domainControllerFunctionality):
770 """Join a host to its own domain."""
771 assert isinstance(invocationid, str)
772 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
773 "CONFIGDN": names.configdn,
774 "SCHEMADN": names.schemadn,
775 "DOMAINDN": names.domaindn,
776 "SERVERDN": names.serverdn,
777 "INVOCATIONID": invocationid,
778 "NETBIOSNAME": names.netbiosname,
779 "DEFAULTSITE": names.sitename,
780 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
781 "MACHINEPASS_B64": b64encode(machinepass),
782 "DNSPASS_B64": b64encode(dnspass),
783 "REALM": names.realm,
784 "DOMAIN": names.domain,
785 "DNSDOMAIN": names.dnsdomain,
786 "SAMBA_VERSION_STRING": version,
787 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
789 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
790 "POLICYGUID": policyguid,
791 "POLICYGUID_DC": policyguid_dc,
792 "DNSDOMAIN": names.dnsdomain,
793 "DOMAINSID": str(domainsid),
794 "DOMAINDN": names.domaindn})
796 # add the NTDSGUID based SPNs
797 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
798 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
799 expression="", scope=SCOPE_BASE)
800 assert isinstance(names.ntdsguid, str)
802 # Setup fSMORoleOwner entries to point at the newly created DC entry
803 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
804 "DOMAIN": names.domain,
805 "DNSDOMAIN": names.dnsdomain,
806 "DOMAINDN": names.domaindn,
807 "CONFIGDN": names.configdn,
808 "SCHEMADN": names.schemadn,
809 "DEFAULTSITE": names.sitename,
810 "SERVERDN": names.serverdn,
811 "NETBIOSNAME": names.netbiosname,
812 "NTDSGUID": names.ntdsguid
816 def setup_samdb(path, setup_path, session_info, credentials, lp,
817 names, message,
818 domainsid, domainguid, policyguid, policyguid_dc,
819 fill, adminpass, krbtgtpass,
820 machinepass, invocationid, dnspass,
821 serverrole, schema=None, ldap_backend=None):
822 """Setup a complete SAM Database.
824 :note: This will wipe the main SAM database file!
827 domainFunctionality = DS_BEHAVIOR_WIN2008
828 forestFunctionality = DS_BEHAVIOR_WIN2008
829 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
831 # Also wipes the database
832 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
833 credentials=credentials, session_info=session_info,
834 names=names,
835 ldap_backend=ldap_backend, serverrole=serverrole)
837 if (schema == None):
838 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
839 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
841 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
842 samdb = Ldb(session_info=session_info,
843 credentials=credentials, lp=lp)
845 message("Pre-loading the Samba 4 and AD schema")
847 # Load the schema from the one we computed earlier
848 samdb.set_schema_from_ldb(schema.ldb)
850 # And now we can connect to the DB - the schema won't be loaded from the DB
851 samdb.connect(path)
853 # Load @OPTIONS
854 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
856 if fill == FILL_DRS:
857 return samdb
859 samdb.transaction_start()
860 try:
861 message("Erasing data from partitions")
862 # Load the schema (again). This time it will force a reindex,
863 # and will therefore make the erase_partitions() below
864 # computationally sane
865 samdb.set_schema_from_ldb(schema.ldb)
866 samdb.erase_partitions()
868 # Set the domain functionality levels onto the database.
869 # Various module (the password_hash module in particular) need
870 # to know what level of AD we are emulating.
872 # These will be fixed into the database via the database
873 # modifictions below, but we need them set from the start.
874 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
875 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
876 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
878 samdb.set_domain_sid(str(domainsid))
879 if serverrole == "domain controller":
880 samdb.set_invocation_id(invocationid)
882 message("Adding DomainDN: %s" % names.domaindn)
883 if serverrole == "domain controller":
884 domain_oc = "domainDNS"
885 else:
886 domain_oc = "samba4LocalDomain"
888 #impersonate domain admin
889 admin_session_info = admin_session(lp, str(domainsid))
890 samdb.set_session_info(admin_session_info)
892 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
893 "DOMAINDN": names.domaindn,
894 "DOMAIN_OC": domain_oc
897 message("Modifying DomainDN: " + names.domaindn + "")
898 if domainguid is not None:
899 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
900 else:
901 domainguid_mod = ""
903 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
904 "LDAPTIME": timestring(int(time.time())),
905 "DOMAINSID": str(domainsid),
906 "SCHEMADN": names.schemadn,
907 "NETBIOSNAME": names.netbiosname,
908 "DEFAULTSITE": names.sitename,
909 "CONFIGDN": names.configdn,
910 "SERVERDN": names.serverdn,
911 "POLICYGUID": policyguid,
912 "DOMAINDN": names.domaindn,
913 "DOMAINGUID_MOD": domainguid_mod,
914 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
917 message("Adding configuration container")
918 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
919 "CONFIGDN": names.configdn,
921 message("Modifying configuration container")
922 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
923 "CONFIGDN": names.configdn,
924 "SCHEMADN": names.schemadn,
927 # The LDIF here was created when the Schema object was constructed
928 message("Setting up sam.ldb schema")
929 samdb.add_ldif(schema.schema_dn_add)
930 samdb.modify_ldif(schema.schema_dn_modify)
931 samdb.write_prefixes_from_schema()
932 samdb.add_ldif(schema.schema_data)
933 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
934 {"SCHEMADN": names.schemadn})
936 message("Setting up sam.ldb configuration data")
937 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
938 "CONFIGDN": names.configdn,
939 "NETBIOSNAME": names.netbiosname,
940 "DEFAULTSITE": names.sitename,
941 "DNSDOMAIN": names.dnsdomain,
942 "DOMAIN": names.domain,
943 "SCHEMADN": names.schemadn,
944 "DOMAINDN": names.domaindn,
945 "SERVERDN": names.serverdn,
946 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
949 message("Setting up display specifiers")
950 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
951 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
952 check_all_substituted(display_specifiers_ldif)
953 samdb.add_ldif(display_specifiers_ldif)
955 message("Adding users container")
956 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
957 "DOMAINDN": names.domaindn})
958 message("Modifying users container")
959 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
960 "DOMAINDN": names.domaindn})
961 message("Adding computers container")
962 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
963 "DOMAINDN": names.domaindn})
964 message("Modifying computers container")
965 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
966 "DOMAINDN": names.domaindn})
967 message("Setting up sam.ldb data")
968 setup_add_ldif(samdb, setup_path("provision.ldif"), {
969 "DOMAINDN": names.domaindn,
970 "NETBIOSNAME": names.netbiosname,
971 "DEFAULTSITE": names.sitename,
972 "CONFIGDN": names.configdn,
973 "SERVERDN": names.serverdn,
974 "POLICYGUID_DC": policyguid_dc
977 if fill == FILL_FULL:
978 message("Setting up sam.ldb users and groups")
979 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
980 "DOMAINDN": names.domaindn,
981 "DOMAINSID": str(domainsid),
982 "CONFIGDN": names.configdn,
983 "ADMINPASS_B64": b64encode(adminpass),
984 "KRBTGTPASS_B64": b64encode(krbtgtpass),
987 if serverrole == "domain controller":
988 message("Setting up self join")
989 setup_self_join(samdb, names=names, invocationid=invocationid,
990 dnspass=dnspass,
991 machinepass=machinepass,
992 domainsid=domainsid, policyguid=policyguid,
993 policyguid_dc=policyguid_dc,
994 setup_path=setup_path,
995 domainControllerFunctionality=domainControllerFunctionality)
996 # add the NTDSGUID based SPNs
997 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
998 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
999 expression="", scope=SCOPE_BASE)
1000 assert isinstance(names.ntdsguid, str)
1002 except:
1003 samdb.transaction_cancel()
1004 raise
1006 samdb.transaction_commit()
1007 return samdb
1010 FILL_FULL = "FULL"
1011 FILL_NT4SYNC = "NT4SYNC"
1012 FILL_DRS = "DRS"
1015 def provision(setup_dir, message, session_info,
1016 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1017 realm=None,
1018 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1019 serverdn=None,
1020 domain=None, hostname=None, hostip=None, hostip6=None,
1021 domainsid=None, adminpass=None, ldapadminpass=None,
1022 krbtgtpass=None, domainguid=None,
1023 policyguid=None, policyguid_dc=None, invocationid=None,
1024 machinepass=None,
1025 dnspass=None, root=None, nobody=None, users=None,
1026 wheel=None, backup=None, aci=None, serverrole=None,
1027 ldap_backend_extra_port=None, ldap_backend_type=None,
1028 sitename=None,
1029 ol_mmr_urls=None, ol_olc=None,
1030 setup_ds_path=None, slapd_path=None, nosync=False,
1031 ldap_dryrun_mode=False):
1032 """Provision samba4
1034 :note: caution, this wipes all existing data!
1037 def setup_path(file):
1038 return os.path.join(setup_dir, file)
1040 if domainsid is None:
1041 domainsid = security.random_sid()
1043 # create/adapt the group policy GUIDs
1044 if policyguid is None:
1045 policyguid = str(uuid.uuid4())
1046 policyguid = policyguid.upper()
1047 if policyguid_dc is None:
1048 policyguid_dc = str(uuid.uuid4())
1049 policyguid_dc = policyguid_dc.upper()
1051 if adminpass is None:
1052 adminpass = glue.generate_random_str(12)
1053 if krbtgtpass is None:
1054 krbtgtpass = glue.generate_random_str(12)
1055 if machinepass is None:
1056 machinepass = glue.generate_random_str(12)
1057 if dnspass is None:
1058 dnspass = glue.generate_random_str(12)
1059 if ldapadminpass is None:
1060 #Make a new, random password between Samba and it's LDAP server
1061 ldapadminpass=glue.generate_random_str(12)
1064 root_uid = findnss_uid([root or "root"])
1065 nobody_uid = findnss_uid([nobody or "nobody"])
1066 users_gid = findnss_gid([users or "users"])
1067 if wheel is None:
1068 wheel_gid = findnss_gid(["wheel", "adm"])
1069 else:
1070 wheel_gid = findnss_gid([wheel])
1072 if targetdir is not None:
1073 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1074 os.makedirs(os.path.join(targetdir, "etc"))
1075 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1076 elif smbconf is None:
1077 smbconf = param.default_path()
1079 # only install a new smb.conf if there isn't one there already
1080 if not os.path.exists(smbconf):
1081 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1082 targetdir)
1084 lp = param.LoadParm()
1085 lp.load(smbconf)
1087 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1088 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1089 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1090 serverdn=serverdn)
1092 paths = provision_paths_from_lp(lp, names.dnsdomain)
1094 if hostip is None:
1095 try:
1096 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1097 except socket.gaierror, (socket.EAI_NODATA, msg):
1098 hostip = None
1100 if hostip6 is None:
1101 try:
1102 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1103 except socket.gaierror, (socket.EAI_NODATA, msg):
1104 hostip6 = None
1106 if serverrole is None:
1107 serverrole = lp.get("server role")
1109 assert serverrole in ("domain controller", "member server", "standalone")
1110 if invocationid is None and serverrole == "domain controller":
1111 invocationid = str(uuid.uuid4())
1113 if not os.path.exists(paths.private_dir):
1114 os.mkdir(paths.private_dir)
1116 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1118 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1119 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1121 secrets_credentials = credentials
1122 provision_backend = None
1123 if ldap_backend_type:
1124 # We only support an LDAP backend over ldapi://
1126 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1127 lp=lp, credentials=credentials,
1128 names=names,
1129 message=message, hostname=hostname,
1130 root=root, schema=schema,
1131 ldap_backend_type=ldap_backend_type,
1132 ldapadminpass=ldapadminpass,
1133 ldap_backend_extra_port=ldap_backend_extra_port,
1134 ol_mmr_urls=ol_mmr_urls,
1135 slapd_path=slapd_path,
1136 setup_ds_path=setup_ds_path,
1137 ldap_dryrun_mode=ldap_dryrun_mode)
1139 # Now use the backend credentials to access the databases
1140 credentials = provision_backend.credentials
1141 secrets_credentials = provision_backend.adminCredentials
1142 ldapi_url = provision_backend.ldapi_uri
1144 # only install a new shares config db if there is none
1145 if not os.path.exists(paths.shareconf):
1146 message("Setting up share.ldb")
1147 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1148 credentials=credentials, lp=lp)
1149 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1152 message("Setting up secrets.ldb")
1153 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1154 session_info=session_info,
1155 credentials=secrets_credentials, lp=lp)
1157 message("Setting up the registry")
1158 setup_registry(paths.hklm, setup_path, session_info,
1159 lp=lp)
1161 message("Setting up idmap db")
1162 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1163 lp=lp)
1165 message("Setting up SAM db")
1166 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1167 credentials=credentials, lp=lp, names=names,
1168 message=message,
1169 domainsid=domainsid,
1170 schema=schema, domainguid=domainguid,
1171 policyguid=policyguid, policyguid_dc=policyguid_dc,
1172 fill=samdb_fill,
1173 adminpass=adminpass, krbtgtpass=krbtgtpass,
1174 invocationid=invocationid,
1175 machinepass=machinepass, dnspass=dnspass,
1176 serverrole=serverrole, ldap_backend=provision_backend)
1178 if serverrole == "domain controller":
1179 if paths.netlogon is None:
1180 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1181 message("Please either remove %s or see the template at %s" %
1182 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1183 assert(paths.netlogon is not None)
1185 if paths.sysvol is None:
1186 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1187 message("Please either remove %s or see the template at %s" %
1188 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1189 assert(paths.sysvol is not None)
1191 # Set up group policies (domain policy and domain controller policy)
1193 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1194 "{" + policyguid + "}")
1195 os.makedirs(policy_path, 0755)
1196 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1197 "[General]\r\nVersion=65544")
1198 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1199 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1201 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1202 "{" + policyguid_dc + "}")
1203 os.makedirs(policy_path_dc, 0755)
1204 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1205 "[General]\r\nVersion=2")
1206 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1207 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1209 if not os.path.isdir(paths.netlogon):
1210 os.makedirs(paths.netlogon, 0755)
1212 if samdb_fill == FILL_FULL:
1213 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1214 root_uid=root_uid, nobody_uid=nobody_uid,
1215 users_gid=users_gid, wheel_gid=wheel_gid)
1217 message("Setting up sam.ldb rootDSE marking as synchronized")
1218 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1220 # Only make a zone file on the first DC, it should be replicated with DNS replication
1221 if serverrole == "domain controller":
1222 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1223 credentials=credentials, lp=lp)
1224 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1225 realm=names.realm,
1226 netbiosname=names.netbiosname,
1227 domainsid=domainsid,
1228 keytab_path=paths.keytab, samdb_url=paths.samdb,
1229 dns_keytab_path=paths.dns_keytab,
1230 dnspass=dnspass, machinepass=machinepass,
1231 dnsdomain=names.dnsdomain)
1233 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1234 assert isinstance(domainguid, str)
1236 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1237 domaindn=names.domaindn, hostip=hostip,
1238 hostip6=hostip6, hostname=names.hostname,
1239 dnspass=dnspass, realm=names.realm,
1240 domainguid=domainguid, ntdsguid=names.ntdsguid)
1242 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1243 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1245 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1246 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1247 keytab_name=paths.dns_keytab)
1248 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1249 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1251 create_krb5_conf(paths.krb5conf, setup_path,
1252 dnsdomain=names.dnsdomain, hostname=names.hostname,
1253 realm=names.realm)
1254 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1257 if provision_backend is not None:
1258 if ldap_backend_type == "fedora-ds":
1259 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1261 # delete default SASL mappings
1262 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1264 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1265 for i in range (0, len(res)):
1266 dn = str(res[i]["dn"])
1267 ldapi_db.delete(dn)
1269 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1271 m = ldb.Message()
1272 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1274 m.dn = ldb.Dn(1, names.domaindn)
1275 ldapi_db.modify(m)
1277 m.dn = ldb.Dn(1, names.configdn)
1278 ldapi_db.modify(m)
1280 m.dn = ldb.Dn(1, names.schemadn)
1281 ldapi_db.modify(m)
1283 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1284 if provision_backend.slapd.poll() is None:
1285 #Kill the slapd
1286 if hasattr(provision_backend.slapd, "terminate"):
1287 provision_backend.slapd.terminate()
1288 else:
1289 # Older python versions don't have .terminate()
1290 import signal
1291 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1293 #and now wait for it to die
1294 provision_backend.slapd.communicate()
1296 # now display slapd_command_file.txt to show how slapd must be started next time
1297 message("Use later the following commandline to start slapd, then Samba:")
1298 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1299 message(slapd_command)
1300 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1302 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1303 "SLAPD_COMMAND" : slapd_command})
1306 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1307 ldapi_url)
1309 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1311 message("Once the above files are installed, your Samba4 server will be ready to use")
1312 message("Server Role: %s" % serverrole)
1313 message("Hostname: %s" % names.hostname)
1314 message("NetBIOS Domain: %s" % names.domain)
1315 message("DNS Domain: %s" % names.dnsdomain)
1316 message("DOMAIN SID: %s" % str(domainsid))
1317 if samdb_fill == FILL_FULL:
1318 message("Admin password: %s" % adminpass)
1319 if provision_backend:
1320 if provision_backend.credentials.get_bind_dn() is not None:
1321 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1322 else:
1323 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1325 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1327 result = ProvisionResult()
1328 result.domaindn = domaindn
1329 result.paths = paths
1330 result.lp = lp
1331 result.samdb = samdb
1332 return result
1336 def provision_become_dc(setup_dir=None,
1337 smbconf=None, targetdir=None, realm=None,
1338 rootdn=None, domaindn=None, schemadn=None,
1339 configdn=None, serverdn=None,
1340 domain=None, hostname=None, domainsid=None,
1341 adminpass=None, krbtgtpass=None, domainguid=None,
1342 policyguid=None, policyguid_dc=None, invocationid=None,
1343 machinepass=None,
1344 dnspass=None, root=None, nobody=None, users=None,
1345 wheel=None, backup=None, serverrole=None,
1346 ldap_backend=None, ldap_backend_type=None,
1347 sitename=None, debuglevel=1):
1349 def message(text):
1350 """print a message if quiet is not set."""
1351 print text
1353 glue.set_debug_level(debuglevel)
1355 return provision(setup_dir, message, system_session(), None,
1356 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1357 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1358 configdn=configdn, serverdn=serverdn, domain=domain,
1359 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1360 machinepass=machinepass, serverrole="domain controller",
1361 sitename=sitename)
1364 def setup_db_config(setup_path, dbdir):
1365 """Setup a Berkeley database.
1367 :param setup_path: Setup path function.
1368 :param dbdir: Database directory."""
1369 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1370 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1371 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1372 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1374 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1375 {"LDAPDBDIR": dbdir})
1377 class ProvisionBackend(object):
1378 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1379 names=None, message=None,
1380 hostname=None, root=None,
1381 schema=None, ldapadminpass=None,
1382 ldap_backend_type=None, ldap_backend_extra_port=None,
1383 ol_mmr_urls=None,
1384 setup_ds_path=None, slapd_path=None,
1385 nosync=False, ldap_dryrun_mode=False):
1386 """Provision an LDAP backend for samba4
1388 This works for OpenLDAP and Fedora DS
1391 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1393 if not os.path.isdir(paths.ldapdir):
1394 os.makedirs(paths.ldapdir, 0700)
1396 if ldap_backend_type == "existing":
1397 #Check to see that this 'existing' LDAP backend in fact exists
1398 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1399 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1400 expression="(objectClass=OpenLDAProotDSE)")
1402 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1403 # This caused them to be set into the long-term database later in the script.
1404 self.credentials = credentials
1405 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1406 return
1408 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1409 # if another instance of slapd is already running
1410 try:
1411 ldapi_db = Ldb(self.ldapi_uri)
1412 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1413 expression="(objectClass=OpenLDAProotDSE)");
1414 try:
1415 f = open(paths.slapdpid, "r")
1416 p = f.read()
1417 f.close()
1418 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1419 except:
1420 pass
1422 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1424 except LdbError, e:
1425 pass
1427 # Try to print helpful messages when the user has not specified the path to slapd
1428 if slapd_path is None:
1429 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1430 if not os.path.exists(slapd_path):
1431 message (slapd_path)
1432 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1434 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1435 try:
1436 os.unlink(schemadb_path)
1437 except OSError:
1438 pass
1441 # Put the LDIF of the schema into a database so we can search on
1442 # it to generate schema-dependent configurations in Fedora DS and
1443 # OpenLDAP
1444 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1445 schema.ldb.connect(schemadb_path)
1446 schema.ldb.transaction_start()
1448 # These bits of LDIF are supplied when the Schema object is created
1449 schema.ldb.add_ldif(schema.schema_dn_add)
1450 schema.ldb.modify_ldif(schema.schema_dn_modify)
1451 schema.ldb.add_ldif(schema.schema_data)
1452 schema.ldb.transaction_commit()
1454 self.credentials = Credentials()
1455 self.credentials.guess(lp)
1456 #Kerberos to an ldapi:// backend makes no sense
1457 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1459 self.adminCredentials = Credentials()
1460 self.adminCredentials.guess(lp)
1461 #Kerberos to an ldapi:// backend makes no sense
1462 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1464 self.ldap_backend_type = ldap_backend_type
1466 if ldap_backend_type == "fedora-ds":
1467 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1468 names=names, message=message,
1469 hostname=hostname,
1470 ldapadminpass=ldapadminpass, root=root,
1471 schema=schema,
1472 ldap_backend_extra_port=ldap_backend_extra_port,
1473 setup_ds_path=setup_ds_path,
1474 slapd_path=slapd_path,
1475 nosync=nosync,
1476 ldap_dryrun_mode=ldap_dryrun_mode)
1478 elif ldap_backend_type == "openldap":
1479 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1480 names=names, message=message,
1481 hostname=hostname,
1482 ldapadminpass=ldapadminpass, root=root,
1483 schema=schema,
1484 ldap_backend_extra_port=ldap_backend_extra_port,
1485 ol_mmr_urls=ol_mmr_urls,
1486 slapd_path=slapd_path,
1487 nosync=nosync,
1488 ldap_dryrun_mode=ldap_dryrun_mode)
1489 else:
1490 raise ProvisioningError("Unknown LDAP backend type selected")
1492 self.credentials.set_password(ldapadminpass)
1493 self.adminCredentials.set_username("samba-admin")
1494 self.adminCredentials.set_password(ldapadminpass)
1496 # Now start the slapd, so we can provision onto it. We keep the
1497 # subprocess context around, to kill this off at the successful
1498 # end of the script
1499 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1501 while self.slapd.poll() is None:
1502 # Wait until the socket appears
1503 try:
1504 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1505 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1506 expression="(objectClass=OpenLDAProotDSE)")
1507 # If we have got here, then we must have a valid connection to the LDAP server!
1508 return
1509 except LdbError, e:
1510 time.sleep(1)
1511 pass
1513 raise ProvisioningError("slapd died before we could make a connection to it")
1516 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1517 message=None,
1518 hostname=None, ldapadminpass=None, root=None,
1519 schema=None,
1520 ldap_backend_extra_port=None,
1521 ol_mmr_urls=None,
1522 slapd_path=None, nosync=False,
1523 ldap_dryrun_mode=False):
1525 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1526 nosync_config = ""
1527 if nosync:
1528 nosync_config = "dbnosync"
1530 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1531 refint_attributes = ""
1532 memberof_config = "# Generated from Samba4 schema\n"
1533 for att in lnkattr.keys():
1534 if lnkattr[att] is not None:
1535 refint_attributes = refint_attributes + " " + att
1537 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1538 { "MEMBER_ATTR" : att ,
1539 "MEMBEROF_ATTR" : lnkattr[att] })
1541 refint_config = read_and_sub_file(setup_path("refint.conf"),
1542 { "LINK_ATTRS" : refint_attributes})
1544 attrs = ["linkID", "lDAPDisplayName"]
1545 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1546 index_config = ""
1547 for i in range (0, len(res)):
1548 index_attr = res[i]["lDAPDisplayName"][0]
1549 if index_attr == "objectGUID":
1550 index_attr = "entryUUID"
1552 index_config += "index " + index_attr + " eq\n"
1554 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1555 mmr_on_config = ""
1556 mmr_replicator_acl = ""
1557 mmr_serverids_config = ""
1558 mmr_syncrepl_schema_config = ""
1559 mmr_syncrepl_config_config = ""
1560 mmr_syncrepl_user_config = ""
1563 if ol_mmr_urls is not None:
1564 # For now, make these equal
1565 mmr_pass = ldapadminpass
1567 url_list=filter(None,ol_mmr_urls.split(' '))
1568 if (len(url_list) == 1):
1569 url_list=filter(None,ol_mmr_urls.split(','))
1572 mmr_on_config = "MirrorMode On"
1573 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1574 serverid=0
1575 for url in url_list:
1576 serverid=serverid+1
1577 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1578 { "SERVERID" : str(serverid),
1579 "LDAPSERVER" : url })
1580 rid=serverid*10
1581 rid=rid+1
1582 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1583 { "RID" : str(rid),
1584 "MMRDN": names.schemadn,
1585 "LDAPSERVER" : url,
1586 "MMR_PASSWORD": mmr_pass})
1588 rid=rid+1
1589 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1590 { "RID" : str(rid),
1591 "MMRDN": names.configdn,
1592 "LDAPSERVER" : url,
1593 "MMR_PASSWORD": mmr_pass})
1595 rid=rid+1
1596 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1597 { "RID" : str(rid),
1598 "MMRDN": names.domaindn,
1599 "LDAPSERVER" : url,
1600 "MMR_PASSWORD": mmr_pass })
1601 # OpenLDAP cn=config initialisation
1602 olc_syncrepl_config = ""
1603 olc_mmr_config = ""
1604 # if mmr = yes, generate cn=config-replication directives
1605 # and olc_seed.lif for the other mmr-servers
1606 if ol_mmr_urls is not None:
1607 serverid=0
1608 olc_serverids_config = ""
1609 olc_syncrepl_seed_config = ""
1610 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1611 rid=1000
1612 for url in url_list:
1613 serverid=serverid+1
1614 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1615 { "SERVERID" : str(serverid),
1616 "LDAPSERVER" : url })
1618 rid=rid+1
1619 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1620 { "RID" : str(rid),
1621 "LDAPSERVER" : url,
1622 "MMR_PASSWORD": mmr_pass})
1624 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1625 { "RID" : str(rid),
1626 "LDAPSERVER" : url})
1628 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1629 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1630 "OLC_PW": ldapadminpass,
1631 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1632 # end olc
1634 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1635 {"DNSDOMAIN": names.dnsdomain,
1636 "LDAPDIR": paths.ldapdir,
1637 "DOMAINDN": names.domaindn,
1638 "CONFIGDN": names.configdn,
1639 "SCHEMADN": names.schemadn,
1640 "MEMBEROF_CONFIG": memberof_config,
1641 "MIRRORMODE": mmr_on_config,
1642 "REPLICATOR_ACL": mmr_replicator_acl,
1643 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1644 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1645 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1646 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1647 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1648 "OLC_MMR_CONFIG": olc_mmr_config,
1649 "REFINT_CONFIG": refint_config,
1650 "INDEX_CONFIG": index_config,
1651 "NOSYNC": nosync_config})
1653 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1654 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1655 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1657 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1658 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1660 setup_file(setup_path("cn=samba.ldif"),
1661 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1662 { "UUID": str(uuid.uuid4()),
1663 "LDAPTIME": timestring(int(time.time()))} )
1664 setup_file(setup_path("cn=samba-admin.ldif"),
1665 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1666 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1667 "UUID": str(uuid.uuid4()),
1668 "LDAPTIME": timestring(int(time.time()))} )
1670 if ol_mmr_urls is not None:
1671 setup_file(setup_path("cn=replicator.ldif"),
1672 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1673 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1674 "UUID": str(uuid.uuid4()),
1675 "LDAPTIME": timestring(int(time.time()))} )
1678 mapping = "schema-map-openldap-2.3"
1679 backend_schema = "backend-schema.schema"
1681 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1682 assert backend_schema_data is not None
1683 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1685 # now we generate the needed strings to start slapd automatically,
1686 # first ldapi_uri...
1687 if ldap_backend_extra_port is not None:
1688 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1689 # specified there as part of it's clue as to it's own name,
1690 # and not to replicate to itself
1691 if ol_mmr_urls is None:
1692 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1693 else:
1694 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1695 else:
1696 server_port_string = ""
1698 # Prepare the 'result' information - the commands to return in particular
1699 result.slapd_provision_command = [slapd_path]
1701 result.slapd_provision_command.append("-F" + paths.olcdir)
1703 result.slapd_provision_command.append("-h")
1705 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1706 result.slapd_command = list(result.slapd_provision_command)
1708 result.slapd_provision_command.append(result.ldapi_uri)
1709 result.slapd_provision_command.append("-d0")
1711 uris = result.ldapi_uri
1712 if server_port_string is not "":
1713 uris = uris + " " + server_port_string
1715 result.slapd_command.append(uris)
1717 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1718 result.credentials.set_username("samba-admin")
1720 # If we were just looking for crashes up to this point, it's a
1721 # good time to exit before we realise we don't have OpenLDAP on
1722 # this system
1723 if ldap_dryrun_mode:
1724 sys.exit(0)
1726 # Finally, convert the configuration into cn=config style!
1727 if not os.path.isdir(paths.olcdir):
1728 os.makedirs(paths.olcdir, 0770)
1730 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1732 # We can't do this, as OpenLDAP is strange. It gives an error
1733 # output to the above, but does the conversion sucessfully...
1735 # if retcode != 0:
1736 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1738 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1739 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1741 # Don't confuse the admin by leaving the slapd.conf around
1742 os.remove(paths.slapdconf)
1745 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1746 message=None,
1747 hostname=None, ldapadminpass=None, root=None,
1748 schema=None,
1749 ldap_backend_extra_port=None,
1750 setup_ds_path=None,
1751 slapd_path=None,
1752 nosync=False,
1753 ldap_dryrun_mode=False):
1755 if ldap_backend_extra_port is not None:
1756 serverport = "ServerPort=%d" % ldap_backend_extra_port
1757 else:
1758 serverport = ""
1760 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1761 {"ROOT": root,
1762 "HOSTNAME": hostname,
1763 "DNSDOMAIN": names.dnsdomain,
1764 "LDAPDIR": paths.ldapdir,
1765 "DOMAINDN": names.domaindn,
1766 "LDAPMANAGERDN": names.ldapmanagerdn,
1767 "LDAPMANAGERPASS": ldapadminpass,
1768 "SERVERPORT": serverport})
1770 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1771 {"CONFIGDN": names.configdn,
1772 "SCHEMADN": names.schemadn,
1773 "SAMBADN": names.sambadn,
1776 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1777 {"SAMBADN": names.sambadn,
1780 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1781 {"SAMBADN": names.sambadn,
1782 "LDAPADMINPASS": ldapadminpass
1785 mapping = "schema-map-fedora-ds-1.0"
1786 backend_schema = "99_ad.ldif"
1788 # Build a schema file in Fedora DS format
1789 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1790 assert backend_schema_data is not None
1791 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1793 result.credentials.set_bind_dn(names.ldapmanagerdn)
1795 # Destory the target directory, or else setup-ds.pl will complain
1796 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1797 shutil.rmtree(fedora_ds_dir, True)
1799 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1800 #In the 'provision' command line, stay in the foreground so we can easily kill it
1801 result.slapd_provision_command.append("-d0")
1803 #the command for the final run is the normal script
1804 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1806 # If we were just looking for crashes up to this point, it's a
1807 # good time to exit before we realise we don't have Fedora DS on
1808 if ldap_dryrun_mode:
1809 sys.exit(0)
1811 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1812 if setup_ds_path is None:
1813 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\"!")
1814 if not os.path.exists(setup_ds_path):
1815 message (setup_ds_path)
1816 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1818 # Run the Fedora DS setup utility
1819 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1820 if retcode != 0:
1821 raise ProvisioningError("setup-ds failed")
1823 # Load samba-admin
1824 retcode = subprocess.call([
1825 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1826 close_fds=True, shell=False)
1827 if retcode != 0:
1828 raise("ldib2db failed")
1830 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1831 """Create a PHP LDAP admin configuration file.
1833 :param path: Path to write the configuration to.
1834 :param setup_path: Function to generate setup paths.
1836 setup_file(setup_path("phpldapadmin-config.php"), path,
1837 {"S4_LDAPI_URI": ldapi_uri})
1840 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1841 hostip, hostip6, hostname, dnspass, realm, domainguid,
1842 ntdsguid):
1843 """Write out a DNS zone file, from the info in the current database.
1845 :param path: Path of the new zone file.
1846 :param setup_path: Setup path function.
1847 :param dnsdomain: DNS Domain name
1848 :param domaindn: DN of the Domain
1849 :param hostip: Local IPv4 IP
1850 :param hostip6: Local IPv6 IP
1851 :param hostname: Local hostname
1852 :param dnspass: Password for DNS
1853 :param realm: Realm name
1854 :param domainguid: GUID of the domain.
1855 :param ntdsguid: GUID of the hosts nTDSDSA record.
1857 assert isinstance(domainguid, str)
1859 if hostip6 is not None:
1860 hostip6_base_line = " IN AAAA " + hostip6
1861 hostip6_host_line = hostname + " IN AAAA " + hostip6
1862 else:
1863 hostip6_base_line = ""
1864 hostip6_host_line = ""
1866 if hostip is not None:
1867 hostip_base_line = " IN A " + hostip
1868 hostip_host_line = hostname + " IN A " + hostip
1869 else:
1870 hostip_base_line = ""
1871 hostip_host_line = ""
1873 setup_file(setup_path("provision.zone"), path, {
1874 "DNSPASS_B64": b64encode(dnspass),
1875 "HOSTNAME": hostname,
1876 "DNSDOMAIN": dnsdomain,
1877 "REALM": realm,
1878 "HOSTIP_BASE_LINE": hostip_base_line,
1879 "HOSTIP_HOST_LINE": hostip_host_line,
1880 "DOMAINGUID": domainguid,
1881 "DATESTRING": time.strftime("%Y%m%d%H"),
1882 "DEFAULTSITE": DEFAULTSITE,
1883 "NTDSGUID": ntdsguid,
1884 "HOSTIP6_BASE_LINE": hostip6_base_line,
1885 "HOSTIP6_HOST_LINE": hostip6_host_line,
1889 def create_named_conf(path, setup_path, realm, dnsdomain,
1890 private_dir):
1891 """Write out a file containing zone statements suitable for inclusion in a
1892 named.conf file (including GSS-TSIG configuration).
1894 :param path: Path of the new named.conf file.
1895 :param setup_path: Setup path function.
1896 :param realm: Realm name
1897 :param dnsdomain: DNS Domain name
1898 :param private_dir: Path to private directory
1899 :param keytab_name: File name of DNS keytab file
1902 setup_file(setup_path("named.conf"), path, {
1903 "DNSDOMAIN": dnsdomain,
1904 "REALM": realm,
1905 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1906 "PRIVATE_DIR": private_dir
1909 def create_named_txt(path, setup_path, realm, dnsdomain,
1910 private_dir, keytab_name):
1911 """Write out a file containing zone statements suitable for inclusion in a
1912 named.conf file (including GSS-TSIG configuration).
1914 :param path: Path of the new named.conf file.
1915 :param setup_path: Setup path function.
1916 :param realm: Realm name
1917 :param dnsdomain: DNS Domain name
1918 :param private_dir: Path to private directory
1919 :param keytab_name: File name of DNS keytab file
1922 setup_file(setup_path("named.txt"), path, {
1923 "DNSDOMAIN": dnsdomain,
1924 "REALM": realm,
1925 "DNS_KEYTAB": keytab_name,
1926 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1927 "PRIVATE_DIR": private_dir
1930 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1931 """Write out a file containing zone statements suitable for inclusion in a
1932 named.conf file (including GSS-TSIG configuration).
1934 :param path: Path of the new named.conf file.
1935 :param setup_path: Setup path function.
1936 :param dnsdomain: DNS Domain name
1937 :param hostname: Local hostname
1938 :param realm: Realm name
1941 setup_file(setup_path("krb5.conf"), path, {
1942 "DNSDOMAIN": dnsdomain,
1943 "HOSTNAME": hostname,
1944 "REALM": realm,