s4:provision - Fix up ProvisioningError class as suggested by Jelmer
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob9a41709830132f05c0ffacb36e63749c00257e1d
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
29 import os
30 import sys
31 import pwd
32 import grp
33 import time
34 import uuid, glue
35 import socket
36 import param
37 import registry
38 import samba
39 import subprocess
40 import ldb
42 import shutil
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008, DS_DC_FUNCTION_2008_R2
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
52 import urllib
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
61 def find_setup_dir():
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
69 return ret
70 # In source tree
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
73 return ret
74 raise Exception("Unable to find setup directory.")
77 DEFAULTSITE = "Default-First-Site-Name"
79 # Exception classes
81 class ProvisioningError(Exception):
82 """A generic provision error."""
84 class InvalidNetbiosName(Exception):
85 """A specified name was not a valid NetBIOS name."""
86 def __init__(self, name):
87 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
90 class ProvisionPaths(object):
91 def __init__(self):
92 self.shareconf = None
93 self.hklm = None
94 self.hkcu = None
95 self.hkcr = None
96 self.hku = None
97 self.hkpd = None
98 self.hkpt = None
99 self.samdb = None
100 self.idmapdb = None
101 self.secrets = None
102 self.keytab = None
103 self.dns_keytab = None
104 self.dns = None
105 self.winsdb = None
106 self.private_dir = None
107 self.ldapdir = None
108 self.slapdconf = None
109 self.modulesconf = None
110 self.memberofconf = None
111 self.fedoradsinf = None
112 self.fedoradspartitions = None
113 self.fedoradssasl = None
114 self.olmmron = None
115 self.olmmrserveridsconf = None
116 self.olmmrsyncreplconf = None
117 self.olcdir = None
118 self.olslapd = None
119 self.olcseedldif = None
122 class ProvisionNames(object):
123 def __init__(self):
124 self.rootdn = None
125 self.domaindn = None
126 self.configdn = None
127 self.schemadn = None
128 self.sambadn = None
129 self.ldapmanagerdn = None
130 self.dnsdomain = None
131 self.realm = None
132 self.netbiosname = None
133 self.domain = None
134 self.hostname = None
135 self.sitename = None
136 self.smbconf = None
139 class ProvisionResult(object):
140 def __init__(self):
141 self.paths = None
142 self.domaindn = None
143 self.lp = None
144 self.samdb = None
146 class Schema(object):
147 def __init__(self, setup_path, schemadn=None,
148 serverdn=None, sambadn=None, ldap_backend_type=None):
149 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
151 :param samdb: Load a schema into a SamDB.
152 :param setup_path: Setup path function.
153 :param schemadn: DN of the schema
154 :param serverdn: DN of the server
156 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
159 self.ldb = Ldb()
160 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
161 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
162 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
163 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
164 check_all_substituted(self.schema_data)
166 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
167 {"SCHEMADN": schemadn,
168 "SERVERDN": serverdn,
170 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
171 {"SCHEMADN": schemadn
174 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
175 prefixmap = b64encode(prefixmap)
177 # We don't actually add this ldif, just parse it
178 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
179 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
182 # Return a hash with the forward attribute as a key and the back as the value
183 def get_linked_attributes(schemadn,schemaldb):
184 attrs = ["linkID", "lDAPDisplayName"]
185 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)
186 attributes = {}
187 for i in range (0, len(res)):
188 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
189 target = schemaldb.searchone(basedn=schemadn,
190 expression=expression,
191 attribute="lDAPDisplayName",
192 scope=SCOPE_SUBTREE)
193 if target is not None:
194 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
196 return attributes
198 def get_dnsyntax_attributes(schemadn,schemaldb):
199 attrs = ["linkID", "lDAPDisplayName"]
200 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
201 attributes = []
202 for i in range (0, len(res)):
203 attributes.append(str(res[i]["lDAPDisplayName"]))
205 return attributes
208 def check_install(lp, session_info, credentials):
209 """Check whether the current install seems ok.
211 :param lp: Loadparm context
212 :param session_info: Session information
213 :param credentials: Credentials
215 if lp.get("realm") == "":
216 raise Exception("Realm empty")
217 ldb = Ldb(lp.get("sam database"), session_info=session_info,
218 credentials=credentials, lp=lp)
219 if len(ldb.search("(cn=Administrator)")) != 1:
220 raise ProvisioningError("No administrator account found")
223 def findnss(nssfn, names):
224 """Find a user or group from a list of possibilities.
226 :param nssfn: NSS Function to try (should raise KeyError if not found)
227 :param names: Names to check.
228 :return: Value return by first names list.
230 for name in names:
231 try:
232 return nssfn(name)
233 except KeyError:
234 pass
235 raise KeyError("Unable to find user/group %r" % names)
238 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
239 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
242 def read_and_sub_file(file, subst_vars):
243 """Read a file and sub in variables found in it
245 :param file: File to be read (typically from setup directory)
246 param subst_vars: Optional variables to subsitute in the file.
248 data = open(file, 'r').read()
249 if subst_vars is not None:
250 data = substitute_var(data, subst_vars)
251 check_all_substituted(data)
252 return data
255 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
256 """Setup a ldb in the private dir.
258 :param ldb: LDB file to import data into
259 :param ldif_path: Path of the LDIF file to load
260 :param subst_vars: Optional variables to subsitute in LDIF.
262 assert isinstance(ldif_path, str)
264 data = read_and_sub_file(ldif_path, subst_vars)
265 ldb.add_ldif(data)
268 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
269 """Modify a ldb in the private dir.
271 :param ldb: LDB object.
272 :param ldif_path: LDIF file path.
273 :param subst_vars: Optional dictionary with substitution variables.
275 data = read_and_sub_file(ldif_path, subst_vars)
277 ldb.modify_ldif(data)
280 def setup_ldb(ldb, ldif_path, subst_vars):
281 """Import a LDIF a file into a LDB handle, optionally substituting variables.
283 :note: Either all LDIF data will be added or none (using transactions).
285 :param ldb: LDB file to import into.
286 :param ldif_path: Path to the LDIF file.
287 :param subst_vars: Dictionary with substitution variables.
289 assert ldb is not None
290 ldb.transaction_start()
291 try:
292 setup_add_ldif(ldb, ldif_path, subst_vars)
293 except:
294 ldb.transaction_cancel()
295 raise
296 ldb.transaction_commit()
299 def setup_file(template, fname, subst_vars):
300 """Setup a file in the private dir.
302 :param template: Path of the template file.
303 :param fname: Path of the file to create.
304 :param subst_vars: Substitution variables.
306 f = fname
308 if os.path.exists(f):
309 os.unlink(f)
311 data = read_and_sub_file(template, subst_vars)
312 open(f, 'w').write(data)
315 def provision_paths_from_lp(lp, dnsdomain):
316 """Set the default paths for provisioning.
318 :param lp: Loadparm context.
319 :param dnsdomain: DNS Domain name
321 paths = ProvisionPaths()
322 paths.private_dir = lp.get("private dir")
323 paths.dns_keytab = "dns.keytab"
325 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
326 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
327 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
328 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
329 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
330 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
331 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
332 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
333 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
334 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
335 paths.phpldapadminconfig = os.path.join(paths.private_dir,
336 "phpldapadmin-config.php")
337 paths.ldapdir = os.path.join(paths.private_dir,
338 "ldap")
339 paths.slapdconf = os.path.join(paths.ldapdir,
340 "slapd.conf")
341 paths.slapdpid = os.path.join(paths.ldapdir,
342 "slapd.pid")
343 paths.modulesconf = os.path.join(paths.ldapdir,
344 "modules.conf")
345 paths.memberofconf = os.path.join(paths.ldapdir,
346 "memberof.conf")
347 paths.fedoradsinf = os.path.join(paths.ldapdir,
348 "fedorads.inf")
349 paths.fedoradspartitions = os.path.join(paths.ldapdir,
350 "fedorads-partitions.ldif")
351 paths.fedoradssasl = os.path.join(paths.ldapdir,
352 "fedorads-sasl.ldif")
353 paths.fedoradssamba = os.path.join(paths.ldapdir,
354 "fedorads-samba.ldif")
355 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
356 "mmr_serverids.conf")
357 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
358 "mmr_syncrepl.conf")
359 paths.olcdir = os.path.join(paths.ldapdir,
360 "slapd.d")
361 paths.olcseedldif = os.path.join(paths.ldapdir,
362 "olc_seed.ldif")
363 paths.hklm = "hklm.ldb"
364 paths.hkcr = "hkcr.ldb"
365 paths.hkcu = "hkcu.ldb"
366 paths.hku = "hku.ldb"
367 paths.hkpd = "hkpd.ldb"
368 paths.hkpt = "hkpt.ldb"
370 paths.sysvol = lp.get("path", "sysvol")
372 paths.netlogon = lp.get("path", "netlogon")
374 paths.smbconf = lp.configfile
376 return paths
379 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
380 serverrole=None, rootdn=None, domaindn=None, configdn=None,
381 schemadn=None, serverdn=None, sitename=None, sambadn=None):
382 """Guess configuration settings to use."""
384 if hostname is None:
385 hostname = socket.gethostname().split(".")[0].lower()
387 netbiosname = hostname.upper()
388 if not valid_netbios_name(netbiosname):
389 raise InvalidNetbiosName(netbiosname)
391 hostname = hostname.lower()
393 if dnsdomain is None:
394 dnsdomain = lp.get("realm")
396 if serverrole is None:
397 serverrole = lp.get("server role")
399 assert dnsdomain is not None
400 realm = dnsdomain.upper()
402 if lp.get("realm").upper() != realm:
403 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
404 (lp.get("realm"), lp.configfile, realm))
406 dnsdomain = dnsdomain.lower()
408 if serverrole == "domain controller":
409 if domain is None:
410 domain = lp.get("workgroup")
411 if domaindn is None:
412 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
413 if lp.get("workgroup").upper() != domain.upper():
414 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
415 lp.get("workgroup"), domain)
416 else:
417 domain = netbiosname
418 if domaindn is None:
419 domaindn = "CN=" + netbiosname
421 assert domain is not None
422 domain = domain.upper()
423 if not valid_netbios_name(domain):
424 raise InvalidNetbiosName(domain)
426 if netbiosname.upper() == realm.upper():
427 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
429 if hostname.upper() == realm.upper():
430 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
432 if domain.upper() == realm.upper():
433 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
435 if rootdn is None:
436 rootdn = domaindn
438 if configdn is None:
439 configdn = "CN=Configuration," + rootdn
440 if schemadn is None:
441 schemadn = "CN=Schema," + configdn
442 if sambadn is None:
443 sambadn = "CN=Samba"
445 if sitename is None:
446 sitename=DEFAULTSITE
448 names = ProvisionNames()
449 names.rootdn = rootdn
450 names.domaindn = domaindn
451 names.configdn = configdn
452 names.schemadn = schemadn
453 names.sambadn = sambadn
454 names.ldapmanagerdn = "CN=Manager," + rootdn
455 names.dnsdomain = dnsdomain
456 names.domain = domain
457 names.realm = realm
458 names.netbiosname = netbiosname
459 names.hostname = hostname
460 names.sitename = sitename
461 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
463 return names
466 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
467 targetdir):
468 """Create a new smb.conf file based on a couple of basic settings.
470 assert smbconf is not None
471 if hostname is None:
472 hostname = socket.gethostname().split(".")[0].lower()
474 if serverrole is None:
475 serverrole = "standalone"
477 assert serverrole in ("domain controller", "member server", "standalone")
478 if serverrole == "domain controller":
479 smbconfsuffix = "dc"
480 elif serverrole == "member server":
481 smbconfsuffix = "member"
482 elif serverrole == "standalone":
483 smbconfsuffix = "standalone"
485 assert domain is not None
486 assert realm is not None
488 default_lp = param.LoadParm()
489 #Load non-existant file
490 if os.path.exists(smbconf):
491 default_lp.load(smbconf)
493 if targetdir is not None:
494 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
495 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
497 default_lp.set("lock dir", os.path.abspath(targetdir))
498 else:
499 privatedir_line = ""
500 lockdir_line = ""
502 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
503 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
505 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
506 smbconf, {
507 "HOSTNAME": hostname,
508 "DOMAIN": domain,
509 "REALM": realm,
510 "SERVERROLE": serverrole,
511 "NETLOGONPATH": netlogon,
512 "SYSVOLPATH": sysvol,
513 "PRIVATEDIR_LINE": privatedir_line,
514 "LOCKDIR_LINE": lockdir_line
518 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
519 users_gid, wheel_gid):
520 """setup reasonable name mappings for sam names to unix names.
522 :param samdb: SamDB object.
523 :param idmap: IDmap db object.
524 :param sid: The domain sid.
525 :param domaindn: The domain DN.
526 :param root_uid: uid of the UNIX root user.
527 :param nobody_uid: uid of the UNIX nobody user.
528 :param users_gid: gid of the UNIX users group.
529 :param wheel_gid: gid of the UNIX wheel group."""
531 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
532 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
534 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
535 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
537 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
538 credentials, names,
539 serverrole, ldap_backend=None,
540 erase=False):
541 """Setup the partitions for the SAM database.
543 Alternatively, provision() may call this, and then populate the database.
545 :note: This will wipe the Sam Database!
547 :note: This function always removes the local SAM LDB file. The erase
548 parameter controls whether to erase the existing data, which
549 may not be stored locally but in LDAP.
551 assert session_info is not None
553 # We use options=["modules:"] to stop the modules loading - we
554 # just want to wipe and re-initialise the database, not start it up
556 try:
557 samdb = Ldb(url=samdb_path, session_info=session_info,
558 credentials=credentials, lp=lp, options=["modules:"])
559 # Wipes the database
560 samdb.erase_except_schema_controlled()
561 except LdbError:
562 os.unlink(samdb_path)
563 samdb = Ldb(url=samdb_path, session_info=session_info,
564 credentials=credentials, lp=lp, options=["modules:"])
565 # Wipes the database
566 samdb.erase_except_schema_controlled()
569 #Add modules to the list to activate them by default
570 #beware often order is important
572 # Some Known ordering constraints:
573 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
574 # - objectclass must be before password_hash, because password_hash checks
575 # that the objectclass is of type person (filled in by objectclass
576 # module when expanding the objectclass list)
577 # - partition must be last
578 # - each partition has its own module list then
579 modules_list = ["resolve_oids",
580 "rootdse",
581 "paged_results",
582 "ranged_results",
583 "anr",
584 "server_sort",
585 "asq",
586 "extended_dn_store",
587 "extended_dn_in",
588 "rdn_name",
589 "objectclass",
590 "descriptor",
591 "samldb",
592 "password_hash",
593 "operational",
594 "kludge_acl"]
595 tdb_modules_list = [
596 "subtree_rename",
597 "subtree_delete",
598 "linked_attributes",
599 "extended_dn_out_ldb"]
600 modules_list2 = ["show_deleted",
601 "partition"]
603 domaindn_ldb = "users.ldb"
604 configdn_ldb = "configuration.ldb"
605 schemadn_ldb = "schema.ldb"
606 if ldap_backend is not None:
607 domaindn_ldb = ldap_backend.ldapi_uri
608 configdn_ldb = ldap_backend.ldapi_uri
609 schemadn_ldb = ldap_backend.ldapi_uri
611 if ldap_backend.ldap_backend_type == "fedora-ds":
612 backend_modules = ["nsuniqueid", "paged_searches"]
613 # We can handle linked attributes here, as we don't have directory-side subtree operations
614 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
615 elif ldap_backend.ldap_backend_type == "openldap":
616 backend_modules = ["entryuuid", "paged_searches"]
617 # OpenLDAP handles subtree renames, so we don't want to do any of these things
618 tdb_modules_list = ["extended_dn_out_dereference"]
620 elif serverrole == "domain controller":
621 tdb_modules_list.insert(0, "repl_meta_data")
622 backend_modules = []
623 else:
624 backend_modules = ["objectguid"]
626 if tdb_modules_list is None:
627 tdb_modules_list_as_string = ""
628 else:
629 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
631 samdb.transaction_start()
632 try:
633 message("Setting up sam.ldb partitions and settings")
634 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
635 "SCHEMADN": names.schemadn,
636 "SCHEMADN_LDB": schemadn_ldb,
637 "SCHEMADN_MOD2": ",objectguid",
638 "CONFIGDN": names.configdn,
639 "CONFIGDN_LDB": configdn_ldb,
640 "DOMAINDN": names.domaindn,
641 "DOMAINDN_LDB": domaindn_ldb,
642 "SCHEMADN_MOD": "schema_fsmo,instancetype",
643 "CONFIGDN_MOD": "naming_fsmo,instancetype",
644 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
645 "MODULES_LIST": ",".join(modules_list),
646 "TDB_MODULES_LIST": tdb_modules_list_as_string,
647 "MODULES_LIST2": ",".join(modules_list2),
648 "BACKEND_MOD": ",".join(backend_modules),
651 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
653 message("Setting up sam.ldb rootDSE")
654 setup_samdb_rootdse(samdb, setup_path, names)
656 except:
657 samdb.transaction_cancel()
658 raise
660 samdb.transaction_commit()
662 def secretsdb_self_join(secretsdb, domain,
663 netbiosname, domainsid, machinepass,
664 realm=None, dnsdomain=None,
665 keytab_path=None,
666 key_version_number=1,
667 secure_channel_type=SEC_CHAN_WKSTA):
668 """Add domain join-specific bits to a secrets database.
670 :param secretsdb: Ldb Handle to the secrets database
671 :param machinepass: Machine password
673 attrs=["whenChanged",
674 "secret",
675 "priorSecret",
676 "priorChanged",
677 "krb5Keytab",
678 "privateKeytab"]
681 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
682 msg["secureChannelType"] = str(secure_channel_type)
683 msg["flatname"] = [domain]
684 msg["objectClass"] = ["top", "primaryDomain"]
685 if realm is not None:
686 if dnsdomain is None:
687 dnsdomain = realm.lower()
688 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
689 msg["realm"] = realm
690 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
691 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
692 msg["privateKeytab"] = ["secrets.keytab"];
695 msg["secret"] = [machinepass]
696 msg["samAccountName"] = ["%s$" % netbiosname]
697 msg["secureChannelType"] = [str(secure_channel_type)]
698 msg["objectSid"] = [ndr_pack(domainsid)]
700 res = secretsdb.search(base="cn=Primary Domains",
701 attrs=attrs,
702 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
703 scope=SCOPE_ONELEVEL)
705 for del_msg in res:
706 if del_msg.dn is not msg.dn:
707 secretsdb.delete(del_msg.dn)
709 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
711 if len(res) == 1:
712 msg["priorSecret"] = res[0]["secret"]
713 msg["priorWhenChanged"] = res[0]["whenChanged"]
715 if res["privateKeytab"] is not None:
716 msg["privateKeytab"] = res[0]["privateKeytab"]
718 if res["krb5Keytab"] is not None:
719 msg["krb5Keytab"] = res[0]["krb5Keytab"]
721 for el in msg:
722 el.set_flags(ldb.FLAG_MOD_REPLACE)
723 secretsdb.modify(msg)
724 else:
725 secretsdb.add(msg)
728 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
729 dns_keytab_path, dnspass):
730 """Add DNS specific bits to a secrets database.
732 :param secretsdb: Ldb Handle to the secrets database
733 :param setup_path: Setup path function
734 :param machinepass: Machine password
736 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
737 "REALM": realm,
738 "DNSDOMAIN": dnsdomain,
739 "DNS_KEYTAB": dns_keytab_path,
740 "DNSPASS_B64": b64encode(dnspass),
744 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
745 """Setup the secrets database.
747 :param path: Path to the secrets database.
748 :param setup_path: Get the path to a setup file.
749 :param session_info: Session info.
750 :param credentials: Credentials
751 :param lp: Loadparm context
752 :return: LDB handle for the created secrets database
754 if os.path.exists(path):
755 os.unlink(path)
756 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
757 lp=lp)
758 secrets_ldb.erase()
759 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
760 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
761 lp=lp)
762 secrets_ldb.transaction_start()
763 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
765 if credentials is not None and credentials.authentication_requested():
766 if credentials.get_bind_dn() is not None:
767 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
768 "LDAPMANAGERDN": credentials.get_bind_dn(),
769 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
771 else:
772 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
773 "LDAPADMINUSER": credentials.get_username(),
774 "LDAPADMINREALM": credentials.get_realm(),
775 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
778 return secrets_ldb
780 def setup_registry(path, setup_path, session_info, lp):
781 """Setup the registry.
783 :param path: Path to the registry database
784 :param setup_path: Function that returns the path to a setup.
785 :param session_info: Session information
786 :param credentials: Credentials
787 :param lp: Loadparm context
789 reg = registry.Registry()
790 hive = registry.open_ldb(path, session_info=session_info,
791 lp_ctx=lp)
792 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
793 provision_reg = setup_path("provision.reg")
794 assert os.path.exists(provision_reg)
795 reg.diff_apply(provision_reg)
798 def setup_idmapdb(path, setup_path, session_info, lp):
799 """Setup the idmap database.
801 :param path: path to the idmap database
802 :param setup_path: Function that returns a path to a setup file
803 :param session_info: Session information
804 :param credentials: Credentials
805 :param lp: Loadparm context
807 if os.path.exists(path):
808 os.unlink(path)
810 idmap_ldb = IDmapDB(path, session_info=session_info,
811 lp=lp)
813 idmap_ldb.erase()
814 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
815 return idmap_ldb
818 def setup_samdb_rootdse(samdb, setup_path, names):
819 """Setup the SamDB rootdse.
821 :param samdb: Sam Database handle
822 :param setup_path: Obtain setup path
824 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
825 "SCHEMADN": names.schemadn,
826 "NETBIOSNAME": names.netbiosname,
827 "DNSDOMAIN": names.dnsdomain,
828 "REALM": names.realm,
829 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
830 "DOMAINDN": names.domaindn,
831 "ROOTDN": names.rootdn,
832 "CONFIGDN": names.configdn,
833 "SERVERDN": names.serverdn,
837 def setup_self_join(samdb, names,
838 machinepass, dnspass,
839 domainsid, invocationid, setup_path,
840 policyguid, policyguid_dc, domainControllerFunctionality):
841 """Join a host to its own domain."""
842 assert isinstance(invocationid, str)
843 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
844 "CONFIGDN": names.configdn,
845 "SCHEMADN": names.schemadn,
846 "DOMAINDN": names.domaindn,
847 "SERVERDN": names.serverdn,
848 "INVOCATIONID": invocationid,
849 "NETBIOSNAME": names.netbiosname,
850 "DEFAULTSITE": names.sitename,
851 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
852 "MACHINEPASS_B64": b64encode(machinepass),
853 "DNSPASS_B64": b64encode(dnspass),
854 "REALM": names.realm,
855 "DOMAIN": names.domain,
856 "DNSDOMAIN": names.dnsdomain,
857 "SAMBA_VERSION_STRING": version,
858 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
860 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
861 "POLICYGUID": policyguid,
862 "POLICYGUID_DC": policyguid_dc,
863 "DNSDOMAIN": names.dnsdomain,
864 "DOMAINSID": str(domainsid),
865 "DOMAINDN": names.domaindn})
867 # add the NTDSGUID based SPNs
868 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
869 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
870 expression="", scope=SCOPE_BASE)
871 assert isinstance(names.ntdsguid, str)
873 # Setup fSMORoleOwner entries to point at the newly created DC entry
874 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
875 "DOMAIN": names.domain,
876 "DNSDOMAIN": names.dnsdomain,
877 "DOMAINDN": names.domaindn,
878 "CONFIGDN": names.configdn,
879 "SCHEMADN": names.schemadn,
880 "DEFAULTSITE": names.sitename,
881 "SERVERDN": names.serverdn,
882 "NETBIOSNAME": names.netbiosname,
883 "NTDSGUID": names.ntdsguid
887 def setup_samdb(path, setup_path, session_info, credentials, lp,
888 names, message,
889 domainsid, domainguid, policyguid, policyguid_dc,
890 fill, adminpass, krbtgtpass,
891 machinepass, invocationid, dnspass,
892 serverrole, schema=None, ldap_backend=None):
893 """Setup a complete SAM Database.
895 :note: This will wipe the main SAM database file!
898 # Do NOT change these default values without discussion with the team and reslease manager.
899 domainFunctionality = DS_DOMAIN_FUNCTION_2008
900 forestFunctionality = DS_DOMAIN_FUNCTION_2008
901 domainControllerFunctionality = DS_DC_FUNCTION_2008
903 # Also wipes the database
904 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
905 credentials=credentials, session_info=session_info,
906 names=names,
907 ldap_backend=ldap_backend, serverrole=serverrole)
909 if (schema == None):
910 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
911 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
913 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
914 samdb = Ldb(session_info=session_info,
915 credentials=credentials, lp=lp)
917 message("Pre-loading the Samba 4 and AD schema")
919 # Load the schema from the one we computed earlier
920 samdb.set_schema_from_ldb(schema.ldb)
922 # And now we can connect to the DB - the schema won't be loaded from the DB
923 samdb.connect(path)
925 # Load @OPTIONS
926 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
928 if fill == FILL_DRS:
929 return samdb
931 samdb.transaction_start()
932 try:
933 message("Erasing data from partitions")
934 # Load the schema (again). This time it will force a reindex,
935 # and will therefore make the erase_partitions() below
936 # computationally sane
937 samdb.set_schema_from_ldb(schema.ldb)
938 samdb.erase_partitions()
940 # Set the domain functionality levels onto the database.
941 # Various module (the password_hash module in particular) need
942 # to know what level of AD we are emulating.
944 # These will be fixed into the database via the database
945 # modifictions below, but we need them set from the start.
946 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
947 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
948 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
950 samdb.set_domain_sid(str(domainsid))
951 if serverrole == "domain controller":
952 samdb.set_invocation_id(invocationid)
954 message("Adding DomainDN: %s" % names.domaindn)
955 if serverrole == "domain controller":
956 domain_oc = "domainDNS"
957 else:
958 domain_oc = "samba4LocalDomain"
960 #impersonate domain admin
961 admin_session_info = admin_session(lp, str(domainsid))
962 samdb.set_session_info(admin_session_info)
964 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
965 "DOMAINDN": names.domaindn,
966 "DOMAIN_OC": domain_oc
969 message("Modifying DomainDN: " + names.domaindn + "")
970 if domainguid is not None:
971 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
972 else:
973 domainguid_mod = ""
975 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
976 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
977 "DOMAINSID": str(domainsid),
978 "SCHEMADN": names.schemadn,
979 "NETBIOSNAME": names.netbiosname,
980 "DEFAULTSITE": names.sitename,
981 "CONFIGDN": names.configdn,
982 "SERVERDN": names.serverdn,
983 "POLICYGUID": policyguid,
984 "DOMAINDN": names.domaindn,
985 "DOMAINGUID_MOD": domainguid_mod,
986 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
987 "SAMBA_VERSION_STRING": version
990 message("Adding configuration container")
991 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
992 "CONFIGDN": names.configdn,
994 message("Modifying configuration container")
995 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
996 "CONFIGDN": names.configdn,
997 "SCHEMADN": names.schemadn,
1000 # The LDIF here was created when the Schema object was constructed
1001 message("Setting up sam.ldb schema")
1002 samdb.add_ldif(schema.schema_dn_add)
1003 samdb.modify_ldif(schema.schema_dn_modify)
1004 samdb.write_prefixes_from_schema()
1005 samdb.add_ldif(schema.schema_data)
1006 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1007 {"SCHEMADN": names.schemadn})
1009 message("Setting up sam.ldb configuration data")
1010 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1011 "CONFIGDN": names.configdn,
1012 "NETBIOSNAME": names.netbiosname,
1013 "DEFAULTSITE": names.sitename,
1014 "DNSDOMAIN": names.dnsdomain,
1015 "DOMAIN": names.domain,
1016 "SCHEMADN": names.schemadn,
1017 "DOMAINDN": names.domaindn,
1018 "SERVERDN": names.serverdn,
1019 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1022 message("Setting up display specifiers")
1023 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1024 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1025 check_all_substituted(display_specifiers_ldif)
1026 samdb.add_ldif(display_specifiers_ldif)
1028 message("Adding users container")
1029 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1030 "DOMAINDN": names.domaindn})
1031 message("Modifying users container")
1032 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1033 "DOMAINDN": names.domaindn})
1034 message("Adding computers container")
1035 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1036 "DOMAINDN": names.domaindn})
1037 message("Modifying computers container")
1038 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1039 "DOMAINDN": names.domaindn})
1040 message("Setting up sam.ldb data")
1041 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1042 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1043 "DOMAINDN": names.domaindn,
1044 "NETBIOSNAME": names.netbiosname,
1045 "DEFAULTSITE": names.sitename,
1046 "CONFIGDN": names.configdn,
1047 "SERVERDN": names.serverdn,
1048 "POLICYGUID_DC": policyguid_dc
1051 if fill == FILL_FULL:
1052 message("Setting up sam.ldb users and groups")
1053 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1054 "DOMAINDN": names.domaindn,
1055 "DOMAINSID": str(domainsid),
1056 "CONFIGDN": names.configdn,
1057 "ADMINPASS_B64": b64encode(adminpass),
1058 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1061 if serverrole == "domain controller":
1062 message("Setting up self join")
1063 setup_self_join(samdb, names=names, invocationid=invocationid,
1064 dnspass=dnspass,
1065 machinepass=machinepass,
1066 domainsid=domainsid, policyguid=policyguid,
1067 policyguid_dc=policyguid_dc,
1068 setup_path=setup_path,
1069 domainControllerFunctionality=domainControllerFunctionality)
1071 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1072 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1073 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1074 assert isinstance(names.ntdsguid, str)
1076 except:
1077 samdb.transaction_cancel()
1078 raise
1080 samdb.transaction_commit()
1081 return samdb
1084 FILL_FULL = "FULL"
1085 FILL_NT4SYNC = "NT4SYNC"
1086 FILL_DRS = "DRS"
1089 def provision(setup_dir, message, session_info,
1090 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1091 realm=None,
1092 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1093 serverdn=None,
1094 domain=None, hostname=None, hostip=None, hostip6=None,
1095 domainsid=None, adminpass=None, ldapadminpass=None,
1096 krbtgtpass=None, domainguid=None,
1097 policyguid=None, policyguid_dc=None, invocationid=None,
1098 machinepass=None,
1099 dnspass=None, root=None, nobody=None, users=None,
1100 wheel=None, backup=None, aci=None, serverrole=None,
1101 ldap_backend_extra_port=None, ldap_backend_type=None,
1102 sitename=None,
1103 ol_mmr_urls=None, ol_olc=None,
1104 setup_ds_path=None, slapd_path=None, nosync=False,
1105 ldap_dryrun_mode=False):
1106 """Provision samba4
1108 :note: caution, this wipes all existing data!
1111 def setup_path(file):
1112 return os.path.join(setup_dir, file)
1114 if domainsid is None:
1115 domainsid = security.random_sid()
1116 else:
1117 domainsid = security.dom_sid(domainsid)
1120 # create/adapt the group policy GUIDs
1121 if policyguid is None:
1122 policyguid = str(uuid.uuid4())
1123 policyguid = policyguid.upper()
1124 if policyguid_dc is None:
1125 policyguid_dc = str(uuid.uuid4())
1126 policyguid_dc = policyguid_dc.upper()
1128 if adminpass is None:
1129 adminpass = glue.generate_random_str(12)
1130 if krbtgtpass is None:
1131 krbtgtpass = glue.generate_random_str(12)
1132 if machinepass is None:
1133 machinepass = glue.generate_random_str(12)
1134 if dnspass is None:
1135 dnspass = glue.generate_random_str(12)
1136 if ldapadminpass is None:
1137 #Make a new, random password between Samba and it's LDAP server
1138 ldapadminpass=glue.generate_random_str(12)
1141 root_uid = findnss_uid([root or "root"])
1142 nobody_uid = findnss_uid([nobody or "nobody"])
1143 users_gid = findnss_gid([users or "users"])
1144 if wheel is None:
1145 wheel_gid = findnss_gid(["wheel", "adm"])
1146 else:
1147 wheel_gid = findnss_gid([wheel])
1149 if targetdir is not None:
1150 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1151 os.makedirs(os.path.join(targetdir, "etc"))
1152 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1153 elif smbconf is None:
1154 smbconf = param.default_path()
1156 # only install a new smb.conf if there isn't one there already
1157 if not os.path.exists(smbconf):
1158 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1159 targetdir)
1161 lp = param.LoadParm()
1162 lp.load(smbconf)
1164 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1165 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1166 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1167 serverdn=serverdn)
1169 paths = provision_paths_from_lp(lp, names.dnsdomain)
1171 if hostip is None:
1172 try:
1173 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1174 except socket.gaierror, (socket.EAI_NODATA, msg):
1175 hostip = None
1177 if hostip6 is None:
1178 try:
1179 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1180 except socket.gaierror, (socket.EAI_NODATA, msg):
1181 hostip6 = None
1183 if serverrole is None:
1184 serverrole = lp.get("server role")
1186 assert serverrole in ("domain controller", "member server", "standalone")
1187 if invocationid is None and serverrole == "domain controller":
1188 invocationid = str(uuid.uuid4())
1190 if not os.path.exists(paths.private_dir):
1191 os.mkdir(paths.private_dir)
1193 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1195 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1196 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1198 secrets_credentials = credentials
1199 provision_backend = None
1200 if ldap_backend_type:
1201 # We only support an LDAP backend over ldapi://
1203 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1204 lp=lp, credentials=credentials,
1205 names=names,
1206 message=message, hostname=hostname,
1207 root=root, schema=schema,
1208 ldap_backend_type=ldap_backend_type,
1209 ldapadminpass=ldapadminpass,
1210 ldap_backend_extra_port=ldap_backend_extra_port,
1211 ol_mmr_urls=ol_mmr_urls,
1212 slapd_path=slapd_path,
1213 setup_ds_path=setup_ds_path,
1214 ldap_dryrun_mode=ldap_dryrun_mode)
1216 # Now use the backend credentials to access the databases
1217 credentials = provision_backend.credentials
1218 secrets_credentials = provision_backend.adminCredentials
1219 ldapi_url = provision_backend.ldapi_uri
1221 # only install a new shares config db if there is none
1222 if not os.path.exists(paths.shareconf):
1223 message("Setting up share.ldb")
1224 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1225 credentials=credentials, lp=lp)
1226 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1229 message("Setting up secrets.ldb")
1230 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1231 session_info=session_info,
1232 credentials=secrets_credentials, lp=lp)
1234 message("Setting up the registry")
1235 setup_registry(paths.hklm, setup_path, session_info,
1236 lp=lp)
1238 message("Setting up idmap db")
1239 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1240 lp=lp)
1242 message("Setting up SAM db")
1243 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1244 credentials=credentials, lp=lp, names=names,
1245 message=message,
1246 domainsid=domainsid,
1247 schema=schema, domainguid=domainguid,
1248 policyguid=policyguid, policyguid_dc=policyguid_dc,
1249 fill=samdb_fill,
1250 adminpass=adminpass, krbtgtpass=krbtgtpass,
1251 invocationid=invocationid,
1252 machinepass=machinepass, dnspass=dnspass,
1253 serverrole=serverrole, ldap_backend=provision_backend)
1255 if serverrole == "domain controller":
1256 if paths.netlogon is None:
1257 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1258 message("Please either remove %s or see the template at %s" %
1259 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1260 assert(paths.netlogon is not None)
1262 if paths.sysvol is None:
1263 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1264 message("Please either remove %s or see the template at %s" %
1265 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1266 assert(paths.sysvol is not None)
1268 # Set up group policies (domain policy and domain controller policy)
1270 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1271 "{" + policyguid + "}")
1272 os.makedirs(policy_path, 0755)
1273 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1274 "[General]\r\nVersion=65543")
1275 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1276 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1278 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1279 "{" + policyguid_dc + "}")
1280 os.makedirs(policy_path_dc, 0755)
1281 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1282 "[General]\r\nVersion=2")
1283 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1284 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1286 if not os.path.isdir(paths.netlogon):
1287 os.makedirs(paths.netlogon, 0755)
1289 if samdb_fill == FILL_FULL:
1290 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1291 root_uid=root_uid, nobody_uid=nobody_uid,
1292 users_gid=users_gid, wheel_gid=wheel_gid)
1294 message("Setting up sam.ldb rootDSE marking as synchronized")
1295 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1297 # Only make a zone file on the first DC, it should be replicated with DNS replication
1298 if serverrole == "domain controller":
1299 secretsdb_self_join(secrets_ldb, domain=domain,
1300 realm=names.realm,
1301 dnsdomain=names.dnsdomain,
1302 netbiosname=names.netbiosname,
1303 domainsid=domainsid,
1304 machinepass=machinepass,
1305 secure_channel_type=SEC_CHAN_BDC)
1307 secretsdb_setup_dns(secrets_ldb, setup_path,
1308 realm=names.realm, dnsdomain=names.dnsdomain,
1309 dns_keytab_path=paths.dns_keytab,
1310 dnspass=dnspass)
1312 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1313 assert isinstance(domainguid, str)
1315 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1316 domaindn=names.domaindn, hostip=hostip,
1317 hostip6=hostip6, hostname=names.hostname,
1318 dnspass=dnspass, realm=names.realm,
1319 domainguid=domainguid, ntdsguid=names.ntdsguid)
1321 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1322 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1324 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1325 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1326 keytab_name=paths.dns_keytab)
1327 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1328 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1330 create_krb5_conf(paths.krb5conf, setup_path,
1331 dnsdomain=names.dnsdomain, hostname=names.hostname,
1332 realm=names.realm)
1333 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1335 #Now commit the secrets.ldb to disk
1336 secrets_ldb.transaction_commit()
1338 if provision_backend is not None:
1339 if ldap_backend_type == "fedora-ds":
1340 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1342 # delete default SASL mappings
1343 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1345 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1346 for i in range (0, len(res)):
1347 dn = str(res[i]["dn"])
1348 ldapi_db.delete(dn)
1350 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1352 m = ldb.Message()
1353 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1355 m.dn = ldb.Dn(1, names.domaindn)
1356 ldapi_db.modify(m)
1358 m.dn = ldb.Dn(1, names.configdn)
1359 ldapi_db.modify(m)
1361 m.dn = ldb.Dn(1, names.schemadn)
1362 ldapi_db.modify(m)
1364 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1365 if provision_backend.slapd.poll() is None:
1366 #Kill the slapd
1367 if hasattr(provision_backend.slapd, "terminate"):
1368 provision_backend.slapd.terminate()
1369 else:
1370 # Older python versions don't have .terminate()
1371 import signal
1372 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1374 #and now wait for it to die
1375 provision_backend.slapd.communicate()
1377 # now display slapd_command_file.txt to show how slapd must be started next time
1378 message("Use later the following commandline to start slapd, then Samba:")
1379 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1380 message(slapd_command)
1381 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1383 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1384 "SLAPD_COMMAND" : slapd_command})
1387 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1388 ldapi_url)
1390 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1392 message("Once the above files are installed, your Samba4 server will be ready to use")
1393 message("Server Role: %s" % serverrole)
1394 message("Hostname: %s" % names.hostname)
1395 message("NetBIOS Domain: %s" % names.domain)
1396 message("DNS Domain: %s" % names.dnsdomain)
1397 message("DOMAIN SID: %s" % str(domainsid))
1398 if samdb_fill == FILL_FULL:
1399 message("Admin password: %s" % adminpass)
1400 if provision_backend:
1401 if provision_backend.credentials.get_bind_dn() is not None:
1402 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1403 else:
1404 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1406 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1408 result = ProvisionResult()
1409 result.domaindn = domaindn
1410 result.paths = paths
1411 result.lp = lp
1412 result.samdb = samdb
1413 return result
1417 def provision_become_dc(setup_dir=None,
1418 smbconf=None, targetdir=None, realm=None,
1419 rootdn=None, domaindn=None, schemadn=None,
1420 configdn=None, serverdn=None,
1421 domain=None, hostname=None, domainsid=None,
1422 adminpass=None, krbtgtpass=None, domainguid=None,
1423 policyguid=None, policyguid_dc=None, invocationid=None,
1424 machinepass=None,
1425 dnspass=None, root=None, nobody=None, users=None,
1426 wheel=None, backup=None, serverrole=None,
1427 ldap_backend=None, ldap_backend_type=None,
1428 sitename=None, debuglevel=1):
1430 def message(text):
1431 """print a message if quiet is not set."""
1432 print text
1434 glue.set_debug_level(debuglevel)
1436 return provision(setup_dir, message, system_session(), None,
1437 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1438 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1439 configdn=configdn, serverdn=serverdn, domain=domain,
1440 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1441 machinepass=machinepass, serverrole="domain controller",
1442 sitename=sitename)
1445 def setup_db_config(setup_path, dbdir):
1446 """Setup a Berkeley database.
1448 :param setup_path: Setup path function.
1449 :param dbdir: Database directory."""
1450 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1451 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1452 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1453 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1455 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1456 {"LDAPDBDIR": dbdir})
1458 class ProvisionBackend(object):
1459 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1460 names=None, message=None,
1461 hostname=None, root=None,
1462 schema=None, ldapadminpass=None,
1463 ldap_backend_type=None, ldap_backend_extra_port=None,
1464 ol_mmr_urls=None,
1465 setup_ds_path=None, slapd_path=None,
1466 nosync=False, ldap_dryrun_mode=False):
1467 """Provision an LDAP backend for samba4
1469 This works for OpenLDAP and Fedora DS
1472 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1474 if not os.path.isdir(paths.ldapdir):
1475 os.makedirs(paths.ldapdir, 0700)
1477 if ldap_backend_type == "existing":
1478 #Check to see that this 'existing' LDAP backend in fact exists
1479 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1480 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1481 expression="(objectClass=OpenLDAProotDSE)")
1483 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1484 # This caused them to be set into the long-term database later in the script.
1485 self.credentials = credentials
1486 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1487 return
1489 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1490 # if another instance of slapd is already running
1491 try:
1492 ldapi_db = Ldb(self.ldapi_uri)
1493 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1494 expression="(objectClass=OpenLDAProotDSE)");
1495 try:
1496 f = open(paths.slapdpid, "r")
1497 p = f.read()
1498 f.close()
1499 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1500 except:
1501 pass
1503 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1505 except LdbError, e:
1506 pass
1508 # Try to print helpful messages when the user has not specified the path to slapd
1509 if slapd_path is None:
1510 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1511 if not os.path.exists(slapd_path):
1512 message (slapd_path)
1513 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1515 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1516 try:
1517 os.unlink(schemadb_path)
1518 except OSError:
1519 pass
1522 # Put the LDIF of the schema into a database so we can search on
1523 # it to generate schema-dependent configurations in Fedora DS and
1524 # OpenLDAP
1525 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1526 schema.ldb.connect(schemadb_path)
1527 schema.ldb.transaction_start()
1529 # These bits of LDIF are supplied when the Schema object is created
1530 schema.ldb.add_ldif(schema.schema_dn_add)
1531 schema.ldb.modify_ldif(schema.schema_dn_modify)
1532 schema.ldb.add_ldif(schema.schema_data)
1533 schema.ldb.transaction_commit()
1535 self.credentials = Credentials()
1536 self.credentials.guess(lp)
1537 #Kerberos to an ldapi:// backend makes no sense
1538 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1540 self.adminCredentials = Credentials()
1541 self.adminCredentials.guess(lp)
1542 #Kerberos to an ldapi:// backend makes no sense
1543 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1545 self.ldap_backend_type = ldap_backend_type
1547 if ldap_backend_type == "fedora-ds":
1548 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1549 names=names, message=message,
1550 hostname=hostname,
1551 ldapadminpass=ldapadminpass, root=root,
1552 schema=schema,
1553 ldap_backend_extra_port=ldap_backend_extra_port,
1554 setup_ds_path=setup_ds_path,
1555 slapd_path=slapd_path,
1556 nosync=nosync,
1557 ldap_dryrun_mode=ldap_dryrun_mode)
1559 elif ldap_backend_type == "openldap":
1560 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1561 names=names, message=message,
1562 hostname=hostname,
1563 ldapadminpass=ldapadminpass, root=root,
1564 schema=schema,
1565 ldap_backend_extra_port=ldap_backend_extra_port,
1566 ol_mmr_urls=ol_mmr_urls,
1567 slapd_path=slapd_path,
1568 nosync=nosync,
1569 ldap_dryrun_mode=ldap_dryrun_mode)
1570 else:
1571 raise ProvisioningError("Unknown LDAP backend type selected")
1573 self.credentials.set_password(ldapadminpass)
1574 self.adminCredentials.set_username("samba-admin")
1575 self.adminCredentials.set_password(ldapadminpass)
1577 # Now start the slapd, so we can provision onto it. We keep the
1578 # subprocess context around, to kill this off at the successful
1579 # end of the script
1580 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1582 while self.slapd.poll() is None:
1583 # Wait until the socket appears
1584 try:
1585 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1586 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1587 expression="(objectClass=OpenLDAProotDSE)")
1588 # If we have got here, then we must have a valid connection to the LDAP server!
1589 return
1590 except LdbError, e:
1591 time.sleep(1)
1592 pass
1594 raise ProvisioningError("slapd died before we could make a connection to it")
1597 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1598 message=None,
1599 hostname=None, ldapadminpass=None, root=None,
1600 schema=None,
1601 ldap_backend_extra_port=None,
1602 ol_mmr_urls=None,
1603 slapd_path=None, nosync=False,
1604 ldap_dryrun_mode=False):
1606 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1607 nosync_config = ""
1608 if nosync:
1609 nosync_config = "dbnosync"
1611 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1612 refint_attributes = ""
1613 memberof_config = "# Generated from Samba4 schema\n"
1614 for att in lnkattr.keys():
1615 if lnkattr[att] is not None:
1616 refint_attributes = refint_attributes + " " + att
1618 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1619 { "MEMBER_ATTR" : att ,
1620 "MEMBEROF_ATTR" : lnkattr[att] })
1622 refint_config = read_and_sub_file(setup_path("refint.conf"),
1623 { "LINK_ATTRS" : refint_attributes})
1625 attrs = ["linkID", "lDAPDisplayName"]
1626 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1627 index_config = ""
1628 for i in range (0, len(res)):
1629 index_attr = res[i]["lDAPDisplayName"][0]
1630 if index_attr == "objectGUID":
1631 index_attr = "entryUUID"
1633 index_config += "index " + index_attr + " eq\n"
1635 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1636 mmr_on_config = ""
1637 mmr_replicator_acl = ""
1638 mmr_serverids_config = ""
1639 mmr_syncrepl_schema_config = ""
1640 mmr_syncrepl_config_config = ""
1641 mmr_syncrepl_user_config = ""
1644 if ol_mmr_urls is not None:
1645 # For now, make these equal
1646 mmr_pass = ldapadminpass
1648 url_list=filter(None,ol_mmr_urls.split(' '))
1649 if (len(url_list) == 1):
1650 url_list=filter(None,ol_mmr_urls.split(','))
1653 mmr_on_config = "MirrorMode On"
1654 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1655 serverid=0
1656 for url in url_list:
1657 serverid=serverid+1
1658 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1659 { "SERVERID" : str(serverid),
1660 "LDAPSERVER" : url })
1661 rid=serverid*10
1662 rid=rid+1
1663 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1664 { "RID" : str(rid),
1665 "MMRDN": names.schemadn,
1666 "LDAPSERVER" : url,
1667 "MMR_PASSWORD": mmr_pass})
1669 rid=rid+1
1670 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1671 { "RID" : str(rid),
1672 "MMRDN": names.configdn,
1673 "LDAPSERVER" : url,
1674 "MMR_PASSWORD": mmr_pass})
1676 rid=rid+1
1677 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1678 { "RID" : str(rid),
1679 "MMRDN": names.domaindn,
1680 "LDAPSERVER" : url,
1681 "MMR_PASSWORD": mmr_pass })
1682 # OpenLDAP cn=config initialisation
1683 olc_syncrepl_config = ""
1684 olc_mmr_config = ""
1685 # if mmr = yes, generate cn=config-replication directives
1686 # and olc_seed.lif for the other mmr-servers
1687 if ol_mmr_urls is not None:
1688 serverid=0
1689 olc_serverids_config = ""
1690 olc_syncrepl_seed_config = ""
1691 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1692 rid=1000
1693 for url in url_list:
1694 serverid=serverid+1
1695 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1696 { "SERVERID" : str(serverid),
1697 "LDAPSERVER" : url })
1699 rid=rid+1
1700 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1701 { "RID" : str(rid),
1702 "LDAPSERVER" : url,
1703 "MMR_PASSWORD": mmr_pass})
1705 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1706 { "RID" : str(rid),
1707 "LDAPSERVER" : url})
1709 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1710 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1711 "OLC_PW": ldapadminpass,
1712 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1713 # end olc
1715 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1716 {"DNSDOMAIN": names.dnsdomain,
1717 "LDAPDIR": paths.ldapdir,
1718 "DOMAINDN": names.domaindn,
1719 "CONFIGDN": names.configdn,
1720 "SCHEMADN": names.schemadn,
1721 "MEMBEROF_CONFIG": memberof_config,
1722 "MIRRORMODE": mmr_on_config,
1723 "REPLICATOR_ACL": mmr_replicator_acl,
1724 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1725 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1726 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1727 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1728 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1729 "OLC_MMR_CONFIG": olc_mmr_config,
1730 "REFINT_CONFIG": refint_config,
1731 "INDEX_CONFIG": index_config,
1732 "NOSYNC": nosync_config})
1734 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1735 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1736 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1738 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1739 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1741 setup_file(setup_path("cn=samba.ldif"),
1742 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1743 { "UUID": str(uuid.uuid4()),
1744 "LDAPTIME": timestring(int(time.time()))} )
1745 setup_file(setup_path("cn=samba-admin.ldif"),
1746 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1747 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1748 "UUID": str(uuid.uuid4()),
1749 "LDAPTIME": timestring(int(time.time()))} )
1751 if ol_mmr_urls is not None:
1752 setup_file(setup_path("cn=replicator.ldif"),
1753 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1754 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1755 "UUID": str(uuid.uuid4()),
1756 "LDAPTIME": timestring(int(time.time()))} )
1759 mapping = "schema-map-openldap-2.3"
1760 backend_schema = "backend-schema.schema"
1762 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1763 assert backend_schema_data is not None
1764 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1766 # now we generate the needed strings to start slapd automatically,
1767 # first ldapi_uri...
1768 if ldap_backend_extra_port is not None:
1769 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1770 # specified there as part of it's clue as to it's own name,
1771 # and not to replicate to itself
1772 if ol_mmr_urls is None:
1773 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1774 else:
1775 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1776 else:
1777 server_port_string = ""
1779 # Prepare the 'result' information - the commands to return in particular
1780 result.slapd_provision_command = [slapd_path]
1782 result.slapd_provision_command.append("-F" + paths.olcdir)
1784 result.slapd_provision_command.append("-h")
1786 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1787 result.slapd_command = list(result.slapd_provision_command)
1789 result.slapd_provision_command.append(result.ldapi_uri)
1790 result.slapd_provision_command.append("-d0")
1792 uris = result.ldapi_uri
1793 if server_port_string is not "":
1794 uris = uris + " " + server_port_string
1796 result.slapd_command.append(uris)
1798 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1799 result.credentials.set_username("samba-admin")
1801 # If we were just looking for crashes up to this point, it's a
1802 # good time to exit before we realise we don't have OpenLDAP on
1803 # this system
1804 if ldap_dryrun_mode:
1805 sys.exit(0)
1807 # Finally, convert the configuration into cn=config style!
1808 if not os.path.isdir(paths.olcdir):
1809 os.makedirs(paths.olcdir, 0770)
1811 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1813 # We can't do this, as OpenLDAP is strange. It gives an error
1814 # output to the above, but does the conversion sucessfully...
1816 # if retcode != 0:
1817 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1819 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1820 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1822 # Don't confuse the admin by leaving the slapd.conf around
1823 os.remove(paths.slapdconf)
1826 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1827 message=None,
1828 hostname=None, ldapadminpass=None, root=None,
1829 schema=None,
1830 ldap_backend_extra_port=None,
1831 setup_ds_path=None,
1832 slapd_path=None,
1833 nosync=False,
1834 ldap_dryrun_mode=False):
1836 if ldap_backend_extra_port is not None:
1837 serverport = "ServerPort=%d" % ldap_backend_extra_port
1838 else:
1839 serverport = ""
1841 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1842 {"ROOT": root,
1843 "HOSTNAME": hostname,
1844 "DNSDOMAIN": names.dnsdomain,
1845 "LDAPDIR": paths.ldapdir,
1846 "DOMAINDN": names.domaindn,
1847 "LDAPMANAGERDN": names.ldapmanagerdn,
1848 "LDAPMANAGERPASS": ldapadminpass,
1849 "SERVERPORT": serverport})
1851 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1852 {"CONFIGDN": names.configdn,
1853 "SCHEMADN": names.schemadn,
1854 "SAMBADN": names.sambadn,
1857 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1858 {"SAMBADN": names.sambadn,
1861 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1862 {"SAMBADN": names.sambadn,
1863 "LDAPADMINPASS": ldapadminpass
1866 mapping = "schema-map-fedora-ds-1.0"
1867 backend_schema = "99_ad.ldif"
1869 # Build a schema file in Fedora DS format
1870 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1871 assert backend_schema_data is not None
1872 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1874 result.credentials.set_bind_dn(names.ldapmanagerdn)
1876 # Destory the target directory, or else setup-ds.pl will complain
1877 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1878 shutil.rmtree(fedora_ds_dir, True)
1880 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1881 #In the 'provision' command line, stay in the foreground so we can easily kill it
1882 result.slapd_provision_command.append("-d0")
1884 #the command for the final run is the normal script
1885 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1887 # If we were just looking for crashes up to this point, it's a
1888 # good time to exit before we realise we don't have Fedora DS on
1889 if ldap_dryrun_mode:
1890 sys.exit(0)
1892 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1893 if setup_ds_path is None:
1894 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\"!")
1895 if not os.path.exists(setup_ds_path):
1896 message (setup_ds_path)
1897 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1899 # Run the Fedora DS setup utility
1900 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1901 if retcode != 0:
1902 raise ProvisioningError("setup-ds failed")
1904 # Load samba-admin
1905 retcode = subprocess.call([
1906 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1907 close_fds=True, shell=False)
1908 if retcode != 0:
1909 raise("ldib2db failed")
1911 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1912 """Create a PHP LDAP admin configuration file.
1914 :param path: Path to write the configuration to.
1915 :param setup_path: Function to generate setup paths.
1917 setup_file(setup_path("phpldapadmin-config.php"), path,
1918 {"S4_LDAPI_URI": ldapi_uri})
1921 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1922 hostip, hostip6, hostname, dnspass, realm, domainguid,
1923 ntdsguid):
1924 """Write out a DNS zone file, from the info in the current database.
1926 :param path: Path of the new zone file.
1927 :param setup_path: Setup path function.
1928 :param dnsdomain: DNS Domain name
1929 :param domaindn: DN of the Domain
1930 :param hostip: Local IPv4 IP
1931 :param hostip6: Local IPv6 IP
1932 :param hostname: Local hostname
1933 :param dnspass: Password for DNS
1934 :param realm: Realm name
1935 :param domainguid: GUID of the domain.
1936 :param ntdsguid: GUID of the hosts nTDSDSA record.
1938 assert isinstance(domainguid, str)
1940 if hostip6 is not None:
1941 hostip6_base_line = " IN AAAA " + hostip6
1942 hostip6_host_line = hostname + " IN AAAA " + hostip6
1943 else:
1944 hostip6_base_line = ""
1945 hostip6_host_line = ""
1947 if hostip is not None:
1948 hostip_base_line = " IN A " + hostip
1949 hostip_host_line = hostname + " IN A " + hostip
1950 else:
1951 hostip_base_line = ""
1952 hostip_host_line = ""
1954 setup_file(setup_path("provision.zone"), path, {
1955 "DNSPASS_B64": b64encode(dnspass),
1956 "HOSTNAME": hostname,
1957 "DNSDOMAIN": dnsdomain,
1958 "REALM": realm,
1959 "HOSTIP_BASE_LINE": hostip_base_line,
1960 "HOSTIP_HOST_LINE": hostip_host_line,
1961 "DOMAINGUID": domainguid,
1962 "DATESTRING": time.strftime("%Y%m%d%H"),
1963 "DEFAULTSITE": DEFAULTSITE,
1964 "NTDSGUID": ntdsguid,
1965 "HOSTIP6_BASE_LINE": hostip6_base_line,
1966 "HOSTIP6_HOST_LINE": hostip6_host_line,
1970 def create_named_conf(path, setup_path, realm, dnsdomain,
1971 private_dir):
1972 """Write out a file containing zone statements suitable for inclusion in a
1973 named.conf file (including GSS-TSIG configuration).
1975 :param path: Path of the new named.conf file.
1976 :param setup_path: Setup path function.
1977 :param realm: Realm name
1978 :param dnsdomain: DNS Domain name
1979 :param private_dir: Path to private directory
1980 :param keytab_name: File name of DNS keytab file
1983 setup_file(setup_path("named.conf"), path, {
1984 "DNSDOMAIN": dnsdomain,
1985 "REALM": realm,
1986 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1987 "PRIVATE_DIR": private_dir
1990 def create_named_txt(path, setup_path, realm, dnsdomain,
1991 private_dir, keytab_name):
1992 """Write out a file containing zone statements suitable for inclusion in a
1993 named.conf file (including GSS-TSIG configuration).
1995 :param path: Path of the new named.conf file.
1996 :param setup_path: Setup path function.
1997 :param realm: Realm name
1998 :param dnsdomain: DNS Domain name
1999 :param private_dir: Path to private directory
2000 :param keytab_name: File name of DNS keytab file
2003 setup_file(setup_path("named.txt"), path, {
2004 "DNSDOMAIN": dnsdomain,
2005 "REALM": realm,
2006 "DNS_KEYTAB": keytab_name,
2007 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2008 "PRIVATE_DIR": private_dir
2011 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2012 """Write out a file containing zone statements suitable for inclusion in a
2013 named.conf file (including GSS-TSIG configuration).
2015 :param path: Path of the new named.conf file.
2016 :param setup_path: Setup path function.
2017 :param dnsdomain: DNS Domain name
2018 :param hostname: Local hostname
2019 :param realm: Realm name
2022 setup_file(setup_path("krb5.conf"), path, {
2023 "DNSDOMAIN": dnsdomain,
2024 "HOSTNAME": hostname,
2025 "REALM": realm,