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
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
, \
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
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"
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
):
68 ret
= os
.path
.join(dirname
, "../../../setup")
69 if os
.path
.isdir(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):
95 self
.dns_keytab
= None
98 self
.private_dir
= None
100 self
.slapdconf
= None
101 self
.modulesconf
= None
102 self
.memberofconf
= None
103 self
.fedoradsinf
= None
104 self
.fedoradspartitions
= None
106 self
.olmmrserveridsconf
= None
107 self
.olmmrsyncreplconf
= None
110 self
.olcseedldif
= None
113 class ProvisionNames(object):
119 self
.ldapmanagerdn
= None
120 self
.dnsdomain
= None
122 self
.netbiosname
= None
129 class ProvisionResult(object):
136 class Schema(object):
137 def __init__(self
, setup_path
, schemadn
=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
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.
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
)
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
)
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()
256 setup_add_ldif(ldb
, ldif_path
, subst_vars
)
258 ldb
.transaction_cancel()
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.
272 if os
.path
.exists(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
,
305 paths
.slapdconf
= os
.path
.join(paths
.ldapdir
,
307 paths
.slapdpid
= os
.path
.join(paths
.ldapdir
,
309 paths
.modulesconf
= os
.path
.join(paths
.ldapdir
,
311 paths
.memberofconf
= os
.path
.join(paths
.ldapdir
,
313 paths
.fedoradsinf
= os
.path
.join(paths
.ldapdir
,
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
,
321 paths
.olcdir
= os
.path
.join(paths
.ldapdir
,
323 paths
.olcseedldif
= os
.path
.join(paths
.ldapdir
,
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
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,
344 """Guess configuration settings to use."""
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":
372 domain
= lp
.get("workgroup")
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
)
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
)
392 configdn
= "CN=Configuration," + rootdn
394 schemadn
= "CN=Schema," + configdn
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
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
)
416 def make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
418 """Create a new smb.conf file based on a couple of basic settings.
420 assert smbconf
is not 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":
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
))
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
),
457 "HOSTNAME": hostname
,
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."""
484 dn: CN=%s,CN=ForeignSecurityPrincipals,%s
486 objectClass: foreignSecurityPrincipal
488 """ % (sid
, domaindn
, desc
)
489 # deliberately ignore errors from this, as the records may
491 for msg
in self
.parse_ldif(add
):
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
,
508 serverrole
, ldap_backend
=None,
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
526 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
527 credentials
=credentials
, lp
=lp
, options
=["modules:"])
529 samdb
.erase_except_schema_controlled()
531 os
.unlink(samdb_path
)
532 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
533 credentials
=credentials
, lp
=lp
, options
=["modules:"])
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",
566 "extended_dn_out_ldb"]
567 modules_list2
= ["show_deleted",
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"]
590 backend_modules
= ["objectguid"]
592 if tdb_modules_list
is None:
593 tdb_modules_list_as_string
= ""
595 tdb_modules_list_as_string
= ","+",".join(tdb_modules_list
)
597 samdb
.transaction_start()
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
)
623 samdb
.transaction_cancel()
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
),
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
):
665 secrets_ldb
= Ldb(path
, session_info
=session_info
, credentials
=credentials
,
668 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
669 secrets_ldb
= Ldb(path
, session_info
=session_info
, credentials
=credentials
,
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())
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())
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
,
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
707 templates_ldb
.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
709 templates_ldb
= Ldb(url
=path
, session_info
=session_info
,
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
,
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
):
745 idmap_ldb
= IDmapDB(path
, session_info
=session_info
,
749 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
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
,
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
,
862 ldap_backend
=ldap_backend
, serverrole
=serverrole
)
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
880 samdb
.load_ldif_file_add(setup_path("provision_options.ldif"))
885 samdb
.transaction_start()
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"
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
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
,
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
,
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
,
1045 machinepass
=machinepass
,
1046 domainsid
=domainsid
, policyguid
=policyguid
,
1047 setup_path
=setup_path
, domainControllerFunctionality
=domainControllerFunctionality
)
1050 samdb
.transaction_cancel()
1053 samdb
.transaction_commit()
1058 FILL_NT4SYNC
= "NT4SYNC"
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,
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):
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)
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"])
1106 wheel_gid
= findnss_gid(["wheel", "adm"])
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
,
1122 lp
= param
.LoadParm()
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
,
1130 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
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
):
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
):
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
,
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
,
1193 message("Setting up templates db")
1194 setup_templatesdb(paths
.templates
, setup_path
, session_info
=session_info
,
1197 message("Setting up idmap db")
1198 idmap
= setup_idmapdb(paths
.idmapdb
, setup_path
, session_info
=session_info
,
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
,
1205 domainsid
=domainsid
,
1206 schema
=schema
, domainguid
=domainguid
, policyguid
=policyguid
,
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:
1284 if hasattr(provision_backend
.slapd
, "terminate"):
1285 provision_backend
.slapd
.terminate()
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
,
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())
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
1328 result
.samdb
= samdb
1333 def provision_become_dc(setup_dir
=None,
1334 smbconf
=None, targetdir
=None, realm
=None,
1335 rootdn
=None, domaindn
=None, schemadn
=None, configdn
=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):
1345 """print a message if quiet is not set."""
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,
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
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
1401 ldapi_db
= Ldb(self
.ldapi_uri
)
1402 search_ol_rootdse
= ldapi_db
.search(base
="", scope
=SCOPE_BASE
,
1403 expression
="(objectClass=OpenLDAProotDSE)");
1405 f
= open(paths
.slapdpid
, "r")
1408 message("Check for slapd Process with PID: " + str(p
) + " and terminate it manually.")
1412 raise("Warning: Another slapd Instance seems already running on this host, listening to " + self
.ldapi_uri
+ ". Please shut it down before you continue. ")
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")
1426 os
.unlink(schemadb_path
)
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
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
)
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
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
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!
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,
1492 ldap_backend_extra_port
=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
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
)
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
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"
1554 for url
in url_list
:
1556 mmr_serverids_config
+= read_and_sub_file(setup_path("mmr_serverids.conf"),
1557 { "SERVERID" : str(serverid
),
1558 "LDAPSERVER" : url
})
1561 mmr_syncrepl_schema_config
+= read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1563 "MMRDN": names
.schemadn
,
1565 "MMR_PASSWORD": mmr_pass
})
1568 mmr_syncrepl_config_config
+= read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1570 "MMRDN": names
.configdn
,
1572 "MMR_PASSWORD": mmr_pass
})
1575 mmr_syncrepl_user_config
+= read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1577 "MMRDN": names
.domaindn
,
1579 "MMR_PASSWORD": mmr_pass
})
1580 # OpenLDAP cn=config initialisation
1581 olc_syncrepl_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:
1587 olc_serverids_config
= ""
1588 olc_syncrepl_seed_config
= ""
1589 olc_mmr_config
+= read_and_sub_file(setup_path("olc_mmr.conf"),{})
1591 for url
in url_list
:
1593 olc_serverids_config
+= read_and_sub_file(setup_path("olc_serverid.conf"),
1594 { "SERVERID" : str(serverid
),
1595 "LDAPSERVER" : url
})
1598 olc_syncrepl_config
+= read_and_sub_file(setup_path("olc_syncrepl.conf"),
1601 "MMR_PASSWORD": mmr_pass
})
1603 olc_syncrepl_seed_config
+= read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
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
})
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
1673 server_port_string
= "ldap://" + names
.hostname
+ "." + names
.dnsdomain
+":%d" % ldap_backend_extra_port
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
1702 if ldap_dryrun_mode
:
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...
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,
1727 ldap_backend_extra_port
=None,
1731 ldap_dryrun_mode
=False):
1733 if ldap_backend_extra_port
is not None:
1734 serverport
= "ServerPort=%d" % ldap_backend_extra_port
1738 setup_file(setup_path("fedorads.inf"), paths
.fedoradsinf
,
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
:
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)
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
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
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
,
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
,
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
,
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
,
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
,