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
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
50 from ldb
import SCOPE_SUBTREE
, SCOPE_ONELEVEL
, SCOPE_BASE
, LdbError
, timestring
51 from ms_schema
import read_ms_schema
52 from signal
import SIGTERM
54 __docformat__
= "restructuredText"
58 """Find the setup directory used by provision."""
59 dirname
= os
.path
.dirname(__file__
)
60 if "/site-packages/" in dirname
:
61 prefix
= "/".join(dirname
[:dirname
.index("/site-packages/")].split("/")[:-2])
62 for suffix
in ["share/setup", "share/samba/setup", "setup"]:
63 ret
= os
.path
.join(prefix
, suffix
)
64 if os
.path
.isdir(ret
):
67 ret
= os
.path
.join(dirname
, "../../../setup")
68 if os
.path
.isdir(ret
):
70 raise Exception("Unable to find setup directory.")
73 DEFAULTSITE
= "Default-First-Site-Name"
75 class InvalidNetbiosName(Exception):
76 """A specified name was not a valid NetBIOS name."""
77 def __init__(self
, name
):
78 super(InvalidNetbiosName
, self
).__init
__("The name '%r' is not a valid NetBIOS name" % name
)
81 class ProvisionPaths(object):
94 self
.dns_keytab
= None
97 self
.private_dir
= None
100 self
.modulesconf
= None
101 self
.memberofconf
= None
102 self
.fedoradsinf
= None
103 self
.fedoradspartitions
= None
105 self
.olmmrserveridsconf
= None
106 self
.olmmrsyncreplconf
= None
109 self
.olcseedldif
= None
112 class ProvisionNames(object):
118 self
.ldapmanagerdn
= None
119 self
.dnsdomain
= None
121 self
.netbiosname
= None
128 class ProvisionResult(object):
135 class Schema(object):
136 def __init__(self
, setup_path
, schemadn
=None,
138 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
140 :param samdb: Load a schema into a SamDB.
141 :param setup_path: Setup path function.
142 :param schemadn: DN of the schema
143 :param serverdn: DN of the server
145 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
149 self
.schema_data
= read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
150 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
151 self
.schema_data
+= open(setup_path("schema_samba4.ldif"), 'r').read()
152 self
.schema_data
= substitute_var(self
.schema_data
, {"SCHEMADN": schemadn
})
153 check_all_substituted(self
.schema_data
)
155 self
.schema_dn_modify
= read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
156 {"SCHEMADN": schemadn
,
157 "SERVERDN": serverdn
,
159 self
.schema_dn_add
= read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
160 {"SCHEMADN": schemadn
163 prefixmap
= open(setup_path("prefixMap.txt"), 'r').read()
164 prefixmap
= b64encode(prefixmap
)
166 # We don't actually add this ldif, just parse it
167 prefixmap_ldif
= "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
168 self
.ldb
.set_schema_from_ldif(prefixmap_ldif
, self
.schema_data
)
171 # Return a hash with the forward attribute as a key and the back as the value
172 def get_linked_attributes(schemadn
,schemaldb
):
173 attrs
= ["linkID", "lDAPDisplayName"]
174 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
)
176 for i
in range (0, len(res
)):
177 expression
= "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res
[i
]["linkID"][0])+1)
178 target
= schemaldb
.searchone(basedn
=schemadn
,
179 expression
=expression
,
180 attribute
="lDAPDisplayName",
182 if target
is not None:
183 attributes
[str(res
[i
]["lDAPDisplayName"])]=str(target
)
187 def get_dnsyntax_attributes(schemadn
,schemaldb
):
188 attrs
= ["linkID", "lDAPDisplayName"]
189 res
= schemaldb
.search(expression
="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base
=schemadn
, scope
=SCOPE_ONELEVEL
, attrs
=attrs
)
191 for i
in range (0, len(res
)):
192 attributes
.append(str(res
[i
]["lDAPDisplayName"]))
197 def check_install(lp
, session_info
, credentials
):
198 """Check whether the current install seems ok.
200 :param lp: Loadparm context
201 :param session_info: Session information
202 :param credentials: Credentials
204 if lp
.get("realm") == "":
205 raise Exception("Realm empty")
206 ldb
= Ldb(lp
.get("sam database"), session_info
=session_info
,
207 credentials
=credentials
, lp
=lp
)
208 if len(ldb
.search("(cn=Administrator)")) != 1:
209 raise "No administrator account found"
212 def findnss(nssfn
, names
):
213 """Find a user or group from a list of possibilities.
215 :param nssfn: NSS Function to try (should raise KeyError if not found)
216 :param names: Names to check.
217 :return: Value return by first names list.
224 raise KeyError("Unable to find user/group %r" % names
)
227 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
228 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
231 def read_and_sub_file(file, subst_vars
):
232 """Read a file and sub in variables found in it
234 :param file: File to be read (typically from setup directory)
235 param subst_vars: Optional variables to subsitute in the file.
237 data
= open(file, 'r').read()
238 if subst_vars
is not None:
239 data
= substitute_var(data
, subst_vars
)
240 check_all_substituted(data
)
244 def setup_add_ldif(ldb
, ldif_path
, subst_vars
=None):
245 """Setup a ldb in the private dir.
247 :param ldb: LDB file to import data into
248 :param ldif_path: Path of the LDIF file to load
249 :param subst_vars: Optional variables to subsitute in LDIF.
251 assert isinstance(ldif_path
, str)
253 data
= read_and_sub_file(ldif_path
, subst_vars
)
257 def setup_modify_ldif(ldb
, ldif_path
, subst_vars
=None):
258 """Modify a ldb in the private dir.
260 :param ldb: LDB object.
261 :param ldif_path: LDIF file path.
262 :param subst_vars: Optional dictionary with substitution variables.
264 data
= read_and_sub_file(ldif_path
, subst_vars
)
266 ldb
.modify_ldif(data
)
269 def setup_ldb(ldb
, ldif_path
, subst_vars
):
270 """Import a LDIF a file into a LDB handle, optionally substituting variables.
272 :note: Either all LDIF data will be added or none (using transactions).
274 :param ldb: LDB file to import into.
275 :param ldif_path: Path to the LDIF file.
276 :param subst_vars: Dictionary with substitution variables.
278 assert ldb
is not None
279 ldb
.transaction_start()
281 setup_add_ldif(ldb
, ldif_path
, subst_vars
)
283 ldb
.transaction_cancel()
285 ldb
.transaction_commit()
288 def setup_file(template
, fname
, subst_vars
):
289 """Setup a file in the private dir.
291 :param template: Path of the template file.
292 :param fname: Path of the file to create.
293 :param subst_vars: Substitution variables.
297 if os
.path
.exists(f
):
300 data
= read_and_sub_file(template
, subst_vars
)
301 open(f
, 'w').write(data
)
304 def provision_paths_from_lp(lp
, dnsdomain
):
305 """Set the default paths for provisioning.
307 :param lp: Loadparm context.
308 :param dnsdomain: DNS Domain name
310 paths
= ProvisionPaths()
311 paths
.private_dir
= lp
.get("private dir")
312 paths
.keytab
= "secrets.keytab"
313 paths
.dns_keytab
= "dns.keytab"
315 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
316 paths
.samdb
= os
.path
.join(paths
.private_dir
, lp
.get("sam database") or "samdb.ldb")
317 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, lp
.get("idmap database") or "idmap.ldb")
318 paths
.secrets
= os
.path
.join(paths
.private_dir
, lp
.get("secrets database") or "secrets.ldb")
319 paths
.templates
= os
.path
.join(paths
.private_dir
, "templates.ldb")
320 paths
.dns
= os
.path
.join(paths
.private_dir
, dnsdomain
+ ".zone")
321 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
322 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
323 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
324 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
325 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
326 paths
.phpldapadminconfig
= os
.path
.join(paths
.private_dir
,
327 "phpldapadmin-config.php")
328 paths
.ldapdir
= os
.path
.join(paths
.private_dir
,
330 paths
.slapdconf
= os
.path
.join(paths
.ldapdir
,
332 paths
.slapdpid
= os
.path
.join(paths
.ldapdir
,
334 paths
.modulesconf
= os
.path
.join(paths
.ldapdir
,
336 paths
.memberofconf
= os
.path
.join(paths
.ldapdir
,
338 paths
.fedoradsinf
= os
.path
.join(paths
.ldapdir
,
340 paths
.fedoradspartitions
= os
.path
.join(paths
.ldapdir
,
341 "fedorads-partitions.ldif")
342 paths
.olmmrserveridsconf
= os
.path
.join(paths
.ldapdir
,
343 "mmr_serverids.conf")
344 paths
.olmmrsyncreplconf
= os
.path
.join(paths
.ldapdir
,
346 paths
.olcdir
= os
.path
.join(paths
.ldapdir
,
348 paths
.olcseedldif
= os
.path
.join(paths
.ldapdir
,
350 paths
.hklm
= "hklm.ldb"
351 paths
.hkcr
= "hkcr.ldb"
352 paths
.hkcu
= "hkcu.ldb"
353 paths
.hku
= "hku.ldb"
354 paths
.hkpd
= "hkpd.ldb"
355 paths
.hkpt
= "hkpt.ldb"
357 paths
.sysvol
= lp
.get("path", "sysvol")
359 paths
.netlogon
= lp
.get("path", "netlogon")
361 paths
.smbconf
= lp
.configfile
366 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None, serverrole
=None,
367 rootdn
=None, domaindn
=None, configdn
=None, schemadn
=None, serverdn
=None,
369 """Guess configuration settings to use."""
372 hostname
= socket
.gethostname().split(".")[0].lower()
374 netbiosname
= hostname
.upper()
375 if not valid_netbios_name(netbiosname
):
376 raise InvalidNetbiosName(netbiosname
)
378 hostname
= hostname
.lower()
380 if dnsdomain
is None:
381 dnsdomain
= lp
.get("realm")
383 if serverrole
is None:
384 serverrole
= lp
.get("server role")
386 assert dnsdomain
is not None
387 realm
= dnsdomain
.upper()
389 if lp
.get("realm").upper() != realm
:
390 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
391 (lp
.get("realm"), lp
.configfile
, realm
))
393 dnsdomain
= dnsdomain
.lower()
395 if serverrole
== "domain controller":
397 domain
= lp
.get("workgroup")
399 domaindn
= "DC=" + dnsdomain
.replace(".", ",DC=")
400 if lp
.get("workgroup").upper() != domain
.upper():
401 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
402 lp
.get("workgroup"), domain
)
406 domaindn
= "CN=" + netbiosname
408 assert domain
is not None
409 domain
= domain
.upper()
410 if not valid_netbios_name(domain
):
411 raise InvalidNetbiosName(domain
)
417 configdn
= "CN=Configuration," + rootdn
419 schemadn
= "CN=Schema," + configdn
424 names
= ProvisionNames()
425 names
.rootdn
= rootdn
426 names
.domaindn
= domaindn
427 names
.configdn
= configdn
428 names
.schemadn
= schemadn
429 names
.ldapmanagerdn
= "CN=Manager," + rootdn
430 names
.dnsdomain
= dnsdomain
431 names
.domain
= domain
433 names
.netbiosname
= netbiosname
434 names
.hostname
= hostname
435 names
.sitename
= sitename
436 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname
, sitename
, configdn
)
441 def make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
443 """Create a new smb.conf file based on a couple of basic settings.
445 assert smbconf
is not None
447 hostname
= socket
.gethostname().split(".")[0].lower()
449 if serverrole
is None:
450 serverrole
= "standalone"
452 assert serverrole
in ("domain controller", "member server", "standalone")
453 if serverrole
== "domain controller":
455 elif serverrole
== "member server":
456 smbconfsuffix
= "member"
457 elif serverrole
== "standalone":
458 smbconfsuffix
= "standalone"
460 assert domain
is not None
461 assert realm
is not None
463 default_lp
= param
.LoadParm()
464 #Load non-existant file
465 if os
.path
.exists(smbconf
):
466 default_lp
.load(smbconf
)
468 if targetdir
is not None:
469 privatedir_line
= "private dir = " + os
.path
.abspath(os
.path
.join(targetdir
, "private"))
470 lockdir_line
= "lock dir = " + os
.path
.abspath(targetdir
)
472 default_lp
.set("lock dir", os
.path
.abspath(targetdir
))
477 sysvol
= os
.path
.join(default_lp
.get("lock dir"), "sysvol")
478 netlogon
= os
.path
.join(sysvol
, realm
.lower(), "scripts")
480 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix
),
482 "HOSTNAME": hostname
,
485 "SERVERROLE": serverrole
,
486 "NETLOGONPATH": netlogon
,
487 "SYSVOLPATH": sysvol
,
488 "PRIVATEDIR_LINE": privatedir_line
,
489 "LOCKDIR_LINE": lockdir_line
493 def setup_name_mappings(samdb
, idmap
, sid
, domaindn
, root_uid
, nobody_uid
,
494 users_gid
, wheel_gid
):
495 """setup reasonable name mappings for sam names to unix names.
497 :param samdb: SamDB object.
498 :param idmap: IDmap db object.
499 :param sid: The domain sid.
500 :param domaindn: The domain DN.
501 :param root_uid: uid of the UNIX root user.
502 :param nobody_uid: uid of the UNIX nobody user.
503 :param users_gid: gid of the UNIX users group.
504 :param wheel_gid: gid of the UNIX wheel group."""
506 def add_foreign(self
, domaindn
, sid
, desc
):
507 """Add a foreign security principle."""
509 dn: CN=%s,CN=ForeignSecurityPrincipals,%s
511 objectClass: foreignSecurityPrincipal
513 """ % (sid
, domaindn
, desc
)
514 # deliberately ignore errors from this, as the records may
516 for msg
in self
.parse_ldif(add
):
519 add_foreign(samdb
, domaindn
, "S-1-5-7", "Anonymous")
520 add_foreign(samdb
, domaindn
, "S-1-1-0", "World")
521 add_foreign(samdb
, domaindn
, "S-1-5-2", "Network")
522 add_foreign(samdb
, domaindn
, "S-1-5-18", "System")
523 add_foreign(samdb
, domaindn
, "S-1-5-11", "Authenticated Users")
525 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
526 idmap
.setup_name_mapping("S-1-5-32-544", idmap
.TYPE_GID
, wheel_gid
)
528 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
529 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
531 def setup_samdb_partitions(samdb_path
, setup_path
, message
, lp
, session_info
,
533 serverrole
, ldap_backend
=None,
535 """Setup the partitions for the SAM database.
537 Alternatively, provision() may call this, and then populate the database.
539 :note: This will wipe the Sam Database!
541 :note: This function always removes the local SAM LDB file. The erase
542 parameter controls whether to erase the existing data, which
543 may not be stored locally but in LDAP.
545 assert session_info
is not None
547 # We use options=["modules:"] to stop the modules loading - we
548 # just want to wipe and re-initialise the database, not start it up
551 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
552 credentials
=credentials
, lp
=lp
, options
=["modules:"])
554 samdb
.erase_except_schema_controlled()
556 os
.unlink(samdb_path
)
557 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
558 credentials
=credentials
, lp
=lp
, options
=["modules:"])
560 samdb
.erase_except_schema_controlled()
563 #Add modules to the list to activate them by default
564 #beware often order is important
566 # Some Known ordering constraints:
567 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
568 # - objectclass must be before password_hash, because password_hash checks
569 # that the objectclass is of type person (filled in by objectclass
570 # module when expanding the objectclass list)
571 # - partition must be last
572 # - each partition has its own module list then
573 modules_list
= ["rootdse",
592 "extended_dn_out_ldb"]
593 modules_list2
= ["show_deleted",
596 domaindn_ldb
= "users.ldb"
597 configdn_ldb
= "configuration.ldb"
598 schemadn_ldb
= "schema.ldb"
599 if ldap_backend
is not None:
600 domaindn_ldb
= ldap_backend
.ldapi_uri
601 configdn_ldb
= ldap_backend
.ldapi_uri
602 schemadn_ldb
= ldap_backend
.ldapi_uri
604 if ldap_backend
.ldap_backend_type
== "fedora-ds":
605 backend_modules
= ["nsuniqueid", "paged_searches"]
606 # We can handle linked attributes here, as we don't have directory-side subtree operations
607 tdb_modules_list
= ["linked_attributes", "extended_dn_out_dereference"]
608 elif ldap_backend
.ldap_backend_type
== "openldap":
609 backend_modules
= ["entryuuid", "paged_searches"]
610 # OpenLDAP handles subtree renames, so we don't want to do any of these things
611 tdb_modules_list
= ["extended_dn_out_dereference"]
613 elif serverrole
== "domain controller":
616 backend_modules
= ["objectguid"]
618 if tdb_modules_list
is None:
619 tdb_modules_list_as_string
= ""
621 tdb_modules_list_as_string
= ","+",".join(tdb_modules_list
)
623 samdb
.transaction_start()
625 message("Setting up sam.ldb partitions and settings")
626 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
627 "SCHEMADN": names
.schemadn
,
628 "SCHEMADN_LDB": schemadn_ldb
,
629 "SCHEMADN_MOD2": ",objectguid",
630 "CONFIGDN": names
.configdn
,
631 "CONFIGDN_LDB": configdn_ldb
,
632 "DOMAINDN": names
.domaindn
,
633 "DOMAINDN_LDB": domaindn_ldb
,
634 "SCHEMADN_MOD": "schema_fsmo,instancetype",
635 "CONFIGDN_MOD": "naming_fsmo,instancetype",
636 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
637 "MODULES_LIST": ",".join(modules_list
),
638 "TDB_MODULES_LIST": tdb_modules_list_as_string
,
639 "MODULES_LIST2": ",".join(modules_list2
),
640 "BACKEND_MOD": ",".join(backend_modules
),
643 samdb
.load_ldif_file_add(setup_path("provision_init.ldif"))
645 message("Setting up sam.ldb rootDSE")
646 setup_samdb_rootdse(samdb
, setup_path
, names
)
649 samdb
.transaction_cancel()
652 samdb
.transaction_commit()
656 def secretsdb_become_dc(secretsdb
, setup_path
, domain
, realm
, dnsdomain
,
657 netbiosname
, domainsid
, keytab_path
, samdb_url
,
658 dns_keytab_path
, dnspass
, machinepass
):
659 """Add DC-specific bits to a secrets database.
661 :param secretsdb: Ldb Handle to the secrets database
662 :param setup_path: Setup path function
663 :param machinepass: Machine password
665 setup_ldb(secretsdb
, setup_path("secrets_dc.ldif"), {
666 "MACHINEPASS_B64": b64encode(machinepass
),
669 "DNSDOMAIN": dnsdomain
,
670 "DOMAINSID": str(domainsid
),
671 "SECRETS_KEYTAB": keytab_path
,
672 "NETBIOSNAME": netbiosname
,
673 "SAM_LDB": samdb_url
,
674 "DNS_KEYTAB": dns_keytab_path
,
675 "DNSPASS_B64": b64encode(dnspass
),
679 def setup_secretsdb(path
, setup_path
, session_info
, credentials
, lp
):
680 """Setup the secrets database.
682 :param path: Path to the secrets database.
683 :param setup_path: Get the path to a setup file.
684 :param session_info: Session info.
685 :param credentials: Credentials
686 :param lp: Loadparm context
687 :return: LDB handle for the created secrets database
689 if os
.path
.exists(path
):
691 secrets_ldb
= Ldb(path
, session_info
=session_info
, credentials
=credentials
,
694 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
695 secrets_ldb
= Ldb(path
, session_info
=session_info
, credentials
=credentials
,
697 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
699 if credentials
is not None and credentials
.authentication_requested():
700 if credentials
.get_bind_dn() is not None:
701 setup_add_ldif(secrets_ldb
, setup_path("secrets_simple_ldap.ldif"), {
702 "LDAPMANAGERDN": credentials
.get_bind_dn(),
703 "LDAPMANAGERPASS_B64": b64encode(credentials
.get_password())
706 setup_add_ldif(secrets_ldb
, setup_path("secrets_sasl_ldap.ldif"), {
707 "LDAPADMINUSER": credentials
.get_username(),
708 "LDAPADMINREALM": credentials
.get_realm(),
709 "LDAPADMINPASS_B64": b64encode(credentials
.get_password())
715 def setup_templatesdb(path
, setup_path
, session_info
, lp
):
716 """Setup the templates database.
718 :param path: Path to the database.
719 :param setup_path: Function for obtaining the path to setup files.
720 :param session_info: Session info
721 :param credentials: Credentials
722 :param lp: Loadparm context
724 templates_ldb
= Ldb(url
=path
, session_info
=session_info
,
728 templates_ldb
.erase()
729 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
733 templates_ldb
.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
735 templates_ldb
= Ldb(url
=path
, session_info
=session_info
,
738 templates_ldb
.load_ldif_file_add(setup_path("provision_templates.ldif"))
741 def setup_registry(path
, setup_path
, session_info
, lp
):
742 """Setup the registry.
744 :param path: Path to the registry database
745 :param setup_path: Function that returns the path to a setup.
746 :param session_info: Session information
747 :param credentials: Credentials
748 :param lp: Loadparm context
750 reg
= registry
.Registry()
751 hive
= registry
.open_ldb(path
, session_info
=session_info
,
753 reg
.mount_hive(hive
, registry
.HKEY_LOCAL_MACHINE
)
754 provision_reg
= setup_path("provision.reg")
755 assert os
.path
.exists(provision_reg
)
756 reg
.diff_apply(provision_reg
)
759 def setup_idmapdb(path
, setup_path
, session_info
, lp
):
760 """Setup the idmap database.
762 :param path: path to the idmap database
763 :param setup_path: Function that returns a path to a setup file
764 :param session_info: Session information
765 :param credentials: Credentials
766 :param lp: Loadparm context
768 if os
.path
.exists(path
):
771 idmap_ldb
= IDmapDB(path
, session_info
=session_info
,
775 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
779 def setup_samdb_rootdse(samdb
, setup_path
, names
):
780 """Setup the SamDB rootdse.
782 :param samdb: Sam Database handle
783 :param setup_path: Obtain setup path
785 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
786 "SCHEMADN": names
.schemadn
,
787 "NETBIOSNAME": names
.netbiosname
,
788 "DNSDOMAIN": names
.dnsdomain
,
789 "REALM": names
.realm
,
790 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
791 "DOMAINDN": names
.domaindn
,
792 "ROOTDN": names
.rootdn
,
793 "CONFIGDN": names
.configdn
,
794 "SERVERDN": names
.serverdn
,
798 def setup_self_join(samdb
, names
,
799 machinepass
, dnspass
,
800 domainsid
, invocationid
, setup_path
,
801 policyguid
, domainControllerFunctionality
):
802 """Join a host to its own domain."""
803 assert isinstance(invocationid
, str)
804 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
805 "CONFIGDN": names
.configdn
,
806 "SCHEMADN": names
.schemadn
,
807 "DOMAINDN": names
.domaindn
,
808 "SERVERDN": names
.serverdn
,
809 "INVOCATIONID": invocationid
,
810 "NETBIOSNAME": names
.netbiosname
,
811 "DEFAULTSITE": names
.sitename
,
812 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
813 "MACHINEPASS_B64": b64encode(machinepass
),
814 "DNSPASS_B64": b64encode(dnspass
),
815 "REALM": names
.realm
,
816 "DOMAIN": names
.domain
,
817 "DNSDOMAIN": names
.dnsdomain
,
818 "SAMBA_VERSION_STRING": version
,
819 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality
)})
821 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
822 "POLICYGUID": policyguid
,
823 "DNSDOMAIN": names
.dnsdomain
,
824 "DOMAINSID": str(domainsid
),
825 "DOMAINDN": names
.domaindn
})
827 # Setup fSMORoleOwner entries to point at the newly created DC entry
828 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
829 "DOMAINDN": names
.domaindn
,
830 "CONFIGDN": names
.configdn
,
831 "SCHEMADN": names
.schemadn
,
832 "DEFAULTSITE": names
.sitename
,
833 "SERVERDN": names
.serverdn
837 def setup_samdb(path
, setup_path
, session_info
, credentials
, lp
,
839 domainsid
, domainguid
, policyguid
,
840 fill
, adminpass
, krbtgtpass
,
841 machinepass
, invocationid
, dnspass
,
842 serverrole
, schema
=None, ldap_backend
=None):
843 """Setup a complete SAM Database.
845 :note: This will wipe the main SAM database file!
848 domainFunctionality
= DS_BEHAVIOR_WIN2008
849 forestFunctionality
= DS_BEHAVIOR_WIN2008
850 domainControllerFunctionality
= DS_BEHAVIOR_WIN2008
852 # Also wipes the database
853 setup_samdb_partitions(path
, setup_path
, message
=message
, lp
=lp
,
854 credentials
=credentials
, session_info
=session_info
,
856 ldap_backend
=ldap_backend
, serverrole
=serverrole
)
859 schema
= Schema(setup_path
, schemadn
=names
.schemadn
, serverdn
=names
.serverdn
)
861 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
862 samdb
= Ldb(session_info
=session_info
,
863 credentials
=credentials
, lp
=lp
)
865 message("Pre-loading the Samba 4 and AD schema")
867 # Load the schema from the one we computed earlier
868 samdb
.set_schema_from_ldb(schema
.ldb
)
870 # And now we can connect to the DB - the schema won't be loaded from the DB
874 samdb
.load_ldif_file_add(setup_path("provision_options.ldif"))
879 samdb
.transaction_start()
881 message("Erasing data from partitions")
882 # Load the schema (again). This time it will force a reindex,
883 # and will therefore make the erase_partitions() below
884 # computationally sane
885 samdb
.set_schema_from_ldb(schema
.ldb
)
886 samdb
.erase_partitions()
888 # Set the domain functionality levels onto the database.
889 # Various module (the password_hash module in particular) need
890 # to know what level of AD we are emulating.
892 # These will be fixed into the database via the database
893 # modifictions below, but we need them set from the start.
894 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
895 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
896 samdb
.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality
)
898 samdb
.set_domain_sid(str(domainsid
))
899 if serverrole
== "domain controller":
900 samdb
.set_invocation_id(invocationid
)
902 message("Adding DomainDN: %s" % names
.domaindn
)
903 if serverrole
== "domain controller":
904 domain_oc
= "domainDNS"
906 domain_oc
= "samba4LocalDomain"
908 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
909 "DOMAINDN": names
.domaindn
,
910 "DOMAIN_OC": domain_oc
913 message("Modifying DomainDN: " + names
.domaindn
+ "")
914 if domainguid
is not None:
915 domainguid_mod
= "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
919 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
920 "LDAPTIME": timestring(int(time
.time())),
921 "DOMAINSID": str(domainsid
),
922 "SCHEMADN": names
.schemadn
,
923 "NETBIOSNAME": names
.netbiosname
,
924 "DEFAULTSITE": names
.sitename
,
925 "CONFIGDN": names
.configdn
,
926 "SERVERDN": names
.serverdn
,
927 "POLICYGUID": policyguid
,
928 "DOMAINDN": names
.domaindn
,
929 "DOMAINGUID_MOD": domainguid_mod
,
930 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
)
933 message("Adding configuration container")
934 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
935 "CONFIGDN": names
.configdn
,
937 message("Modifying configuration container")
938 setup_modify_ldif(samdb
, setup_path("provision_configuration_basedn_modify.ldif"), {
939 "CONFIGDN": names
.configdn
,
940 "SCHEMADN": names
.schemadn
,
943 # The LDIF here was created when the Schema object was constructed
944 message("Setting up sam.ldb schema")
945 samdb
.add_ldif(schema
.schema_dn_add
)
946 samdb
.modify_ldif(schema
.schema_dn_modify
)
947 samdb
.write_prefixes_from_schema()
948 samdb
.add_ldif(schema
.schema_data
)
949 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
950 {"SCHEMADN": names
.schemadn
})
952 message("Setting up sam.ldb configuration data")
953 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
954 "CONFIGDN": names
.configdn
,
955 "NETBIOSNAME": names
.netbiosname
,
956 "DEFAULTSITE": names
.sitename
,
957 "DNSDOMAIN": names
.dnsdomain
,
958 "DOMAIN": names
.domain
,
959 "SCHEMADN": names
.schemadn
,
960 "DOMAINDN": names
.domaindn
,
961 "SERVERDN": names
.serverdn
,
962 "FOREST_FUNCTIONALALITY": str(forestFunctionality
)
965 message("Setting up display specifiers")
966 setup_add_ldif(samdb
, setup_path("display_specifiers.ldif"),
967 {"CONFIGDN": names
.configdn
})
969 message("Adding users container")
970 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
971 "DOMAINDN": names
.domaindn
})
972 message("Modifying users container")
973 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
974 "DOMAINDN": names
.domaindn
})
975 message("Adding computers container")
976 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
977 "DOMAINDN": names
.domaindn
})
978 message("Modifying computers container")
979 setup_modify_ldif(samdb
, setup_path("provision_computers_modify.ldif"), {
980 "DOMAINDN": names
.domaindn
})
981 message("Setting up sam.ldb data")
982 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
983 "DOMAINDN": names
.domaindn
,
984 "NETBIOSNAME": names
.netbiosname
,
985 "DEFAULTSITE": names
.sitename
,
986 "CONFIGDN": names
.configdn
,
987 "SERVERDN": names
.serverdn
990 if fill
== FILL_FULL
:
991 message("Setting up sam.ldb users and groups")
992 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
993 "DOMAINDN": names
.domaindn
,
994 "DOMAINSID": str(domainsid
),
995 "CONFIGDN": names
.configdn
,
996 "ADMINPASS_B64": b64encode(adminpass
),
997 "KRBTGTPASS_B64": b64encode(krbtgtpass
),
1000 if serverrole
== "domain controller":
1001 message("Setting up self join")
1002 setup_self_join(samdb
, names
=names
, invocationid
=invocationid
,
1004 machinepass
=machinepass
,
1005 domainsid
=domainsid
, policyguid
=policyguid
,
1006 setup_path
=setup_path
, domainControllerFunctionality
=domainControllerFunctionality
)
1009 samdb
.transaction_cancel()
1012 samdb
.transaction_commit()
1017 FILL_NT4SYNC
= "NT4SYNC"
1021 def provision(setup_dir
, message
, session_info
,
1022 credentials
, smbconf
=None, targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None,
1023 rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
1025 domain
=None, hostname
=None, hostip
=None, hostip6
=None,
1026 domainsid
=None, adminpass
=None, ldapadminpass
=None,
1027 krbtgtpass
=None, domainguid
=None,
1028 policyguid
=None, invocationid
=None, machinepass
=None,
1029 dnspass
=None, root
=None, nobody
=None, users
=None,
1030 wheel
=None, backup
=None, aci
=None, serverrole
=None,
1031 ldap_backend_extra_port
=None, ldap_backend_type
=None, sitename
=None,
1032 ol_mmr_urls
=None, ol_olc
=None,
1033 setup_ds_path
=None, slapd_path
=None, nosync
=False,
1034 ldap_dryrun_mode
=False):
1037 :note: caution, this wipes all existing data!
1040 def setup_path(file):
1041 return os
.path
.join(setup_dir
, file)
1043 if domainsid
is None:
1044 domainsid
= security
.random_sid()
1046 if policyguid
is None:
1047 policyguid
= str(uuid
.uuid4())
1048 if adminpass
is None:
1049 adminpass
= glue
.generate_random_str(12)
1050 if krbtgtpass
is None:
1051 krbtgtpass
= glue
.generate_random_str(12)
1052 if machinepass
is None:
1053 machinepass
= glue
.generate_random_str(12)
1055 dnspass
= glue
.generate_random_str(12)
1056 if ldapadminpass
is None:
1057 #Make a new, random password between Samba and it's LDAP server
1058 ldapadminpass
=glue
.generate_random_str(12)
1061 root_uid
= findnss_uid([root
or "root"])
1062 nobody_uid
= findnss_uid([nobody
or "nobody"])
1063 users_gid
= findnss_gid([users
or "users"])
1065 wheel_gid
= findnss_gid(["wheel", "adm"])
1067 wheel_gid
= findnss_gid([wheel
])
1069 if targetdir
is not None:
1070 if (not os
.path
.exists(os
.path
.join(targetdir
, "etc"))):
1071 os
.makedirs(os
.path
.join(targetdir
, "etc"))
1072 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1073 elif smbconf
is None:
1074 smbconf
= param
.default_path()
1076 # only install a new smb.conf if there isn't one there already
1077 if not os
.path
.exists(smbconf
):
1078 make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
1081 lp
= param
.LoadParm()
1084 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1085 dnsdomain
=realm
, serverrole
=serverrole
, sitename
=sitename
,
1086 rootdn
=rootdn
, domaindn
=domaindn
, configdn
=configdn
, schemadn
=schemadn
,
1089 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1093 hostip
= socket
.getaddrinfo(names
.hostname
, None, socket
.AF_INET
, socket
.AI_CANONNAME
, socket
.IPPROTO_IP
)[0][-1][0]
1094 except socket
.gaierror
, (socket
.EAI_NODATA
, msg
):
1099 hostip6
= socket
.getaddrinfo(names
.hostname
, None, socket
.AF_INET6
, socket
.AI_CANONNAME
, socket
.IPPROTO_IP
)[0][-1][0]
1100 except socket
.gaierror
, (socket
.EAI_NODATA
, msg
):
1103 if serverrole
is None:
1104 serverrole
= lp
.get("server role")
1106 assert serverrole
in ("domain controller", "member server", "standalone")
1107 if invocationid
is None and serverrole
== "domain controller":
1108 invocationid
= str(uuid
.uuid4())
1110 if not os
.path
.exists(paths
.private_dir
):
1111 os
.mkdir(paths
.private_dir
)
1113 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1115 schema
= Schema(setup_path
, schemadn
=names
.schemadn
, serverdn
=names
.serverdn
)
1117 provision_backend
= None
1118 if ldap_backend_type
:
1119 # We only support an LDAP backend over ldapi://
1121 provision_backend
= ProvisionBackend(paths
=paths
, setup_path
=setup_path
, lp
=lp
, credentials
=credentials
,
1123 message
=message
, hostname
=hostname
,
1124 root
=root
, schema
=schema
, ldap_backend_type
=ldap_backend_type
,
1125 ldapadminpass
=ldapadminpass
,
1126 ldap_backend_extra_port
=ldap_backend_extra_port
,
1127 ol_mmr_urls
=ol_mmr_urls
,
1128 slapd_path
=slapd_path
,
1129 setup_ds_path
=setup_ds_path
,
1130 ldap_dryrun_mode
=ldap_dryrun_mode
)
1132 # Now use the backend credentials to access the databases
1133 credentials
= provision_backend
.credentials
1135 # only install a new shares config db if there is none
1136 if not os
.path
.exists(paths
.shareconf
):
1137 message("Setting up share.ldb")
1138 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
,
1139 credentials
=credentials
, lp
=lp
)
1140 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1143 message("Setting up secrets.ldb")
1144 secrets_ldb
= setup_secretsdb(paths
.secrets
, setup_path
,
1145 session_info
=session_info
,
1146 credentials
=credentials
, lp
=lp
)
1148 message("Setting up the registry")
1149 setup_registry(paths
.hklm
, setup_path
, session_info
,
1152 message("Setting up templates db")
1153 setup_templatesdb(paths
.templates
, setup_path
, session_info
=session_info
,
1156 message("Setting up idmap db")
1157 idmap
= setup_idmapdb(paths
.idmapdb
, setup_path
, session_info
=session_info
,
1160 message("Setting up SAM db")
1161 samdb
= setup_samdb(paths
.samdb
, setup_path
, session_info
=session_info
,
1162 credentials
=credentials
, lp
=lp
, names
=names
,
1164 domainsid
=domainsid
,
1165 schema
=schema
, domainguid
=domainguid
, policyguid
=policyguid
,
1167 adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1168 invocationid
=invocationid
,
1169 machinepass
=machinepass
, dnspass
=dnspass
,
1170 serverrole
=serverrole
, ldap_backend
=provision_backend
)
1172 if serverrole
== "domain controller":
1173 if paths
.netlogon
is None:
1174 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1175 message("Please either remove %s or see the template at %s" %
1176 ( paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1177 assert(paths
.netlogon
is not None)
1179 if paths
.sysvol
is None:
1180 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1181 message("Please either remove %s or see the template at %s" %
1182 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1183 assert(paths
.sysvol
is not None)
1185 policy_path
= os
.path
.join(paths
.sysvol
, names
.dnsdomain
, "Policies",
1186 "{" + policyguid
+ "}")
1187 os
.makedirs(policy_path
, 0755)
1188 open(os
.path
.join(policy_path
, "GPT.INI"), 'w').write("")
1189 os
.makedirs(os
.path
.join(policy_path
, "Machine"), 0755)
1190 os
.makedirs(os
.path
.join(policy_path
, "User"), 0755)
1191 if not os
.path
.isdir(paths
.netlogon
):
1192 os
.makedirs(paths
.netlogon
, 0755)
1194 if samdb_fill
== FILL_FULL
:
1195 setup_name_mappings(samdb
, idmap
, str(domainsid
), names
.domaindn
,
1196 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1197 users_gid
=users_gid
, wheel_gid
=wheel_gid
)
1199 message("Setting up sam.ldb rootDSE marking as synchronized")
1200 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"))
1202 # Only make a zone file on the first DC, it should be replicated with DNS replication
1203 if serverrole
== "domain controller":
1204 secrets_ldb
= Ldb(paths
.secrets
, session_info
=session_info
,
1205 credentials
=credentials
, lp
=lp
)
1206 secretsdb_become_dc(secrets_ldb
, setup_path
, domain
=domain
, realm
=names
.realm
,
1207 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1208 keytab_path
=paths
.keytab
, samdb_url
=paths
.samdb
,
1209 dns_keytab_path
=paths
.dns_keytab
, dnspass
=dnspass
,
1210 machinepass
=machinepass
, dnsdomain
=names
.dnsdomain
)
1212 domainguid
= samdb
.searchone(basedn
=domaindn
, attribute
="objectGUID")
1213 assert isinstance(domainguid
, str)
1214 hostguid
= samdb
.searchone(basedn
=domaindn
, attribute
="objectGUID",
1215 expression
="(&(objectClass=computer)(cn=%s))" % names
.hostname
,
1216 scope
=SCOPE_SUBTREE
)
1217 assert isinstance(hostguid
, str)
1219 create_zone_file(paths
.dns
, setup_path
, dnsdomain
=names
.dnsdomain
,
1220 domaindn
=names
.domaindn
, hostip
=hostip
,
1221 hostip6
=hostip6
, hostname
=names
.hostname
,
1222 dnspass
=dnspass
, realm
=names
.realm
,
1223 domainguid
=domainguid
, hostguid
=hostguid
)
1225 create_named_conf(paths
.namedconf
, setup_path
, realm
=names
.realm
,
1226 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
)
1228 create_named_txt(paths
.namedtxt
, setup_path
, realm
=names
.realm
,
1229 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
,
1230 keytab_name
=paths
.dns_keytab
)
1231 message("See %s for an example configuration include file for BIND" % paths
.namedconf
)
1232 message("and %s for further documentation required for secure DNS updates" % paths
.namedtxt
)
1234 create_krb5_conf(paths
.krb5conf
, setup_path
, dnsdomain
=names
.dnsdomain
,
1235 hostname
=names
.hostname
, realm
=names
.realm
)
1236 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths
.krb5conf
)
1239 # if backend is openldap, terminate slapd after final provision and check its proper termination
1240 if provision_backend
is not None and provision_backend
.slapd
is not None:
1241 if provision_backend
.slapd
.poll() is None:
1243 if hasattr(provision_backend
.slapd
, "terminate"):
1244 provision_backend
.slapd
.terminate()
1247 os
.kill(provision_backend
.slapd
.pid
, signal
.SIGTERM
)
1249 #and now wait for it to die
1250 provision_backend
.slapd
.communicate()
1252 # now display slapd_command_file.txt to show how slapd must be started next time
1253 message("Use later the following commandline to start slapd, then Samba:")
1254 slapd_command
= "\'" + "\' \'".join(provision_backend
.slapd_command
) + "\'"
1255 message(slapd_command
)
1256 message("This slapd-Commandline is also stored under: " + paths
.ldapdir
+ "/ldap_backend_startup.sh")
1258 setup_file(setup_path("ldap_backend_startup.sh"), paths
.ldapdir
+ "/ldap_backend_startup.sh", {
1259 "SLAPD_COMMAND" : slapd_command
})
1262 create_phpldapadmin_config(paths
.phpldapadminconfig
, setup_path
,
1265 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths
.phpldapadminconfig
)
1267 message("Once the above files are installed, your Samba4 server will be ready to use")
1268 message("Server Role: %s" % serverrole
)
1269 message("Hostname: %s" % names
.hostname
)
1270 message("NetBIOS Domain: %s" % names
.domain
)
1271 message("DNS Domain: %s" % names
.dnsdomain
)
1272 message("DOMAIN SID: %s" % str(domainsid
))
1273 if samdb_fill
== FILL_FULL
:
1274 message("Admin password: %s" % adminpass
)
1275 if provision_backend
:
1276 if provision_backend
.credentials
.get_bind_dn() is not None:
1277 message("LDAP Backend Admin DN: %s" % provision_backend
.credentials
.get_bind_dn())
1279 message("LDAP Admin User: %s" % provision_backend
.credentials
.get_username())
1281 message("LDAP Admin Password: %s" % provision_backend
.credentials
.get_password())
1283 result
= ProvisionResult()
1284 result
.domaindn
= domaindn
1285 result
.paths
= paths
1287 result
.samdb
= samdb
1292 def provision_become_dc(setup_dir
=None,
1293 smbconf
=None, targetdir
=None, realm
=None,
1294 rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
1296 domain
=None, hostname
=None, domainsid
=None,
1297 adminpass
=None, krbtgtpass
=None, domainguid
=None,
1298 policyguid
=None, invocationid
=None, machinepass
=None,
1299 dnspass
=None, root
=None, nobody
=None, users
=None,
1300 wheel
=None, backup
=None, serverrole
=None,
1301 ldap_backend
=None, ldap_backend_type
=None, sitename
=None):
1304 """print a message if quiet is not set."""
1307 return provision(setup_dir
, message
, system_session(), None,
1308 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
, realm
=realm
,
1309 rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
, configdn
=configdn
, serverdn
=serverdn
,
1310 domain
=domain
, hostname
=hostname
, hostip
="127.0.0.1", domainsid
=domainsid
, machinepass
=machinepass
, serverrole
="domain controller", sitename
=sitename
)
1313 def setup_db_config(setup_path
, dbdir
):
1314 """Setup a Berkeley database.
1316 :param setup_path: Setup path function.
1317 :param dbdir: Database directory."""
1318 if not os
.path
.isdir(os
.path
.join(dbdir
, "bdb-logs")):
1319 os
.makedirs(os
.path
.join(dbdir
, "bdb-logs"), 0700)
1320 if not os
.path
.isdir(os
.path
.join(dbdir
, "tmp")):
1321 os
.makedirs(os
.path
.join(dbdir
, "tmp"), 0700)
1323 setup_file(setup_path("DB_CONFIG"), os
.path
.join(dbdir
, "DB_CONFIG"),
1324 {"LDAPDBDIR": dbdir
})
1326 class ProvisionBackend(object):
1327 def __init__(self
, paths
=None, setup_path
=None, lp
=None, credentials
=None,
1328 names
=None, message
=None,
1329 hostname
=None, root
=None,
1330 schema
=None, ldapadminpass
=None,
1331 ldap_backend_type
=None, ldap_backend_extra_port
=None,
1333 setup_ds_path
=None, slapd_path
=None,
1334 nosync
=False, ldap_dryrun_mode
=False):
1335 """Provision an LDAP backend for samba4
1337 This works for OpenLDAP and Fedora DS
1340 self
.ldapi_uri
= "ldapi://" + urllib
.quote(os
.path
.join(paths
.ldapdir
, "ldapi"), safe
="")
1342 if not os
.path
.isdir(paths
.ldapdir
):
1343 os
.makedirs(paths
.ldapdir
, 0700)
1345 if ldap_backend_type
== "existing":
1346 #Check to see that this 'existing' LDAP backend in fact exists
1347 ldapi_db
= Ldb(self
.ldapi_uri
, credentials
=credentials
)
1348 search_ol_rootdse
= ldapi_db
.search(base
="", scope
=SCOPE_BASE
,
1349 expression
="(objectClass=OpenLDAProotDSE)")
1351 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1352 # This caused them to be set into the long-term database later in the script.
1353 self
.credentials
= credentials
1354 self
.ldap_backend_type
= "openldap" #For now, assume existing backends at least emulate OpenLDAP
1357 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1358 # if another instance of slapd is already running
1360 ldapi_db
= Ldb(self
.ldapi_uri
)
1361 search_ol_rootdse
= ldapi_db
.search(base
="", scope
=SCOPE_BASE
,
1362 expression
="(objectClass=OpenLDAProotDSE)");
1364 f
= open(paths
.slapdpid
, "r")
1367 message("Check for slapd Process with PID: " + str(p
) + " and terminate it manually.")
1371 raise("Warning: Another slapd Instance seems already running on this host, listening to " + self
.ldapi_uri
+ ". Please shut it down before you continue. ")
1376 # Try to print helpful messages when the user has not specified the path to slapd
1377 if slapd_path
is None:
1378 raise("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1379 if not os
.path
.exists(slapd_path
):
1380 message (slapd_path
)
1381 raise("Warning: Given Path to slapd does not exist!")
1383 schemadb_path
= os
.path
.join(paths
.ldapdir
, "schema-tmp.ldb")
1385 os
.unlink(schemadb_path
)
1390 # Put the LDIF of the schema into a database so we can search on
1391 # it to generate schema-dependent configurations in Fedora DS and
1393 os
.path
.join(paths
.ldapdir
, "schema-tmp.ldb")
1394 schema
.ldb
.connect(schemadb_path
)
1395 schema
.ldb
.transaction_start()
1397 # These bits of LDIF are supplied when the Schema object is created
1398 schema
.ldb
.add_ldif(schema
.schema_dn_add
)
1399 schema
.ldb
.modify_ldif(schema
.schema_dn_modify
)
1400 schema
.ldb
.add_ldif(schema
.schema_data
)
1401 schema
.ldb
.transaction_commit()
1403 self
.credentials
= Credentials()
1404 self
.credentials
.guess(lp
)
1405 #Kerberos to an ldapi:// backend makes no sense
1406 self
.credentials
.set_kerberos_state(DONT_USE_KERBEROS
)
1407 self
.ldap_backend_type
= ldap_backend_type
1409 if ldap_backend_type
== "fedora-ds":
1410 provision_fds_backend(self
, paths
=paths
, setup_path
=setup_path
, names
=names
, message
=message
,
1411 hostname
=hostname
, ldapadminpass
=ldapadminpass
, root
=root
,
1412 schema
=schema
, ldap_backend_extra_port
=ldap_backend_extra_port
,
1413 setup_ds_path
=setup_ds_path
, slapd_path
=slapd_path
,
1414 nosync
=nosync
, ldap_dryrun_mode
=ldap_dryrun_mode
)
1416 elif ldap_backend_type
== "openldap":
1417 provision_openldap_backend(self
, paths
=paths
, setup_path
=setup_path
, names
=names
, message
=message
,
1418 hostname
=hostname
, ldapadminpass
=ldapadminpass
, root
=root
,
1419 schema
=schema
, ldap_backend_extra_port
=ldap_backend_extra_port
,
1420 ol_mmr_urls
=ol_mmr_urls
,
1421 slapd_path
=slapd_path
,
1422 nosync
=nosync
, ldap_dryrun_mode
=ldap_dryrun_mode
)
1424 raise("Unknown LDAP backend type selected")
1426 self
.credentials
.set_password(ldapadminpass
)
1428 # Now start the slapd, so we can provision onto it. We keep the
1429 # subprocess context around, to kill this off at the successful
1431 self
.slapd
= subprocess
.Popen(self
.slapd_provision_command
, close_fds
=True, shell
=False)
1433 while self
.slapd
.poll() is None:
1434 # Wait until the socket appears
1436 ldapi_db
= Ldb(self
.ldapi_uri
, lp
=lp
, credentials
=self
.credentials
)
1437 search_ol_rootdse
= ldapi_db
.search(base
="", scope
=SCOPE_BASE
,
1438 expression
="(objectClass=OpenLDAProotDSE)")
1439 # If we have got here, then we must have a valid connection to the LDAP server!
1445 raise "slapd died before we could make a connection to it"
1448 def provision_openldap_backend(result
, paths
=None, setup_path
=None, names
=None, message
=None,
1449 hostname
=None, ldapadminpass
=None, root
=None,
1451 ldap_backend_extra_port
=None,
1453 slapd_path
=None, nosync
=False,
1454 ldap_dryrun_mode
=False):
1456 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1459 nosync_config
= "dbnosync"
1461 lnkattr
= get_linked_attributes(names
.schemadn
,schema
.ldb
)
1462 refint_attributes
= ""
1463 memberof_config
= "# Generated from Samba4 schema\n"
1464 for att
in lnkattr
.keys():
1465 if lnkattr
[att
] is not None:
1466 refint_attributes
= refint_attributes
+ " " + att
1468 memberof_config
+= read_and_sub_file(setup_path("memberof.conf"),
1469 { "MEMBER_ATTR" : att
,
1470 "MEMBEROF_ATTR" : lnkattr
[att
] })
1472 refint_config
= read_and_sub_file(setup_path("refint.conf"),
1473 { "LINK_ATTRS" : refint_attributes
})
1475 attrs
= ["linkID", "lDAPDisplayName"]
1476 res
= schema
.ldb
.search(expression
="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base
=names
.schemadn
, scope
=SCOPE_ONELEVEL
, attrs
=attrs
)
1478 for i
in range (0, len(res
)):
1479 index_attr
= res
[i
]["lDAPDisplayName"][0]
1480 if index_attr
== "objectGUID":
1481 index_attr
= "entryUUID"
1483 index_config
+= "index " + index_attr
+ " eq\n"
1485 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1487 mmr_replicator_acl
= ""
1488 mmr_serverids_config
= ""
1489 mmr_syncrepl_schema_config
= ""
1490 mmr_syncrepl_config_config
= ""
1491 mmr_syncrepl_user_config
= ""
1494 if ol_mmr_urls
is not None:
1495 # For now, make these equal
1496 mmr_pass
= ldapadminpass
1498 url_list
=filter(None,ol_mmr_urls
.split(' '))
1499 if (len(url_list
) == 1):
1500 url_list
=filter(None,ol_mmr_urls
.split(','))
1503 mmr_on_config
= "MirrorMode On"
1504 mmr_replicator_acl
= " by dn=cn=replicator,cn=samba read"
1506 for url
in url_list
:
1508 mmr_serverids_config
+= read_and_sub_file(setup_path("mmr_serverids.conf"),
1509 { "SERVERID" : str(serverid
),
1510 "LDAPSERVER" : url
})
1513 mmr_syncrepl_schema_config
+= read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1515 "MMRDN": names
.schemadn
,
1517 "MMR_PASSWORD": mmr_pass
})
1520 mmr_syncrepl_config_config
+= read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1522 "MMRDN": names
.configdn
,
1524 "MMR_PASSWORD": mmr_pass
})
1527 mmr_syncrepl_user_config
+= read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1529 "MMRDN": names
.domaindn
,
1531 "MMR_PASSWORD": mmr_pass
})
1532 # OpenLDAP cn=config initialisation
1533 olc_syncrepl_config
= ""
1535 # if mmr = yes, generate cn=config-replication directives
1536 # and olc_seed.lif for the other mmr-servers
1537 if ol_mmr_urls
is not None:
1539 olc_serverids_config
= ""
1540 olc_syncrepl_seed_config
= ""
1541 olc_mmr_config
+= read_and_sub_file(setup_path("olc_mmr.conf"),{})
1543 for url
in url_list
:
1545 olc_serverids_config
+= read_and_sub_file(setup_path("olc_serverid.conf"),
1546 { "SERVERID" : str(serverid
),
1547 "LDAPSERVER" : url
})
1550 olc_syncrepl_config
+= read_and_sub_file(setup_path("olc_syncrepl.conf"),
1553 "MMR_PASSWORD": mmr_pass
})
1555 olc_syncrepl_seed_config
+= read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1557 "LDAPSERVER" : url
})
1559 setup_file(setup_path("olc_seed.ldif"), paths
.olcseedldif
,
1560 {"OLC_SERVER_ID_CONF": olc_serverids_config
,
1561 "OLC_PW": ldapadminpass
,
1562 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config
})
1565 setup_file(setup_path("slapd.conf"), paths
.slapdconf
,
1566 {"DNSDOMAIN": names
.dnsdomain
,
1567 "LDAPDIR": paths
.ldapdir
,
1568 "DOMAINDN": names
.domaindn
,
1569 "CONFIGDN": names
.configdn
,
1570 "SCHEMADN": names
.schemadn
,
1571 "MEMBEROF_CONFIG": memberof_config
,
1572 "MIRRORMODE": mmr_on_config
,
1573 "REPLICATOR_ACL": mmr_replicator_acl
,
1574 "MMR_SERVERIDS_CONFIG": mmr_serverids_config
,
1575 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config
,
1576 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config
,
1577 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config
,
1578 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config
,
1579 "OLC_MMR_CONFIG": olc_mmr_config
,
1580 "REFINT_CONFIG": refint_config
,
1581 "INDEX_CONFIG": index_config
,
1582 "NOSYNC": nosync_config
})
1584 setup_db_config(setup_path
, os
.path
.join(paths
.ldapdir
, "db", "user"))
1585 setup_db_config(setup_path
, os
.path
.join(paths
.ldapdir
, "db", "config"))
1586 setup_db_config(setup_path
, os
.path
.join(paths
.ldapdir
, "db", "schema"))
1588 if not os
.path
.exists(os
.path
.join(paths
.ldapdir
, "db", "samba", "cn=samba")):
1589 os
.makedirs(os
.path
.join(paths
.ldapdir
, "db", "samba", "cn=samba"), 0700)
1591 setup_file(setup_path("cn=samba.ldif"),
1592 os
.path
.join(paths
.ldapdir
, "db", "samba", "cn=samba.ldif"),
1593 { "UUID": str(uuid
.uuid4()),
1594 "LDAPTIME": timestring(int(time
.time()))} )
1595 setup_file(setup_path("cn=samba-admin.ldif"),
1596 os
.path
.join(paths
.ldapdir
, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1597 {"LDAPADMINPASS_B64": b64encode(ldapadminpass
),
1598 "UUID": str(uuid
.uuid4()),
1599 "LDAPTIME": timestring(int(time
.time()))} )
1601 if ol_mmr_urls
is not None:
1602 setup_file(setup_path("cn=replicator.ldif"),
1603 os
.path
.join(paths
.ldapdir
, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1604 {"MMR_PASSWORD_B64": b64encode(mmr_pass
),
1605 "UUID": str(uuid
.uuid4()),
1606 "LDAPTIME": timestring(int(time
.time()))} )
1609 mapping
= "schema-map-openldap-2.3"
1610 backend_schema
= "backend-schema.schema"
1612 backend_schema_data
= schema
.ldb
.convert_schema_to_openldap("openldap", open(setup_path(mapping
), 'r').read())
1613 assert backend_schema_data
is not None
1614 open(os
.path
.join(paths
.ldapdir
, backend_schema
), 'w').write(backend_schema_data
)
1616 # now we generate the needed strings to start slapd automatically,
1617 # first ldapi_uri...
1618 if ldap_backend_extra_port
is not None:
1619 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1620 # specified there as part of it's clue as to it's own name,
1621 # and not to replicate to itself
1622 if ol_mmr_urls
is None:
1623 server_port_string
= "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1625 server_port_string
= "ldap://" + names
.hostname
+ "." + names
.dnsdomain
+":%d" % ldap_backend_extra_port
1627 server_port_string
= ""
1629 # Prepare the 'result' information - the commands to return in particular
1630 result
.slapd_provision_command
= [slapd_path
]
1632 result
.slapd_provision_command
.append("-F" + paths
.olcdir
)
1634 result
.slapd_provision_command
.append("-h")
1636 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1637 result
.slapd_command
= list(result
.slapd_provision_command
)
1639 result
.slapd_provision_command
.append(result
.ldapi_uri
)
1640 result
.slapd_provision_command
.append("-d0")
1642 uris
= result
.ldapi_uri
1643 if server_port_string
is not "":
1644 uris
= uris
+ " " + server_port_string
1646 result
.slapd_command
.append(uris
)
1648 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1649 result
.credentials
.set_username("samba-admin")
1651 # If we were just looking for crashes up to this point, it's a
1652 # good time to exit before we realise we don't have OpenLDAP on
1654 if ldap_dryrun_mode
:
1657 # Finally, convert the configuration into cn=config style!
1658 if not os
.path
.isdir(paths
.olcdir
):
1659 os
.makedirs(paths
.olcdir
, 0770)
1661 retcode
= subprocess
.call([slapd_path
, "-Ttest", "-f", paths
.slapdconf
, "-F", paths
.olcdir
], close_fds
=True, shell
=False)
1663 # We can't do this, as OpenLDAP is strange. It gives an error
1664 # output to the above, but does the conversion sucessfully...
1667 # raise("conversion from slapd.conf to cn=config failed")
1669 if not os
.path
.exists(os
.path
.join(paths
.olcdir
, "cn=config.ldif")):
1670 raise("conversion from slapd.conf to cn=config failed")
1672 # Don't confuse the admin by leaving the slapd.conf around
1673 os
.remove(paths
.slapdconf
)
1676 def provision_fds_backend(result
, paths
=None, setup_path
=None, names
=None, message
=None,
1677 hostname
=None, ldapadminpass
=None, root
=None,
1679 ldap_backend_extra_port
=None,
1683 ldap_dryrun_mode
=False):
1685 if ldap_backend_extra_port
is not None:
1686 serverport
= "ServerPort=%d" % ldap_backend_extra_port
1690 setup_file(setup_path("fedorads.inf"), paths
.fedoradsinf
,
1692 "HOSTNAME": hostname
,
1693 "DNSDOMAIN": names
.dnsdomain
,
1694 "LDAPDIR": paths
.ldapdir
,
1695 "DOMAINDN": names
.domaindn
,
1696 "LDAPMANAGERDN": names
.ldapmanagerdn
,
1697 "LDAPMANAGERPASS": ldapadminpass
,
1698 "SERVERPORT": serverport
})
1700 setup_file(setup_path("fedorads-partitions.ldif"), paths
.fedoradspartitions
,
1701 {"CONFIGDN": names
.configdn
,
1702 "SCHEMADN": names
.schemadn
,
1705 mapping
= "schema-map-fedora-ds-1.0"
1706 backend_schema
= "99_ad.ldif"
1708 # Build a schema file in Fedora DS format
1709 backend_schema_data
= schema
.ldb
.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping
), 'r').read())
1710 assert backend_schema_data
is not None
1711 open(os
.path
.join(paths
.ldapdir
, backend_schema
), 'w').write(backend_schema_data
)
1713 result
.credentials
.set_bind_dn(names
.ldapmanagerdn
)
1715 # Destory the target directory, or else setup-ds.pl will complain
1716 fedora_ds_dir
= os
.path
.join(paths
.ldapdir
, "slapd-samba4")
1717 shutil
.rmtree(fedora_ds_dir
, True)
1719 result
.slapd_provision_command
= [slapd_path
, "-D", fedora_ds_dir
, "-i", paths
.slapdpid
];
1720 #In the 'provision' command line, stay in the foreground so we can easily kill it
1721 result
.slapd_provision_command
.append("-d0")
1723 #the command for the final run is the normal script
1724 result
.slapd_command
= [os
.path
.join(paths
.ldapdir
, "slapd-samba4", "start-slapd")]
1726 # If we were just looking for crashes up to this point, it's a
1727 # good time to exit before we realise we don't have Fedora DS on
1728 if ldap_dryrun_mode
:
1731 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1732 if setup_ds_path
is None:
1733 raise("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1734 if not os
.path
.exists(setup_ds_path
):
1735 message (setup_ds_path
)
1736 raise("Warning: Given Path to slapd does not exist!")
1738 # Run the Fedora DS setup utility
1739 retcode
= subprocess
.call([setup_ds_path
, "--silent", "--file", paths
.fedoradsinf
], close_fds
=True, shell
=False)
1741 raise("setup-ds failed")
1743 def create_phpldapadmin_config(path
, setup_path
, ldapi_uri
):
1744 """Create a PHP LDAP admin configuration file.
1746 :param path: Path to write the configuration to.
1747 :param setup_path: Function to generate setup paths.
1749 setup_file(setup_path("phpldapadmin-config.php"), path
,
1750 {"S4_LDAPI_URI": ldapi_uri
})
1753 def create_zone_file(path
, setup_path
, dnsdomain
, domaindn
,
1754 hostip
, hostip6
, hostname
, dnspass
, realm
, domainguid
, hostguid
):
1755 """Write out a DNS zone file, from the info in the current database.
1757 :param path: Path of the new zone file.
1758 :param setup_path: Setup path function.
1759 :param dnsdomain: DNS Domain name
1760 :param domaindn: DN of the Domain
1761 :param hostip: Local IPv4 IP
1762 :param hostip6: Local IPv6 IP
1763 :param hostname: Local hostname
1764 :param dnspass: Password for DNS
1765 :param realm: Realm name
1766 :param domainguid: GUID of the domain.
1767 :param hostguid: GUID of the host.
1769 assert isinstance(domainguid
, str)
1771 if hostip6
is not None:
1772 hostip6_base_line
= " IN AAAA " + hostip6
1773 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
1775 hostip6_base_line
= ""
1776 hostip6_host_line
= ""
1778 if hostip
is not None:
1779 hostip_base_line
= " IN A " + hostip
1780 hostip_host_line
= hostname
+ " IN A " + hostip
1782 hostip_base_line
= ""
1783 hostip_host_line
= ""
1785 setup_file(setup_path("provision.zone"), path
, {
1786 "DNSPASS_B64": b64encode(dnspass
),
1787 "HOSTNAME": hostname
,
1788 "DNSDOMAIN": dnsdomain
,
1790 "HOSTIP_BASE_LINE": hostip_base_line
,
1791 "HOSTIP_HOST_LINE": hostip_host_line
,
1792 "DOMAINGUID": domainguid
,
1793 "DATESTRING": time
.strftime("%Y%m%d%H"),
1794 "DEFAULTSITE": DEFAULTSITE
,
1795 "HOSTGUID": hostguid
,
1796 "HOSTIP6_BASE_LINE": hostip6_base_line
,
1797 "HOSTIP6_HOST_LINE": hostip6_host_line
,
1801 def create_named_conf(path
, setup_path
, realm
, dnsdomain
,
1803 """Write out a file containing zone statements suitable for inclusion in a
1804 named.conf file (including GSS-TSIG configuration).
1806 :param path: Path of the new named.conf file.
1807 :param setup_path: Setup path function.
1808 :param realm: Realm name
1809 :param dnsdomain: DNS Domain name
1810 :param private_dir: Path to private directory
1811 :param keytab_name: File name of DNS keytab file
1814 setup_file(setup_path("named.conf"), path
, {
1815 "DNSDOMAIN": dnsdomain
,
1817 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
1818 "PRIVATE_DIR": private_dir
1821 def create_named_txt(path
, setup_path
, realm
, dnsdomain
,
1822 private_dir
, keytab_name
):
1823 """Write out a file containing zone statements suitable for inclusion in a
1824 named.conf file (including GSS-TSIG configuration).
1826 :param path: Path of the new named.conf file.
1827 :param setup_path: Setup path function.
1828 :param realm: Realm name
1829 :param dnsdomain: DNS Domain name
1830 :param private_dir: Path to private directory
1831 :param keytab_name: File name of DNS keytab file
1834 setup_file(setup_path("named.txt"), path
, {
1835 "DNSDOMAIN": dnsdomain
,
1837 "DNS_KEYTAB": keytab_name
,
1838 "DNS_KEYTAB_ABS": os
.path
.join(private_dir
, keytab_name
),
1839 "PRIVATE_DIR": private_dir
1842 def create_krb5_conf(path
, setup_path
, dnsdomain
, hostname
, realm
):
1843 """Write out a file containing zone statements suitable for inclusion in a
1844 named.conf file (including GSS-TSIG configuration).
1846 :param path: Path of the new named.conf file.
1847 :param setup_path: Setup path function.
1848 :param dnsdomain: DNS Domain name
1849 :param hostname: Local hostname
1850 :param realm: Realm name
1853 setup_file(setup_path("krb5.conf"), path
, {
1854 "DNSDOMAIN": dnsdomain
,
1855 "HOSTNAME": hostname
,