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