Moved admin user impersonation so that partitions are created with the correct owner...
[Samba/nivanova.git] / source4 / scripting / python / samba / provision.py
blobfa73652a9873ce6e321acfee71f2b8a578a1abcb
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
41 import shutil
42 from credentials import Credentials, DONT_USE_KERBEROS
43 from auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, check_all_substituted, \
45 DS_BEHAVIOR_WIN2008
46 from samba.samdb import SamDB
47 from samba.idmap import IDmapDB
48 from samba.dcerpc import security
49 from samba.ndr import ndr_pack
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 signal import SIGTERM
55 __docformat__ = "restructuredText"
58 def find_setup_dir():
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
66 return ret
67 # In source tree
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
70 return ret
71 raise Exception("Unable to find setup directory.")
74 DEFAULTSITE = "Default-First-Site-Name"
76 class InvalidNetbiosName(Exception):
77 """A specified name was not a valid NetBIOS name."""
78 def __init__(self, name):
79 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
82 class ProvisionPaths(object):
83 def __init__(self):
84 self.shareconf = None
85 self.hklm = None
86 self.hkcu = None
87 self.hkcr = None
88 self.hku = None
89 self.hkpd = None
90 self.hkpt = None
91 self.samdb = None
92 self.idmapdb = None
93 self.secrets = None
94 self.keytab = None
95 self.dns_keytab = None
96 self.dns = None
97 self.winsdb = None
98 self.private_dir = None
99 self.ldapdir = None
100 self.slapdconf = None
101 self.modulesconf = None
102 self.memberofconf = None
103 self.fedoradsinf = None
104 self.fedoradspartitions = None
105 self.olmmron = None
106 self.olmmrserveridsconf = None
107 self.olmmrsyncreplconf = None
108 self.olcdir = None
109 self.olslapd = None
110 self.olcseedldif = None
113 class ProvisionNames(object):
114 def __init__(self):
115 self.rootdn = None
116 self.domaindn = None
117 self.configdn = None
118 self.schemadn = None
119 self.ldapmanagerdn = None
120 self.dnsdomain = None
121 self.realm = None
122 self.netbiosname = None
123 self.domain = None
124 self.hostname = None
125 self.sitename = None
126 self.smbconf = None
129 class ProvisionResult(object):
130 def __init__(self):
131 self.paths = None
132 self.domaindn = None
133 self.lp = None
134 self.samdb = None
136 class Schema(object):
137 def __init__(self, setup_path, schemadn=None,
138 serverdn=None):
139 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
141 :param samdb: Load a schema into a SamDB.
142 :param setup_path: Setup path function.
143 :param schemadn: DN of the schema
144 :param serverdn: DN of the server
146 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
149 self.ldb = Ldb()
150 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
151 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
152 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
153 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
154 check_all_substituted(self.schema_data)
156 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
157 {"SCHEMADN": schemadn,
158 "SERVERDN": serverdn,
160 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
161 {"SCHEMADN": schemadn
164 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
165 prefixmap = b64encode(prefixmap)
167 # We don't actually add this ldif, just parse it
168 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
169 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
172 def check_install(lp, session_info, credentials):
173 """Check whether the current install seems ok.
175 :param lp: Loadparm context
176 :param session_info: Session information
177 :param credentials: Credentials
179 if lp.get("realm") == "":
180 raise Exception("Realm empty")
181 ldb = Ldb(lp.get("sam database"), session_info=session_info,
182 credentials=credentials, lp=lp)
183 if len(ldb.search("(cn=Administrator)")) != 1:
184 raise "No administrator account found"
187 def findnss(nssfn, names):
188 """Find a user or group from a list of possibilities.
190 :param nssfn: NSS Function to try (should raise KeyError if not found)
191 :param names: Names to check.
192 :return: Value return by first names list.
194 for name in names:
195 try:
196 return nssfn(name)
197 except KeyError:
198 pass
199 raise KeyError("Unable to find user/group %r" % names)
202 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
203 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
206 def read_and_sub_file(file, subst_vars):
207 """Read a file and sub in variables found in it
209 :param file: File to be read (typically from setup directory)
210 param subst_vars: Optional variables to subsitute in the file.
212 data = open(file, 'r').read()
213 if subst_vars is not None:
214 data = substitute_var(data, subst_vars)
215 check_all_substituted(data)
216 return data
219 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
220 """Setup a ldb in the private dir.
222 :param ldb: LDB file to import data into
223 :param ldif_path: Path of the LDIF file to load
224 :param subst_vars: Optional variables to subsitute in LDIF.
226 assert isinstance(ldif_path, str)
228 data = read_and_sub_file(ldif_path, subst_vars)
229 ldb.add_ldif(data)
232 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
233 """Modify a ldb in the private dir.
235 :param ldb: LDB object.
236 :param ldif_path: LDIF file path.
237 :param subst_vars: Optional dictionary with substitution variables.
239 data = read_and_sub_file(ldif_path, subst_vars)
241 ldb.modify_ldif(data)
244 def setup_ldb(ldb, ldif_path, subst_vars):
245 """Import a LDIF a file into a LDB handle, optionally substituting variables.
247 :note: Either all LDIF data will be added or none (using transactions).
249 :param ldb: LDB file to import into.
250 :param ldif_path: Path to the LDIF file.
251 :param subst_vars: Dictionary with substitution variables.
253 assert ldb is not None
254 ldb.transaction_start()
255 try:
256 setup_add_ldif(ldb, ldif_path, subst_vars)
257 except:
258 ldb.transaction_cancel()
259 raise
260 ldb.transaction_commit()
263 def setup_file(template, fname, subst_vars):
264 """Setup a file in the private dir.
266 :param template: Path of the template file.
267 :param fname: Path of the file to create.
268 :param subst_vars: Substitution variables.
270 f = fname
272 if os.path.exists(f):
273 os.unlink(f)
275 data = read_and_sub_file(template, subst_vars)
276 open(f, 'w').write(data)
279 def provision_paths_from_lp(lp, dnsdomain):
280 """Set the default paths for provisioning.
282 :param lp: Loadparm context.
283 :param dnsdomain: DNS Domain name
285 paths = ProvisionPaths()
286 paths.private_dir = lp.get("private dir")
287 paths.keytab = "secrets.keytab"
288 paths.dns_keytab = "dns.keytab"
290 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
291 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
292 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
293 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
294 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
295 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
296 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
297 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
298 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
299 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
300 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
301 paths.phpldapadminconfig = os.path.join(paths.private_dir,
302 "phpldapadmin-config.php")
303 paths.ldapdir = os.path.join(paths.private_dir,
304 "ldap")
305 paths.slapdconf = os.path.join(paths.ldapdir,
306 "slapd.conf")
307 paths.slapdpid = os.path.join(paths.ldapdir,
308 "slapd.pid")
309 paths.modulesconf = os.path.join(paths.ldapdir,
310 "modules.conf")
311 paths.memberofconf = os.path.join(paths.ldapdir,
312 "memberof.conf")
313 paths.fedoradsinf = os.path.join(paths.ldapdir,
314 "fedorads.inf")
315 paths.fedoradspartitions = os.path.join(paths.ldapdir,
316 "fedorads-partitions.ldif")
317 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
318 "mmr_serverids.conf")
319 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
320 "mmr_syncrepl.conf")
321 paths.olcdir = os.path.join(paths.ldapdir,
322 "slapd.d")
323 paths.olcseedldif = os.path.join(paths.ldapdir,
324 "olc_seed.ldif")
325 paths.hklm = "hklm.ldb"
326 paths.hkcr = "hkcr.ldb"
327 paths.hkcu = "hkcu.ldb"
328 paths.hku = "hku.ldb"
329 paths.hkpd = "hkpd.ldb"
330 paths.hkpt = "hkpt.ldb"
332 paths.sysvol = lp.get("path", "sysvol")
334 paths.netlogon = lp.get("path", "netlogon")
336 paths.smbconf = lp.configfile
338 return paths
341 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
342 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
343 sitename=None):
344 """Guess configuration settings to use."""
346 if hostname is None:
347 hostname = socket.gethostname().split(".")[0].lower()
349 netbiosname = hostname.upper()
350 if not valid_netbios_name(netbiosname):
351 raise InvalidNetbiosName(netbiosname)
353 hostname = hostname.lower()
355 if dnsdomain is None:
356 dnsdomain = lp.get("realm")
358 if serverrole is None:
359 serverrole = lp.get("server role")
361 assert dnsdomain is not None
362 realm = dnsdomain.upper()
364 if lp.get("realm").upper() != realm:
365 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
366 (lp.get("realm"), lp.configfile, realm))
368 dnsdomain = dnsdomain.lower()
370 if serverrole == "domain controller":
371 if domain is None:
372 domain = lp.get("workgroup")
373 if domaindn is None:
374 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
375 if lp.get("workgroup").upper() != domain.upper():
376 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
377 lp.get("workgroup"), domain)
378 else:
379 domain = netbiosname
380 if domaindn is None:
381 domaindn = "CN=" + netbiosname
383 assert domain is not None
384 domain = domain.upper()
385 if not valid_netbios_name(domain):
386 raise InvalidNetbiosName(domain)
388 if rootdn is None:
389 rootdn = domaindn
391 if configdn is None:
392 configdn = "CN=Configuration," + rootdn
393 if schemadn is None:
394 schemadn = "CN=Schema," + configdn
396 if sitename is None:
397 sitename=DEFAULTSITE
399 names = ProvisionNames()
400 names.rootdn = rootdn
401 names.domaindn = domaindn
402 names.configdn = configdn
403 names.schemadn = schemadn
404 names.ldapmanagerdn = "CN=Manager," + rootdn
405 names.dnsdomain = dnsdomain
406 names.domain = domain
407 names.realm = realm
408 names.netbiosname = netbiosname
409 names.hostname = hostname
410 names.sitename = sitename
411 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
413 return names
416 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
417 targetdir):
418 """Create a new smb.conf file based on a couple of basic settings.
420 assert smbconf is not None
421 if hostname is None:
422 hostname = socket.gethostname().split(".")[0].lower()
424 if serverrole is None:
425 serverrole = "standalone"
427 assert serverrole in ("domain controller", "member server", "standalone")
428 if serverrole == "domain controller":
429 smbconfsuffix = "dc"
430 elif serverrole == "member server":
431 smbconfsuffix = "member"
432 elif serverrole == "standalone":
433 smbconfsuffix = "standalone"
435 assert domain is not None
436 assert realm is not None
438 default_lp = param.LoadParm()
439 #Load non-existant file
440 if os.path.exists(smbconf):
441 default_lp.load(smbconf)
443 if targetdir is not None:
444 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
445 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
447 default_lp.set("lock dir", os.path.abspath(targetdir))
448 else:
449 privatedir_line = ""
450 lockdir_line = ""
452 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
453 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
455 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
456 smbconf, {
457 "HOSTNAME": hostname,
458 "DOMAIN": domain,
459 "REALM": realm,
460 "SERVERROLE": serverrole,
461 "NETLOGONPATH": netlogon,
462 "SYSVOLPATH": sysvol,
463 "PRIVATEDIR_LINE": privatedir_line,
464 "LOCKDIR_LINE": lockdir_line
468 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
469 users_gid, wheel_gid):
470 """setup reasonable name mappings for sam names to unix names.
472 :param samdb: SamDB object.
473 :param idmap: IDmap db object.
474 :param sid: The domain sid.
475 :param domaindn: The domain DN.
476 :param root_uid: uid of the UNIX root user.
477 :param nobody_uid: uid of the UNIX nobody user.
478 :param users_gid: gid of the UNIX users group.
479 :param wheel_gid: gid of the UNIX wheel group."""
481 def add_foreign(self, domaindn, sid, desc):
482 """Add a foreign security principle."""
483 add = """
484 dn: CN=%s,CN=ForeignSecurityPrincipals,%s
485 objectClass: top
486 objectClass: foreignSecurityPrincipal
487 description: %s
488 """ % (sid, domaindn, desc)
489 # deliberately ignore errors from this, as the records may
490 # already exist
491 for msg in self.parse_ldif(add):
492 self.add(msg[1])
494 add_foreign(samdb, domaindn, "S-1-5-7", "Anonymous")
495 add_foreign(samdb, domaindn, "S-1-1-0", "World")
496 add_foreign(samdb, domaindn, "S-1-5-2", "Network")
497 add_foreign(samdb, domaindn, "S-1-5-18", "System")
498 add_foreign(samdb, domaindn, "S-1-5-11", "Authenticated Users")
500 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
501 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
503 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
504 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
506 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
507 credentials, names,
508 serverrole, ldap_backend=None,
509 erase=False):
510 """Setup the partitions for the SAM database.
512 Alternatively, provision() may call this, and then populate the database.
514 :note: This will wipe the Sam Database!
516 :note: This function always removes the local SAM LDB file. The erase
517 parameter controls whether to erase the existing data, which
518 may not be stored locally but in LDAP.
520 assert session_info is not None
522 # We use options=["modules:"] to stop the modules loading - we
523 # just want to wipe and re-initialise the database, not start it up
525 try:
526 samdb = Ldb(url=samdb_path, session_info=session_info,
527 credentials=credentials, lp=lp, options=["modules:"])
528 # Wipes the database
529 samdb.erase_except_schema_controlled()
530 except LdbError:
531 os.unlink(samdb_path)
532 samdb = Ldb(url=samdb_path, session_info=session_info,
533 credentials=credentials, lp=lp, options=["modules:"])
534 # Wipes the database
535 samdb.erase_except_schema_controlled()
538 #Add modules to the list to activate them by default
539 #beware often order is important
541 # Some Known ordering constraints:
542 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
543 # - objectclass must be before password_hash, because password_hash checks
544 # that the objectclass is of type person (filled in by objectclass
545 # module when expanding the objectclass list)
546 # - partition must be last
547 # - each partition has its own module list then
548 modules_list = ["rootdse",
549 "acl",
550 "paged_results",
551 "ranged_results",
552 "anr",
553 "server_sort",
554 "asq",
555 "extended_dn_store",
556 "extended_dn_in",
557 "rdn_name",
558 "objectclass",
559 "samldb",
560 "password_hash",
561 "operational"]
562 tdb_modules_list = [
563 "subtree_rename",
564 "subtree_delete",
565 "linked_attributes",
566 "extended_dn_out_ldb"]
567 modules_list2 = ["show_deleted",
568 "partition"]
570 domaindn_ldb = "users.ldb"
571 configdn_ldb = "configuration.ldb"
572 schemadn_ldb = "schema.ldb"
573 if ldap_backend is not None:
574 domaindn_ldb = ldap_backend.ldapi_uri
575 configdn_ldb = ldap_backend.ldapi_uri
576 schemadn_ldb = ldap_backend.ldapi_uri
578 if ldap_backend.ldap_backend_type == "fedora-ds":
579 backend_modules = ["nsuniqueid", "paged_searches"]
580 # We can handle linked attributes here, as we don't have directory-side subtree operations
581 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
582 elif ldap_backend.ldap_backend_type == "openldap":
583 backend_modules = ["entryuuid", "paged_searches"]
584 # OpenLDAP handles subtree renames, so we don't want to do any of these things
585 tdb_modules_list = ["extended_dn_out_dereference"]
587 elif serverrole == "domain controller":
588 backend_modules = ["repl_meta_data"]
589 else:
590 backend_modules = ["objectguid"]
592 if tdb_modules_list is None:
593 tdb_modules_list_as_string = ""
594 else:
595 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
597 samdb.transaction_start()
598 try:
599 message("Setting up sam.ldb partitions and settings")
600 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
601 "SCHEMADN": names.schemadn,
602 "SCHEMADN_LDB": schemadn_ldb,
603 "SCHEMADN_MOD2": ",objectguid",
604 "CONFIGDN": names.configdn,
605 "CONFIGDN_LDB": configdn_ldb,
606 "DOMAINDN": names.domaindn,
607 "DOMAINDN_LDB": domaindn_ldb,
608 "SCHEMADN_MOD": "schema_fsmo,instancetype",
609 "CONFIGDN_MOD": "naming_fsmo,instancetype",
610 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
611 "MODULES_LIST": ",".join(modules_list),
612 "TDB_MODULES_LIST": tdb_modules_list_as_string,
613 "MODULES_LIST2": ",".join(modules_list2),
614 "BACKEND_MOD": ",".join(backend_modules),
617 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
619 message("Setting up sam.ldb rootDSE")
620 setup_samdb_rootdse(samdb, setup_path, names)
622 except:
623 samdb.transaction_cancel()
624 raise
626 samdb.transaction_commit()
630 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
631 netbiosname, domainsid, keytab_path, samdb_url,
632 dns_keytab_path, dnspass, machinepass):
633 """Add DC-specific bits to a secrets database.
635 :param secretsdb: Ldb Handle to the secrets database
636 :param setup_path: Setup path function
637 :param machinepass: Machine password
639 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
640 "MACHINEPASS_B64": b64encode(machinepass),
641 "DOMAIN": domain,
642 "REALM": realm,
643 "DNSDOMAIN": dnsdomain,
644 "DOMAINSID": str(domainsid),
645 "SECRETS_KEYTAB": keytab_path,
646 "NETBIOSNAME": netbiosname,
647 "SAM_LDB": samdb_url,
648 "DNS_KEYTAB": dns_keytab_path,
649 "DNSPASS_B64": b64encode(dnspass),
653 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
654 """Setup the secrets database.
656 :param path: Path to the secrets database.
657 :param setup_path: Get the path to a setup file.
658 :param session_info: Session info.
659 :param credentials: Credentials
660 :param lp: Loadparm context
661 :return: LDB handle for the created secrets database
663 if os.path.exists(path):
664 os.unlink(path)
665 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
666 lp=lp)
667 secrets_ldb.erase()
668 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
669 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
670 lp=lp)
671 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
673 if credentials is not None and credentials.authentication_requested():
674 if credentials.get_bind_dn() is not None:
675 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
676 "LDAPMANAGERDN": credentials.get_bind_dn(),
677 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
679 else:
680 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
681 "LDAPADMINUSER": credentials.get_username(),
682 "LDAPADMINREALM": credentials.get_realm(),
683 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
686 return secrets_ldb
689 def setup_templatesdb(path, setup_path, session_info, lp):
690 """Setup the templates database.
692 :param path: Path to the database.
693 :param setup_path: Function for obtaining the path to setup files.
694 :param session_info: Session info
695 :param credentials: Credentials
696 :param lp: Loadparm context
698 templates_ldb = Ldb(url=path, session_info=session_info,
699 lp=lp)
700 # Wipes the database
701 try:
702 templates_ldb.erase()
703 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
704 except:
705 os.unlink(path)
707 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
709 templates_ldb = Ldb(url=path, session_info=session_info,
710 lp=lp)
712 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
715 def setup_registry(path, setup_path, session_info, lp):
716 """Setup the registry.
718 :param path: Path to the registry database
719 :param setup_path: Function that returns the path to a setup.
720 :param session_info: Session information
721 :param credentials: Credentials
722 :param lp: Loadparm context
724 reg = registry.Registry()
725 hive = registry.open_ldb(path, session_info=session_info,
726 lp_ctx=lp)
727 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
728 provision_reg = setup_path("provision.reg")
729 assert os.path.exists(provision_reg)
730 reg.diff_apply(provision_reg)
733 def setup_idmapdb(path, setup_path, session_info, lp):
734 """Setup the idmap database.
736 :param path: path to the idmap database
737 :param setup_path: Function that returns a path to a setup file
738 :param session_info: Session information
739 :param credentials: Credentials
740 :param lp: Loadparm context
742 if os.path.exists(path):
743 os.unlink(path)
745 idmap_ldb = IDmapDB(path, session_info=session_info,
746 lp=lp)
748 idmap_ldb.erase()
749 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
750 return idmap_ldb
753 def setup_samdb_rootdse(samdb, setup_path, names):
754 """Setup the SamDB rootdse.
756 :param samdb: Sam Database handle
757 :param setup_path: Obtain setup path
759 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
760 "SCHEMADN": names.schemadn,
761 "NETBIOSNAME": names.netbiosname,
762 "DNSDOMAIN": names.dnsdomain,
763 "REALM": names.realm,
764 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
765 "DOMAINDN": names.domaindn,
766 "ROOTDN": names.rootdn,
767 "CONFIGDN": names.configdn,
768 "SERVERDN": names.serverdn,
772 def setup_self_join(samdb, names,
773 machinepass, dnspass,
774 domainsid, invocationid, setup_path,
775 policyguid, domainControllerFunctionality):
776 """Join a host to its own domain."""
777 assert isinstance(invocationid, str)
778 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
779 "CONFIGDN": names.configdn,
780 "SCHEMADN": names.schemadn,
781 "DOMAINDN": names.domaindn,
782 "SERVERDN": names.serverdn,
783 "INVOCATIONID": invocationid,
784 "NETBIOSNAME": names.netbiosname,
785 "DEFAULTSITE": names.sitename,
786 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
787 "MACHINEPASS_B64": b64encode(machinepass),
788 "DNSPASS_B64": b64encode(dnspass),
789 "REALM": names.realm,
790 "DOMAIN": names.domain,
791 "DNSDOMAIN": names.dnsdomain,
792 "SAMBA_VERSION_STRING": version,
793 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
795 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
796 "POLICYGUID": policyguid,
797 "DNSDOMAIN": names.dnsdomain,
798 "DOMAINSID": str(domainsid),
799 "DOMAINDN": names.domaindn})
801 # Setup fSMORoleOwner entries to point at the newly created DC entry
802 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
803 "DOMAINDN": names.domaindn,
804 "CONFIGDN": names.configdn,
805 "SCHEMADN": names.schemadn,
806 "DEFAULTSITE": names.sitename,
807 "SERVERDN": names.serverdn
810 def get_schema_descriptor(domain_sid):
811 sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
812 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
813 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
814 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
815 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
816 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
817 "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
818 "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
819 "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
820 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
821 sec = security.descriptor.from_sddl(sddl, domain_sid)
822 return b64encode(ndr_pack(sec))
824 def get_config_descriptor(domain_sid):
825 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
826 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
827 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
828 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
829 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
830 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
831 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
832 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
833 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
834 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
835 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
836 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
837 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
838 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
839 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
840 sec = security.descriptor.from_sddl(sddl, domain_sid)
841 return b64encode(ndr_pack(sec))
843 def setup_samdb(path, setup_path, session_info, credentials, lp,
844 names, message,
845 domainsid, domainguid, policyguid,
846 fill, adminpass, krbtgtpass,
847 machinepass, invocationid, dnspass,
848 serverrole, schema=None, ldap_backend=None):
849 """Setup a complete SAM Database.
851 :note: This will wipe the main SAM database file!
854 domainFunctionality = DS_BEHAVIOR_WIN2008
855 forestFunctionality = DS_BEHAVIOR_WIN2008
856 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
858 # Also wipes the database
859 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
860 credentials=credentials, session_info=session_info,
861 names=names,
862 ldap_backend=ldap_backend, serverrole=serverrole)
864 if (schema == None):
865 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
867 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
868 samdb = Ldb(session_info=session_info,
869 credentials=credentials, lp=lp)
871 message("Pre-loading the Samba 4 and AD schema")
873 # Load the schema from the one we computed earlier
874 samdb.set_schema_from_ldb(schema.ldb)
876 # And now we can connect to the DB - the schema won't be loaded from the DB
877 samdb.connect(path)
879 # Load @OPTIONS
880 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
882 if fill == FILL_DRS:
883 return samdb
885 samdb.transaction_start()
886 try:
887 message("Erasing data from partitions")
888 # Load the schema (again). This time it will force a reindex,
889 # and will therefore make the erase_partitions() below
890 # computationally sane
891 samdb.set_schema_from_ldb(schema.ldb)
892 samdb.erase_partitions()
894 # Set the domain functionality levels onto the database.
895 # Various module (the password_hash module in particular) need
896 # to know what level of AD we are emulating.
898 # These will be fixed into the database via the database
899 # modifictions below, but we need them set from the start.
900 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
901 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
902 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
904 samdb.set_domain_sid(str(domainsid))
905 if serverrole == "domain controller":
906 samdb.set_invocation_id(invocationid)
908 message("Adding DomainDN: %s" % names.domaindn)
909 if serverrole == "domain controller":
910 domain_oc = "domainDNS"
911 else:
912 domain_oc = "samba4LocalDomain"
914 #impersonate domain admin
915 admin_session_info = admin_session(lp, domainsid)
916 samdb.set_session_info(admin_session_info)
918 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
919 "DOMAINDN": names.domaindn,
920 "DOMAIN_OC": domain_oc
923 message("Modifying DomainDN: " + names.domaindn + "")
924 if domainguid is not None:
925 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
926 else:
927 domainguid_mod = ""
929 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
930 "LDAPTIME": timestring(int(time.time())),
931 "DOMAINSID": str(domainsid),
932 "SCHEMADN": names.schemadn,
933 "NETBIOSNAME": names.netbiosname,
934 "DEFAULTSITE": names.sitename,
935 "CONFIGDN": names.configdn,
936 "SERVERDN": names.serverdn,
937 "POLICYGUID": policyguid,
938 "DOMAINDN": names.domaindn,
939 "DOMAINGUID_MOD": domainguid_mod,
940 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
943 descr = get_config_descriptor(domainsid);
944 message("Adding configuration container (permitted to fail)")
945 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
946 "CONFIGDN": names.configdn,
947 "ACI": aci,
948 "DESCRIPTOR": descr,
950 message("Modifying configuration container")
951 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
952 "CONFIGDN": names.configdn,
953 "SCHEMADN": names.schemadn,
956 # The LDIF here was created when the Schema object was constructed
957 descr = get_schema_descriptor(domainsid)
958 message("Adding schema container (permitted to fail)")
959 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
960 "SCHEMADN": names.schemadn,
961 "ACI": aci,
962 "DESCRIPTOR": descr,
964 message("Modifying schema container")
966 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
968 setup_modify_ldif(samdb,
969 setup_path("provision_schema_basedn_modify.ldif"), {
970 "SCHEMADN": names.schemadn,
971 "NETBIOSNAME": names.netbiosname,
972 "DEFAULTSITE": names.sitename,
973 "CONFIGDN": names.configdn,
974 "SERVERDN": names.serverdn,
975 "PREFIXMAP_B64": b64encode(prefixmap)
978 #impersonate domain admin
979 admin_session_info = admin_session(lp, domainsid)
980 samdb.set_session_info(admin_session_info)
982 message("Setting up sam.ldb schema")
983 samdb.add_ldif(schema.schema_dn_add)
984 samdb.modify_ldif(schema.schema_dn_modify)
985 samdb.write_prefixes_from_schema()
986 samdb.add_ldif(schema.schema_data)
987 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
988 {"SCHEMADN": names.schemadn})
990 message("Setting up sam.ldb configuration data")
991 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
992 "CONFIGDN": names.configdn,
993 "NETBIOSNAME": names.netbiosname,
994 "DEFAULTSITE": names.sitename,
995 "DNSDOMAIN": names.dnsdomain,
996 "DOMAIN": names.domain,
997 "SCHEMADN": names.schemadn,
998 "DOMAINDN": names.domaindn,
999 "SERVERDN": names.serverdn,
1000 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1003 message("Setting up display specifiers")
1004 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
1005 {"CONFIGDN": names.configdn})
1007 message("Adding users container")
1008 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1009 "DOMAINDN": names.domaindn})
1010 message("Modifying users container")
1011 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1012 "DOMAINDN": names.domaindn})
1013 message("Adding computers container")
1014 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1015 "DOMAINDN": names.domaindn})
1016 message("Modifying computers container")
1017 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1018 "DOMAINDN": names.domaindn})
1019 message("Setting up sam.ldb data")
1020 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1021 "DOMAINDN": names.domaindn,
1022 "NETBIOSNAME": names.netbiosname,
1023 "DEFAULTSITE": names.sitename,
1024 "CONFIGDN": names.configdn,
1025 "SERVERDN": names.serverdn
1028 #return back to system
1029 samdb.set_session_info(session_info)
1031 if fill == FILL_FULL:
1032 message("Setting up sam.ldb users and groups")
1033 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1034 "DOMAINDN": names.domaindn,
1035 "DOMAINSID": str(domainsid),
1036 "CONFIGDN": names.configdn,
1037 "ADMINPASS_B64": b64encode(adminpass),
1038 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1041 if serverrole == "domain controller":
1042 message("Setting up self join")
1043 setup_self_join(samdb, names=names, invocationid=invocationid,
1044 dnspass=dnspass,
1045 machinepass=machinepass,
1046 domainsid=domainsid, policyguid=policyguid,
1047 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
1049 except:
1050 samdb.transaction_cancel()
1051 raise
1053 samdb.transaction_commit()
1054 return samdb
1057 FILL_FULL = "FULL"
1058 FILL_NT4SYNC = "NT4SYNC"
1059 FILL_DRS = "DRS"
1062 def provision(setup_dir, message, session_info,
1063 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
1064 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1065 serverdn=None,
1066 domain=None, hostname=None, hostip=None, hostip6=None,
1067 domainsid=None, adminpass=None, ldapadminpass=None,
1068 krbtgtpass=None, domainguid=None,
1069 policyguid=None, invocationid=None, machinepass=None,
1070 dnspass=None, root=None, nobody=None, users=None,
1071 wheel=None, backup=None, aci=None, serverrole=None,
1072 ldap_backend_extra_port=None, ldap_backend_type=None, sitename=None,
1073 ol_mmr_urls=None, ol_olc=None,
1074 setup_ds_path=None, slapd_path=None, nosync=False,
1075 ldap_dryrun_mode=False):
1076 """Provision samba4
1078 :note: caution, this wipes all existing data!
1081 def setup_path(file):
1082 return os.path.join(setup_dir, file)
1084 if domainsid is None:
1085 domainsid = security.random_sid()
1087 if policyguid is None:
1088 policyguid = str(uuid.uuid4())
1089 if adminpass is None:
1090 adminpass = glue.generate_random_str(12)
1091 if krbtgtpass is None:
1092 krbtgtpass = glue.generate_random_str(12)
1093 if machinepass is None:
1094 machinepass = glue.generate_random_str(12)
1095 if dnspass is None:
1096 dnspass = glue.generate_random_str(12)
1097 if ldapadminpass is None:
1098 #Make a new, random password between Samba and it's LDAP server
1099 ldapadminpass=glue.generate_random_str(12)
1102 root_uid = findnss_uid([root or "root"])
1103 nobody_uid = findnss_uid([nobody or "nobody"])
1104 users_gid = findnss_gid([users or "users"])
1105 if wheel is None:
1106 wheel_gid = findnss_gid(["wheel", "adm"])
1107 else:
1108 wheel_gid = findnss_gid([wheel])
1110 if targetdir is not None:
1111 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1112 os.makedirs(os.path.join(targetdir, "etc"))
1113 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1114 elif smbconf is None:
1115 smbconf = param.default_path()
1117 # only install a new smb.conf if there isn't one there already
1118 if not os.path.exists(smbconf):
1119 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1120 targetdir)
1122 lp = param.LoadParm()
1123 lp.load(smbconf)
1125 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1126 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1127 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1128 serverdn=serverdn)
1130 paths = provision_paths_from_lp(lp, names.dnsdomain)
1132 if hostip is None:
1133 try:
1134 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1135 except socket.gaierror, (socket.EAI_NODATA, msg):
1136 hostip = None
1138 if hostip6 is None:
1139 try:
1140 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1141 except socket.gaierror, (socket.EAI_NODATA, msg):
1142 hostip6 = None
1144 if serverrole is None:
1145 serverrole = lp.get("server role")
1147 assert serverrole in ("domain controller", "member server", "standalone")
1148 if invocationid is None and serverrole == "domain controller":
1149 invocationid = str(uuid.uuid4())
1151 if not os.path.exists(paths.private_dir):
1152 os.mkdir(paths.private_dir)
1154 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1156 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1158 provision_backend = None
1159 if ldap_backend_type:
1160 # We only support an LDAP backend over ldapi://
1162 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, lp=lp, credentials=credentials,
1163 names=names,
1164 message=message, hostname=hostname,
1165 root=root, schema=schema, ldap_backend_type=ldap_backend_type,
1166 ldapadminpass=ldapadminpass,
1167 ldap_backend_extra_port=ldap_backend_extra_port,
1168 ol_mmr_urls=ol_mmr_urls,
1169 slapd_path=slapd_path,
1170 setup_ds_path=setup_ds_path,
1171 ldap_dryrun_mode=ldap_dryrun_mode)
1173 # Now use the backend credentials to access the databases
1174 credentials = provision_backend.credentials
1176 # only install a new shares config db if there is none
1177 if not os.path.exists(paths.shareconf):
1178 message("Setting up share.ldb")
1179 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1180 credentials=credentials, lp=lp)
1181 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1184 message("Setting up secrets.ldb")
1185 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1186 session_info=session_info,
1187 credentials=credentials, lp=lp)
1189 message("Setting up the registry")
1190 setup_registry(paths.hklm, setup_path, session_info,
1191 lp=lp)
1193 message("Setting up templates db")
1194 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1195 lp=lp)
1197 message("Setting up idmap db")
1198 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1199 lp=lp)
1201 message("Setting up SAM db")
1202 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1203 credentials=credentials, lp=lp, names=names,
1204 message=message,
1205 domainsid=domainsid,
1206 schema=schema, domainguid=domainguid, policyguid=policyguid,
1207 fill=samdb_fill,
1208 adminpass=adminpass, krbtgtpass=krbtgtpass,
1209 invocationid=invocationid,
1210 machinepass=machinepass, dnspass=dnspass,
1211 serverrole=serverrole, ldap_backend=provision_backend)
1213 if serverrole == "domain controller":
1214 if paths.netlogon is None:
1215 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1216 message("Please either remove %s or see the template at %s" %
1217 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1218 assert(paths.netlogon is not None)
1220 if paths.sysvol is None:
1221 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1222 message("Please either remove %s or see the template at %s" %
1223 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1224 assert(paths.sysvol is not None)
1226 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1227 "{" + policyguid + "}")
1228 os.makedirs(policy_path, 0755)
1229 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1230 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1231 os.makedirs(os.path.join(policy_path, "User"), 0755)
1232 if not os.path.isdir(paths.netlogon):
1233 os.makedirs(paths.netlogon, 0755)
1235 if samdb_fill == FILL_FULL:
1236 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1237 root_uid=root_uid, nobody_uid=nobody_uid,
1238 users_gid=users_gid, wheel_gid=wheel_gid)
1240 message("Setting up sam.ldb rootDSE marking as synchronized")
1241 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1243 # Only make a zone file on the first DC, it should be replicated with DNS replication
1244 if serverrole == "domain controller":
1245 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1246 credentials=credentials, lp=lp)
1247 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1248 netbiosname=names.netbiosname, domainsid=domainsid,
1249 keytab_path=paths.keytab, samdb_url=paths.samdb,
1250 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1251 machinepass=machinepass, dnsdomain=names.dnsdomain)
1253 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1254 assert isinstance(domainguid, str)
1255 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1256 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1257 scope=SCOPE_SUBTREE)
1258 assert isinstance(hostguid, str)
1260 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1261 domaindn=names.domaindn, hostip=hostip,
1262 hostip6=hostip6, hostname=names.hostname,
1263 dnspass=dnspass, realm=names.realm,
1264 domainguid=domainguid, hostguid=hostguid)
1266 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1267 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1269 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1270 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1271 keytab_name=paths.dns_keytab)
1272 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1273 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1275 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1276 hostname=names.hostname, realm=names.realm)
1277 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1280 # if backend is openldap, terminate slapd after final provision and check its proper termination
1281 if provision_backend is not None and provision_backend.slapd is not None:
1282 if provision_backend.slapd.poll() is None:
1283 #Kill the slapd
1284 if hasattr(provision_backend.slapd, "terminate"):
1285 provision_backend.slapd.terminate()
1286 else:
1287 import signal
1288 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1290 #and now wait for it to die
1291 provision_backend.slapd.communicate()
1293 # now display slapd_command_file.txt to show how slapd must be started next time
1294 message("Use later the following commandline to start slapd, then Samba:")
1295 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1296 message(slapd_command)
1297 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1299 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1300 "SLAPD_COMMAND" : slapd_command})
1303 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1304 ldapi_url)
1306 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1308 message("Once the above files are installed, your Samba4 server will be ready to use")
1309 message("Server Role: %s" % serverrole)
1310 message("Hostname: %s" % names.hostname)
1311 message("NetBIOS Domain: %s" % names.domain)
1312 message("DNS Domain: %s" % names.dnsdomain)
1313 message("DOMAIN SID: %s" % str(domainsid))
1314 if samdb_fill == FILL_FULL:
1315 message("Admin password: %s" % adminpass)
1316 if provision_backend:
1317 if provision_backend.credentials.get_bind_dn() is not None:
1318 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1319 else:
1320 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1322 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1324 result = ProvisionResult()
1325 result.domaindn = domaindn
1326 result.paths = paths
1327 result.lp = lp
1328 result.samdb = samdb
1329 return result
1333 def provision_become_dc(setup_dir=None,
1334 smbconf=None, targetdir=None, realm=None,
1335 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1336 serverdn=None,
1337 domain=None, hostname=None, domainsid=None,
1338 adminpass=None, krbtgtpass=None, domainguid=None,
1339 policyguid=None, invocationid=None, machinepass=None,
1340 dnspass=None, root=None, nobody=None, users=None,
1341 wheel=None, backup=None, serverrole=None,
1342 ldap_backend=None, ldap_backend_type=None, sitename=None):
1344 def message(text):
1345 """print a message if quiet is not set."""
1346 print text
1348 return provision(setup_dir, message, system_session(), None,
1349 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1350 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1351 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1354 def setup_db_config(setup_path, dbdir):
1355 """Setup a Berkeley database.
1357 :param setup_path: Setup path function.
1358 :param dbdir: Database directory."""
1359 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1360 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1361 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1362 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1364 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1365 {"LDAPDBDIR": dbdir})
1367 class ProvisionBackend(object):
1368 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1369 names=None, message=None,
1370 hostname=None, root=None,
1371 schema=None, ldapadminpass=None,
1372 ldap_backend_type=None, ldap_backend_extra_port=None,
1373 ol_mmr_urls=None,
1374 setup_ds_path=None, slapd_path=None,
1375 nosync=False, ldap_dryrun_mode=False):
1376 """Provision an LDAP backend for samba4
1378 This works for OpenLDAP and Fedora DS
1381 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1383 if not os.path.isdir(paths.ldapdir):
1384 os.makedirs(paths.ldapdir, 0700)
1386 if ldap_backend_type == "existing":
1387 #Check to see that this 'existing' LDAP backend in fact exists
1388 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1389 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1390 expression="(objectClass=OpenLDAProotDSE)")
1392 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1393 # This caused them to be set into the long-term database later in the script.
1394 self.credentials = credentials
1395 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1396 return
1398 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1399 # if another instance of slapd is already running
1400 try:
1401 ldapi_db = Ldb(self.ldapi_uri)
1402 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1403 expression="(objectClass=OpenLDAProotDSE)");
1404 try:
1405 f = open(paths.slapdpid, "r")
1406 p = f.read()
1407 f.close()
1408 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1409 except:
1410 pass
1412 raise("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1414 except LdbError, e:
1415 pass
1417 # Try to print helpful messages when the user has not specified the path to slapd
1418 if slapd_path is None:
1419 raise("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1420 if not os.path.exists(slapd_path):
1421 message (slapd_path)
1422 raise("Warning: Given Path to slapd does not exist!")
1424 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1425 try:
1426 os.unlink(schemadb_path)
1427 except OSError:
1428 pass
1431 # Put the LDIF of the schema into a database so we can search on
1432 # it to generate schema-dependent configurations in Fedora DS and
1433 # OpenLDAP
1434 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1435 schema.ldb.connect(schemadb_path)
1436 schema.ldb.transaction_start()
1438 # These bits of LDIF are supplied when the Schema object is created
1439 schema.ldb.add_ldif(schema.schema_dn_add)
1440 schema.ldb.modify_ldif(schema.schema_dn_modify)
1441 schema.ldb.add_ldif(schema.schema_data)
1442 schema.ldb.transaction_commit()
1444 self.credentials = Credentials()
1445 self.credentials.guess(lp)
1446 #Kerberos to an ldapi:// backend makes no sense
1447 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1448 self.ldap_backend_type = ldap_backend_type
1450 if ldap_backend_type == "fedora-ds":
1451 provision_fds_backend(self, paths=paths, setup_path=setup_path, names=names, message=message,
1452 hostname=hostname, ldapadminpass=ldapadminpass, root=root,
1453 schema=schema, ldap_backend_extra_port=ldap_backend_extra_port,
1454 setup_ds_path=setup_ds_path, slapd_path=slapd_path,
1455 nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1457 elif ldap_backend_type == "openldap":
1458 provision_openldap_backend(self, paths=paths, setup_path=setup_path, names=names, message=message,
1459 hostname=hostname, ldapadminpass=ldapadminpass, root=root,
1460 schema=schema, ldap_backend_extra_port=ldap_backend_extra_port,
1461 ol_mmr_urls=ol_mmr_urls,
1462 slapd_path=slapd_path,
1463 nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1464 else:
1465 raise("Unknown LDAP backend type selected")
1467 self.credentials.set_password(ldapadminpass)
1469 # Now start the slapd, so we can provision onto it. We keep the
1470 # subprocess context around, to kill this off at the successful
1471 # end of the script
1472 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1474 while self.slapd.poll() is None:
1475 # Wait until the socket appears
1476 try:
1477 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1478 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1479 expression="(objectClass=OpenLDAProotDSE)")
1480 # If we have got here, then we must have a valid connection to the LDAP server!
1481 return
1482 except LdbError, e:
1483 time.sleep(1)
1484 pass
1486 raise "slapd died before we could make a connection to it"
1489 def provision_openldap_backend(result, paths=None, setup_path=None, names=None, message=None,
1490 hostname=None, ldapadminpass=None, root=None,
1491 schema=None,
1492 ldap_backend_extra_port=None,
1493 ol_mmr_urls=None,
1494 slapd_path=None, nosync=False,
1495 ldap_dryrun_mode=False):
1497 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1498 nosync_config = ""
1499 if nosync:
1500 nosync_config = "dbnosync"
1503 attrs = ["linkID", "lDAPDisplayName"]
1504 res = schema.ldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1506 memberof_config = "# Generated from Samba4 schema\n"
1507 refint_attributes = ""
1508 for i in range (0, len(res)):
1509 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1510 target = schema.ldb.searchone(basedn=names.schemadn,
1511 expression=expression,
1512 attribute="lDAPDisplayName",
1513 scope=SCOPE_SUBTREE)
1514 if target is not None:
1515 refint_attributes = refint_attributes + " " + res[i]["lDAPDisplayName"][0]
1517 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1518 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1519 "MEMBEROF_ATTR" : str(target) })
1521 refint_config = read_and_sub_file(setup_path("refint.conf"),
1522 { "LINK_ATTRS" : refint_attributes})
1524 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1525 index_config = ""
1526 for i in range (0, len(res)):
1527 index_attr = res[i]["lDAPDisplayName"][0]
1528 if index_attr == "objectGUID":
1529 index_attr = "entryUUID"
1531 index_config += "index " + index_attr + " eq\n"
1533 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1534 mmr_on_config = ""
1535 mmr_replicator_acl = ""
1536 mmr_serverids_config = ""
1537 mmr_syncrepl_schema_config = ""
1538 mmr_syncrepl_config_config = ""
1539 mmr_syncrepl_user_config = ""
1542 if ol_mmr_urls is not None:
1543 # For now, make these equal
1544 mmr_pass = ldapadminpass
1546 url_list=filter(None,ol_mmr_urls.split(' '))
1547 if (len(url_list) == 1):
1548 url_list=filter(None,ol_mmr_urls.split(','))
1551 mmr_on_config = "MirrorMode On"
1552 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1553 serverid=0
1554 for url in url_list:
1555 serverid=serverid+1
1556 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1557 { "SERVERID" : str(serverid),
1558 "LDAPSERVER" : url })
1559 rid=serverid*10
1560 rid=rid+1
1561 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1562 { "RID" : str(rid),
1563 "MMRDN": names.schemadn,
1564 "LDAPSERVER" : url,
1565 "MMR_PASSWORD": mmr_pass})
1567 rid=rid+1
1568 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1569 { "RID" : str(rid),
1570 "MMRDN": names.configdn,
1571 "LDAPSERVER" : url,
1572 "MMR_PASSWORD": mmr_pass})
1574 rid=rid+1
1575 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1576 { "RID" : str(rid),
1577 "MMRDN": names.domaindn,
1578 "LDAPSERVER" : url,
1579 "MMR_PASSWORD": mmr_pass })
1580 # OpenLDAP cn=config initialisation
1581 olc_syncrepl_config = ""
1582 olc_mmr_config = ""
1583 # if mmr = yes, generate cn=config-replication directives
1584 # and olc_seed.lif for the other mmr-servers
1585 if ol_mmr_urls is not None:
1586 serverid=0
1587 olc_serverids_config = ""
1588 olc_syncrepl_seed_config = ""
1589 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1590 rid=1000
1591 for url in url_list:
1592 serverid=serverid+1
1593 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1594 { "SERVERID" : str(serverid),
1595 "LDAPSERVER" : url })
1597 rid=rid+1
1598 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1599 { "RID" : str(rid),
1600 "LDAPSERVER" : url,
1601 "MMR_PASSWORD": mmr_pass})
1603 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1604 { "RID" : str(rid),
1605 "LDAPSERVER" : url})
1607 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1608 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1609 "OLC_PW": ldapadminpass,
1610 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1611 # end olc
1613 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1614 {"DNSDOMAIN": names.dnsdomain,
1615 "LDAPDIR": paths.ldapdir,
1616 "DOMAINDN": names.domaindn,
1617 "CONFIGDN": names.configdn,
1618 "SCHEMADN": names.schemadn,
1619 "MEMBEROF_CONFIG": memberof_config,
1620 "MIRRORMODE": mmr_on_config,
1621 "REPLICATOR_ACL": mmr_replicator_acl,
1622 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1623 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1624 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1625 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1626 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1627 "OLC_MMR_CONFIG": olc_mmr_config,
1628 "REFINT_CONFIG": refint_config,
1629 "INDEX_CONFIG": index_config,
1630 "NOSYNC": nosync_config})
1632 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1633 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1634 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1636 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1637 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1639 setup_file(setup_path("cn=samba.ldif"),
1640 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1641 { "UUID": str(uuid.uuid4()),
1642 "LDAPTIME": timestring(int(time.time()))} )
1643 setup_file(setup_path("cn=samba-admin.ldif"),
1644 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1645 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1646 "UUID": str(uuid.uuid4()),
1647 "LDAPTIME": timestring(int(time.time()))} )
1649 if ol_mmr_urls is not None:
1650 setup_file(setup_path("cn=replicator.ldif"),
1651 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1652 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1653 "UUID": str(uuid.uuid4()),
1654 "LDAPTIME": timestring(int(time.time()))} )
1657 mapping = "schema-map-openldap-2.3"
1658 backend_schema = "backend-schema.schema"
1660 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1661 assert backend_schema_data is not None
1662 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1664 # now we generate the needed strings to start slapd automatically,
1665 # first ldapi_uri...
1666 if ldap_backend_extra_port is not None:
1667 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1668 # specified there as part of it's clue as to it's own name,
1669 # and not to replicate to itself
1670 if ol_mmr_urls is None:
1671 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1672 else:
1673 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1674 else:
1675 server_port_string = ""
1677 # Prepare the 'result' information - the commands to return in particular
1678 result.slapd_provision_command = [slapd_path]
1680 result.slapd_provision_command.append("-F" + paths.olcdir)
1682 result.slapd_provision_command.append("-h")
1684 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1685 result.slapd_command = list(result.slapd_provision_command)
1687 result.slapd_provision_command.append(result.ldapi_uri)
1688 result.slapd_provision_command.append("-d0")
1690 uris = result.ldapi_uri
1691 if server_port_string is not "":
1692 uris = uris + " " + server_port_string
1694 result.slapd_command.append(uris)
1696 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1697 result.credentials.set_username("samba-admin")
1699 # If we were just looking for crashes up to this point, it's a
1700 # good time to exit before we realise we don't have OpenLDAP on
1701 # this system
1702 if ldap_dryrun_mode:
1703 sys.exit(0)
1705 # Finally, convert the configuration into cn=config style!
1706 if not os.path.isdir(paths.olcdir):
1707 os.makedirs(paths.olcdir, 0770)
1709 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1711 # We can't do this, as OpenLDAP is strange. It gives an error
1712 # output to the above, but does the conversion sucessfully...
1714 # if retcode != 0:
1715 # raise("conversion from slapd.conf to cn=config failed")
1717 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1718 raise("conversion from slapd.conf to cn=config failed")
1720 # Don't confuse the admin by leaving the slapd.conf around
1721 os.remove(paths.slapdconf)
1724 def provision_fds_backend(result, paths=None, setup_path=None, names=None, message=None,
1725 hostname=None, ldapadminpass=None, root=None,
1726 schema=None,
1727 ldap_backend_extra_port=None,
1728 setup_ds_path=None,
1729 slapd_path=None,
1730 nosync=False,
1731 ldap_dryrun_mode=False):
1733 if ldap_backend_extra_port is not None:
1734 serverport = "ServerPort=%d" % ldap_backend_extra_port
1735 else:
1736 serverport = ""
1738 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1739 {"ROOT": root,
1740 "HOSTNAME": hostname,
1741 "DNSDOMAIN": names.dnsdomain,
1742 "LDAPDIR": paths.ldapdir,
1743 "DOMAINDN": names.domaindn,
1744 "LDAPMANAGERDN": names.ldapmanagerdn,
1745 "LDAPMANAGERPASS": ldapadminpass,
1746 "SERVERPORT": serverport})
1748 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1749 {"CONFIGDN": names.configdn,
1750 "SCHEMADN": names.schemadn,
1753 mapping = "schema-map-fedora-ds-1.0"
1754 backend_schema = "99_ad.ldif"
1756 # Build a schema file in Fedora DS format
1757 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1758 assert backend_schema_data is not None
1759 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1761 result.credentials.set_bind_dn(names.ldapmanagerdn)
1763 # Destory the target directory, or else setup-ds.pl will complain
1764 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1765 shutil.rmtree(fedora_ds_dir, True)
1767 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1768 #In the 'provision' command line, stay in the foreground so we can easily kill it
1769 result.slapd_provision_command.append("-d0")
1771 #the command for the final run is the normal script
1772 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1774 # If we were just looking for crashes up to this point, it's a
1775 # good time to exit before we realise we don't have Fedora DS on
1776 if ldap_dryrun_mode:
1777 sys.exit(0)
1779 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1780 if setup_ds_path is None:
1781 raise("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1782 if not os.path.exists(setup_ds_path):
1783 message (setup_ds_path)
1784 raise("Warning: Given Path to slapd does not exist!")
1786 # Run the Fedora DS setup utility
1787 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1788 if retcode != 0:
1789 raise("setup-ds failed")
1791 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1792 """Create a PHP LDAP admin configuration file.
1794 :param path: Path to write the configuration to.
1795 :param setup_path: Function to generate setup paths.
1797 setup_file(setup_path("phpldapadmin-config.php"), path,
1798 {"S4_LDAPI_URI": ldapi_uri})
1801 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1802 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1803 """Write out a DNS zone file, from the info in the current database.
1805 :param path: Path of the new zone file.
1806 :param setup_path: Setup path function.
1807 :param dnsdomain: DNS Domain name
1808 :param domaindn: DN of the Domain
1809 :param hostip: Local IPv4 IP
1810 :param hostip6: Local IPv6 IP
1811 :param hostname: Local hostname
1812 :param dnspass: Password for DNS
1813 :param realm: Realm name
1814 :param domainguid: GUID of the domain.
1815 :param hostguid: GUID of the host.
1817 assert isinstance(domainguid, str)
1819 if hostip6 is not None:
1820 hostip6_base_line = " IN AAAA " + hostip6
1821 hostip6_host_line = hostname + " IN AAAA " + hostip6
1822 else:
1823 hostip6_base_line = ""
1824 hostip6_host_line = ""
1826 if hostip is not None:
1827 hostip_base_line = " IN A " + hostip
1828 hostip_host_line = hostname + " IN A " + hostip
1829 else:
1830 hostip_base_line = ""
1831 hostip_host_line = ""
1833 setup_file(setup_path("provision.zone"), path, {
1834 "DNSPASS_B64": b64encode(dnspass),
1835 "HOSTNAME": hostname,
1836 "DNSDOMAIN": dnsdomain,
1837 "REALM": realm,
1838 "HOSTIP_BASE_LINE": hostip_base_line,
1839 "HOSTIP_HOST_LINE": hostip_host_line,
1840 "DOMAINGUID": domainguid,
1841 "DATESTRING": time.strftime("%Y%m%d%H"),
1842 "DEFAULTSITE": DEFAULTSITE,
1843 "HOSTGUID": hostguid,
1844 "HOSTIP6_BASE_LINE": hostip6_base_line,
1845 "HOSTIP6_HOST_LINE": hostip6_host_line,
1849 def create_named_conf(path, setup_path, realm, dnsdomain,
1850 private_dir):
1851 """Write out a file containing zone statements suitable for inclusion in a
1852 named.conf file (including GSS-TSIG configuration).
1854 :param path: Path of the new named.conf file.
1855 :param setup_path: Setup path function.
1856 :param realm: Realm name
1857 :param dnsdomain: DNS Domain name
1858 :param private_dir: Path to private directory
1859 :param keytab_name: File name of DNS keytab file
1862 setup_file(setup_path("named.conf"), path, {
1863 "DNSDOMAIN": dnsdomain,
1864 "REALM": realm,
1865 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1866 "PRIVATE_DIR": private_dir
1869 def create_named_txt(path, setup_path, realm, dnsdomain,
1870 private_dir, keytab_name):
1871 """Write out a file containing zone statements suitable for inclusion in a
1872 named.conf file (including GSS-TSIG configuration).
1874 :param path: Path of the new named.conf file.
1875 :param setup_path: Setup path function.
1876 :param realm: Realm name
1877 :param dnsdomain: DNS Domain name
1878 :param private_dir: Path to private directory
1879 :param keytab_name: File name of DNS keytab file
1882 setup_file(setup_path("named.txt"), path, {
1883 "DNSDOMAIN": dnsdomain,
1884 "REALM": realm,
1885 "DNS_KEYTAB": keytab_name,
1886 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1887 "PRIVATE_DIR": private_dir
1890 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
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 dnsdomain: DNS Domain name
1897 :param hostname: Local hostname
1898 :param realm: Realm name
1901 setup_file(setup_path("krb5.conf"), path, {
1902 "DNSDOMAIN": dnsdomain,
1903 "HOSTNAME": hostname,
1904 "REALM": realm,