# Unix SMB/CIFS implementation.
-# backend code for provisioning a Samba4 server
+# backend code for provisioning a Samba AD server
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
__docformat__ = "restructuredText"
+from samba.compat import urllib_quote
from base64 import b64encode
+import errno
import os
+import stat
import re
import pwd
import grp
import time
import uuid
import socket
-import urllib
import string
import tempfile
+import samba.dsdb
import ldb
from samba.auth import system_session, admin_session
import samba
+from samba import auth
from samba.samba3 import smbd, passdb
from samba.samba3 import param as s3param
from samba.dsdb import DS_DOMAIN_FUNCTION_2000
substitute_var,
valid_netbios_name,
version,
- )
+ is_heimdal_built,
+)
from samba.dcerpc import security, misc
from samba.dcerpc.misc import (
SEC_CHAN_BDC,
SEC_CHAN_WKSTA,
- )
+)
from samba.dsdb import (
DS_DOMAIN_FUNCTION_2003,
DS_DOMAIN_FUNCTION_2008_R2,
ENC_ALL_TYPES,
- )
+)
from samba.idmap import IDmapDB
from samba.ms_display_specifiers import read_ms_ldif
from samba.ntacls import setntacl, getntacl, dsacl2fsacl
FDSBackend,
LDBBackend,
OpenLDAPBackend,
- )
+)
from samba.descriptor import (
get_empty_descriptor,
get_config_descriptor,
get_dns_partition_descriptor,
get_dns_forest_microsoft_dns_descriptor,
get_dns_domain_microsoft_dns_descriptor,
- )
+ get_managed_service_accounts_descriptor,
+)
from samba.provision.common import (
setup_path,
setup_add_ldif,
setup_modify_ldif,
- )
+ FILL_FULL,
+ FILL_SUBDOMAIN,
+ FILL_NT4SYNC,
+ FILL_DRS
+)
from samba.provision.sambadns import (
get_dnsadmins_sid,
setup_ad_dns,
create_dns_update_list
- )
+)
import samba.param
import samba.registry
from samba.schema import Schema
from samba.samdb import SamDB
from samba.dbchecker import dbcheck
-
+from samba.provision.kerberos import create_kdc_conf
+from samba.samdb import get_default_backend_store
DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
-DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
+DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
DEFAULTSITE = "Default-First-Site-Name"
LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
+DEFAULT_MIN_PWD_LENGTH = 7
+
class ProvisionPaths(object):
self.dns = None
self.winsdb = None
self.private_dir = None
+ self.binddns_dir = None
self.state_dir = None
self.hostname = None
self.sitename = None
self.smbconf = None
+ self.domainsid = None
+ self.forestsid = None
+ self.domainguid = None
self.name_map = {}
def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
- lp):
+ lp):
"""Get key provision parameters (realm, domain, ...) from a given provision
:param samdb: An LDB object connected to the sam.ldb file
names.adminpass = None
# NT domain, kerberos realm, root dn, domain dn, domain dns name
- names.domain = string.upper(lp.get("workgroup"))
+ names.domain = lp.get("workgroup").upper()
names.realm = lp.get("realm")
names.dnsdomain = names.realm.lower()
basedn = samba.dn_from_dns_name(names.dnsdomain)
- names.realm = string.upper(names.realm)
+ names.realm = names.realm.upper()
# netbiosname
# Get the netbiosname first (could be obtained from smb.conf in theory)
res = secretsdb.search(expression="(flatname=%s)" %
- names.domain,base="CN=Primary Domains",
- scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
- names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
+ names.domain, base="CN=Primary Domains",
+ scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
+ names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
names.smbconf = smbconf
# That's a bit simplistic but it's ok as long as we have only 3
# partitions
current = samdb.search(expression="(objectClass=*)",
- base="", scope=ldb.SCOPE_BASE,
- attrs=["defaultNamingContext", "schemaNamingContext",
- "configurationNamingContext","rootDomainNamingContext",
- "namingContexts"])
+ base="", scope=ldb.SCOPE_BASE,
+ attrs=["defaultNamingContext", "schemaNamingContext",
+ "configurationNamingContext", "rootDomainNamingContext",
+ "namingContexts"])
names.configdn = current[0]["configurationNamingContext"][0]
names.schemadn = current[0]["schemaNamingContext"][0]
if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
- current[0]["defaultNamingContext"][0]))):
+ current[0]["defaultNamingContext"][0].decode('utf8')))):
raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
"is not the same ..." % (paths.samdb,
- str(current[0]["defaultNamingContext"][0]),
- paths.smbconf, basedn)))
+ str(current[0]["defaultNamingContext"][0].decode('utf8')),
+ paths.smbconf, basedn)))
- names.domaindn=current[0]["defaultNamingContext"][0]
- names.rootdn=current[0]["rootDomainNamingContext"][0]
- names.ncs=current[0]["namingContexts"]
+ names.domaindn = current[0]["defaultNamingContext"][0]
+ names.rootdn = current[0]["rootDomainNamingContext"][0]
+ names.ncs = current[0]["namingContexts"]
names.dnsforestdn = None
names.dnsdomaindn = None
# default site name
res3 = samdb.search(expression="(objectClass=site)",
- base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
+ base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
names.sitename = str(res3[0]["cn"])
# dns hostname and server dn
res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
- base="OU=Domain Controllers,%s" % basedn,
- scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
+ base="OU=Domain Controllers,%s" % basedn,
+ scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
+ if len(res4) == 0:
+ raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
+
names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
- attrs=[], base=names.configdn)
+ attrs=[], base=names.configdn)
names.serverdn = str(server_res[0].dn)
# invocation id/objectguid
res5 = samdb.search(expression="(objectClass=*)",
- base="CN=NTDS Settings,%s" % str(names.serverdn),
- scope=ldb.SCOPE_BASE,
- attrs=["invocationID", "objectGUID"])
+ base="CN=NTDS Settings,%s" % str(names.serverdn),
+ scope=ldb.SCOPE_BASE,
+ attrs=["invocationID", "objectGUID"])
names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
# domain guid/sid
res6 = samdb.search(expression="(objectClass=*)", base=basedn,
- scope=ldb.SCOPE_BASE, attrs=["objectGUID",
- "objectSid","msDS-Behavior-Version" ])
+ scope=ldb.SCOPE_BASE, attrs=["objectGUID",
+ "objectSid", "msDS-Behavior-Version"])
names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
- names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
+ names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
+ names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
if res6[0].get("msDS-Behavior-Version") is None or \
- int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
+ int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
names.domainlevel = DS_DOMAIN_FUNCTION_2000
else:
names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
# policy guid
- res7 = samdb.search(expression="(displayName=Default Domain Policy)",
+ res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
base="CN=Policies,CN=System," + basedn,
- scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
- names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
+ scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
+ names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
# dc policy guid
- res8 = samdb.search(expression="(displayName=Default Domain Controllers"
- " Policy)",
- base="CN=Policies,CN=System," + basedn,
- scope=ldb.SCOPE_ONELEVEL,
- attrs=["cn","displayName"])
+ res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
+ base="CN=Policies,CN=System," + basedn,
+ scope=ldb.SCOPE_ONELEVEL,
+ attrs=["cn", "displayName"])
if len(res8) == 1:
- names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
+ names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
else:
names.policyid_dc = None
tab.append("%s-%s;%s" % (low, high, id))
delta = ldb.Message()
delta.dn = ldb.Dn(samdb, "@PROVISION")
- delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
- ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
+ delta[LAST_PROVISION_USN_ATTRIBUTE] = \
+ ldb.MessageElement(tab,
+ ldb.FLAG_MOD_REPLACE,
+ LAST_PROVISION_USN_ATTRIBUTE)
entry = samdb.search(expression='provisionnerID=*',
base="@PROVISION", scope=ldb.SCOPE_BASE,
attrs=["provisionnerID"])
delta = ldb.Message()
delta.dn = ldb.Dn(samdb, "@PROVISION")
- delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
- ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
+ delta[LAST_PROVISION_USN_ATTRIBUTE] = \
+ ldb.MessageElement(tab,
+ ldb.FLAG_MOD_ADD,
+ LAST_PROVISION_USN_ATTRIBUTE)
samdb.add(delta)
-def get_max_usn(samdb,basedn):
+def get_max_usn(samdb, basedn):
""" This function return the biggest USN present in the provision
:param samdb: A LDB object pointing to the sam.ldb
(ie. DC=foo, DC=bar)
:return: The biggest USN in the provision"""
- res = samdb.search(expression="objectClass=*",base=basedn,
- scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
- controls=["search_options:1:2",
- "server_sort:1:1:uSNChanged",
- "paged_results:1:1"])
+ res = samdb.search(expression="objectClass=*", base=basedn,
+ scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
+ controls=["search_options:1:2",
+ "server_sort:1:1:uSNChanged",
+ "paged_results:1:1"])
return res[0]["uSNChanged"]
"""
try:
entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
- base="@PROVISION", scope=ldb.SCOPE_BASE,
- attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
- except ldb.LdbError, (ecode, emsg):
+ base="@PROVISION", scope=ldb.SCOPE_BASE,
+ attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
+ except ldb.LdbError as e1:
+ (ecode, emsg) = e1.args
if ecode == ldb.ERR_NO_SUCH_OBJECT:
return None
raise
def report_logger(self, logger):
"""Report this provision result to a logger."""
logger.info(
- "Once the above files are installed, your Samba4 server will "
+ "Once the above files are installed, your Samba AD server will "
"be ready to use")
if self.adminpass_generated:
logger.info("Admin password: %s", self.adminpass)
if lp.get("realm") == "":
raise Exception("Realm empty")
samdb = Ldb(lp.samdb_url(), session_info=session_info,
- credentials=credentials, lp=lp)
+ credentials=credentials, lp=lp)
if len(samdb.search("(cn=Administrator)")) != 1:
raise ProvisioningError("No administrator account found")
raise KeyError("Unable to find user/group in %r" % names)
-findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
-findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
+def findnss_uid(names):
+ return findnss(pwd.getpwnam, names)[2]
+
+
+def findnss_gid(names):
+ return findnss(grp.getgrnam, names)[2]
def provision_paths_from_lp(lp, dnsdomain):
"""
paths = ProvisionPaths()
paths.private_dir = lp.get("private dir")
+ paths.binddns_dir = lp.get("binddns dir")
paths.state_dir = lp.get("state directory")
# This is stored without path prefix for the "privateKeytab" attribute in
paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
- paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
- paths.namedconf = os.path.join(paths.private_dir, "named.conf")
- paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
- paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
+ paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
+ paths.encrypted_secrets_key_path = os.path.join(
+ paths.private_dir,
+ "encrypted_secrets.key")
+
+ paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
+ paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
+ paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
+ paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
+
paths.hklm = "hklm.ldb"
paths.hkcr = "hkcr.ldb"
paths.hkcu = "hkcu.ldb"
raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
if lp.get("realm").upper() != realm:
- raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
+ raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), lp.configfile, realm))
if lp.get("server role").lower() != serverrole:
raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), lp.configfile, serverrole))
if domain == realm and not domain_names_forced:
raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
+ if serverrole != "active directory domain controller":
+ #
+ # This is the code path for a domain member
+ # where we provision the database as if we where
+ # on a domain controller, so we should not use
+ # the same dnsdomain as the domain controllers
+ # of our primary domain.
+ #
+ # This will be important if we start doing
+ # SID/name filtering and reject the local
+ # sid and names if they come from a domain
+ # controller.
+ #
+ realm = netbiosname
+ dnsdomain = netbiosname.lower()
+
if rootdn is None:
- rootdn = domaindn
+ rootdn = domaindn
if configdn is None:
configdn = "CN=Configuration," + rootdn
"workgroup": domain,
"realm": realm,
"server role": serverrole,
- }
+ }
if lp is None:
lp = samba.param.LoadParm()
- #Load non-existent file
+ # Load non-existent file
if os.path.exists(smbconf):
lp.load(smbconf)
global_settings["lock dir"] = os.path.abspath(targetdir)
global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
+ global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
lp.set("lock dir", os.path.abspath(targetdir))
- lp.set("state directory", global_settings["state directory"])
+ lp.set("state directory", global_settings["state directory"])
lp.set("cache directory", global_settings["cache directory"])
+ lp.set("binddns dir", global_settings["binddns dir"])
if eadb:
if use_ntvfs and not lp.get("posix:eadb"):
if serverrole == "active directory domain controller":
shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
- "scripts")
+ "scripts")
else:
global_settings["passdb backend"] = "samba_dsdb"
f = open(smbconf, 'w')
try:
f.write("[globals]\n")
- for key, val in global_settings.iteritems():
+ for key, val in global_settings.items():
f.write("\t%s = %s\n" % (key, val))
f.write("\n")
- for name, path in shares.iteritems():
+ for name, path in shares.items():
f.write("[%s]\n" % name)
f.write("\tpath = %s\n" % path)
f.write("\tread only = no\n")
# and dump it without any values that are the default
# this ensures that any smb.conf parameters that were set
# on the provision/join command line are set in the resulting smb.conf
- f = open(smbconf, mode='w')
- try:
- lp.dump(f, False)
- finally:
- f.close()
+ lp.dump(False, smbconf)
def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
def setup_samdb_partitions(samdb_path, logger, lp, session_info,
- provision_backend, names, schema, serverrole,
- erase=False):
+ provision_backend, names, serverrole,
+ erase=False, plaintext_secrets=False,
+ backend_store=None):
"""Setup the partitions for the SAM database.
Alternatively, provision() may call this, and then populate the database.
if provision_backend.type != "ldb":
ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
+ required_features = None
+ if not plaintext_secrets:
+ required_features = "requiredFeatures: encryptedSecrets"
+
+ if backend_store is None:
+ backend_store = get_default_backend_store()
+ backend_store_line = "backendStore: %s" % backend_store
+
+ if backend_store == "mdb":
+ if required_features is not None:
+ required_features += "\n"
+ else:
+ required_features = ""
+ required_features += "requiredFeatures: lmdbLevelOne"
+
+ if required_features is None:
+ required_features = "# No required features"
+
samdb.transaction_start()
try:
logger.info("Setting up sam.ldb partitions and settings")
setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
- "LDAP_BACKEND_LINE": ldap_backend_line
+ "LDAP_BACKEND_LINE": ldap_backend_line,
+ "BACKEND_STORE": backend_store_line
})
-
setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
"BACKEND_TYPE": provision_backend.type,
- "SERVER_ROLE": serverrole
+ "SERVER_ROLE": serverrole,
+ "REQUIRED_FEATURES": required_features
})
logger.info("Setting up sam.ldb rootDSE")
:param machinepass: Machine password
"""
attrs = ["whenChanged",
- "secret",
- "priorSecret",
- "priorChanged",
- "krb5Keytab",
- "privateKeytab"]
+ "secret",
+ "priorSecret",
+ "priorChanged",
+ "krb5Keytab",
+ "privateKeytab"]
if realm is not None:
if dnsdomain is None:
msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
msg["privateKeytab"] = ["secrets.keytab"]
- msg["secret"] = [machinepass]
+ msg["secret"] = [machinepass.encode('utf-8')]
msg["samAccountName"] = ["%s$" % netbiosname]
msg["secureChannelType"] = [str(secure_channel_type)]
if domainsid is not None:
# but we don't delete the old record that we are about to modify,
# because that would delete the keytab and previous password.
res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
- expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
- scope=ldb.SCOPE_ONELEVEL)
+ expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
+ scope=ldb.SCOPE_ONELEVEL)
for del_msg in res:
secretsdb.delete(del_msg.dn)
if len(res) == 1:
msg["priorSecret"] = [res[0]["secret"][0]]
- msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
+ try:
+ msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
+ except KeyError:
+ pass
try:
msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
secretsdb.modify(msg)
secretsdb.rename(res[0].dn, msg.dn)
else:
- spn = [ 'HOST/%s' % shortname ]
+ spn = ['HOST/%s' % shortname]
if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
# we are a domain controller then we add servicePrincipalName
# entries for the keytab code to update.
- spn.extend([ 'HOST/%s' % dnsname ])
+ spn.extend(['HOST/%s' % dnsname])
msg["servicePrincipalName"] = spn
secretsdb.add(msg)
def setup_secretsdb(paths, session_info, backend_credentials, lp):
"""Setup the secrets database.
- :note: This function does not handle exceptions and transaction on purpose,
+ :note: This function does not handle exceptions and transaction on purpose,
it's up to the caller to do this job.
:param path: Path to the secrets database.
if os.path.exists(keytab_path):
os.unlink(keytab_path)
+ bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
+ if os.path.exists(bind_dns_keytab_path):
+ os.unlink(bind_dns_keytab_path)
+
dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
if os.path.exists(dns_keytab_path):
os.unlink(dns_keytab_path)
backend_credentials.authentication_requested()):
if backend_credentials.get_bind_dn() is not None:
setup_add_ldif(secrets_ldb,
- setup_path("secrets_simple_ldap.ldif"), {
+ setup_path("secrets_simple_ldap.ldif"), {
"LDAPMANAGERDN": backend_credentials.get_bind_dn(),
- "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
- })
+ "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
+ })
else:
setup_add_ldif(secrets_ldb,
- setup_path("secrets_sasl_ldap.ldif"), {
+ setup_path("secrets_sasl_ldap.ldif"), {
"LDAPADMINUSER": backend_credentials.get_username(),
"LDAPADMINREALM": backend_credentials.get_realm(),
- "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
- })
+ "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
+ })
except:
secrets_ldb.transaction_cancel()
raise
privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
+def setup_encrypted_secrets_key(path):
+ """Setup the encrypted secrets key file.
+
+ Any existing key file will be deleted and a new random key generated.
+
+ :param path: Path to the secrets key file.
+
+ """
+ if os.path.exists(path):
+ os.unlink(path)
+
+ flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
+ mode = stat.S_IRUSR | stat.S_IWUSR
+
+ umask_original = os.umask(0)
+ try:
+ fd = os.open(path, flags, mode)
+ finally:
+ os.umask(umask_original)
+
+ with os.fdopen(fd, 'w') as f:
+ key = samba.generate_random_bytes(16)
+ f.write(key)
+
+
def setup_registry(path, session_info, lp):
"""Setup the registry.
setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
"SCHEMADN": names.schemadn,
"DOMAINDN": names.domaindn,
- "ROOTDN" : names.rootdn,
+ "ROOTDN": names.rootdn,
"CONFIGDN": names.configdn,
"SERVERDN": names.serverdn,
- })
+ })
def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
- dns_backend, dnspass, domainsid, next_rid, invocationid,
- policyguid, policyguid_dc,
- domainControllerFunctionality, ntdsguid=None, dc_rid=None):
+ dns_backend, dnspass, domainsid, next_rid, invocationid,
+ policyguid, policyguid_dc,
+ domainControllerFunctionality, ntdsguid=None, dc_rid=None):
"""Join a host to its own domain."""
assert isinstance(invocationid, str)
if ntdsguid is not None:
- ntdsguid_line = "objectGUID: %s\n"%ntdsguid
+ ntdsguid_line = "objectGUID: %s\n" %ntdsguid
else:
ntdsguid_line = ""
"INVOCATIONID": invocationid,
"NETBIOSNAME": names.netbiosname,
"DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
- "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
+ "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
"DOMAINSID": str(domainsid),
"DCRID": str(dc_rid),
"SAMBA_VERSION_STRING": version,
"INVOCATIONID": invocationid,
"NETBIOSNAME": names.netbiosname,
"DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
- "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
+ "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
"DOMAINSID": str(domainsid),
"DCRID": str(dc_rid),
"SAMBA_VERSION_STRING": version,
# Setup fSMORoleOwner entries to point at the newly created DC entry
setup_modify_ldif(samdb,
- setup_path("provision_self_join_modify_config.ldif"), {
+ setup_path("provision_self_join_modify_config.ldif"), {
"CONFIGDN": names.configdn,
"SCHEMADN": names.schemadn,
"DEFAULTSITE": names.sitename,
"NETBIOSNAME": names.netbiosname,
"SERVERDN": names.serverdn,
- })
+ })
system_session_info = system_session()
samdb.set_session_info(system_session_info)
setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
"DNSDOMAIN": names.dnsdomain,
"DOMAINDN": names.domaindn,
- "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
- "HOSTNAME" : names.hostname,
- "DNSNAME" : '%s.%s' % (
+ "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
+ "HOSTNAME": names.hostname,
+ "DNSNAME": '%s.%s' % (
names.netbiosname.lower(), names.dnsdomain.lower())
})
def create_gpo_struct(policy_path):
if not os.path.exists(policy_path):
- os.makedirs(policy_path, 0775)
+ os.makedirs(policy_path, 0o775)
f = open(os.path.join(policy_path, "GPT.INI"), 'w')
try:
f.write("[General]\r\nVersion=0")
f.close()
p = os.path.join(policy_path, "MACHINE")
if not os.path.exists(p):
- os.makedirs(p, 0775)
+ os.makedirs(p, 0o775)
p = os.path.join(policy_path, "USER")
if not os.path.exists(p):
- os.makedirs(p, 0775)
+ os.makedirs(p, 0o775)
def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
:param policyguid: GUID of the default domain policy
:param policyguid_dc: GUID of the default domain controler policy
"""
- policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
+ policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
create_gpo_struct(policy_path)
- policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
+ policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
create_gpo_struct(policy_path)
def setup_samdb(path, session_info, provision_backend, lp, names,
- logger, fill, serverrole, schema, am_rodc=False):
+ logger, fill, serverrole, schema, am_rodc=False,
+ plaintext_secrets=False, backend_store=None):
"""Setup a complete SAM Database.
:note: This will wipe the main SAM database file!
# Also wipes the database
setup_samdb_partitions(path, logger=logger, lp=lp,
- provision_backend=provision_backend, session_info=session_info,
- names=names, serverrole=serverrole, schema=schema)
+ provision_backend=provision_backend, session_info=session_info,
+ names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
+ backend_store=backend_store)
# Load the database, but don's load the global schema and don't connect
# quite yet
# And now we can connect to the DB - the schema won't be loaded from the
# DB
- samdb.connect(path)
+ try:
+ samdb.connect(path)
+ except ldb.LdbError as e2:
+ (num, string_error) = e2.args
+ if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
+ raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
+ else:
+ raise
# But we have to give it one more kick to have it use the schema
# during provision - it needs, now that it is connected, to write
return samdb
-def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
- policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
- dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
- dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
+def fill_samdb(samdb, lp, names, logger, policyguid,
+ policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
+ dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
+ dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
+ backend_store=None):
if next_rid is None:
next_rid = 1000
domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
if dom_for_fun_level is None:
- dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
+ dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
if dom_for_fun_level > domainControllerFunctionality:
raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
# before the provisioned tree exists and we connect
samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
- samdb.transaction_start()
- try:
- # Set the domain functionality levels onto the database.
- # Various module (the password_hash module in particular) need
- # to know what level of AD we are emulating.
-
- # These will be fixed into the database via the database
- # modifictions below, but we need them set from the start.
- samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
- samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
- samdb.set_opaque_integer("domainControllerFunctionality",
- domainControllerFunctionality)
-
- samdb.set_domain_sid(str(domainsid))
- samdb.set_invocation_id(invocationid)
-
- logger.info("Adding DomainDN: %s" % names.domaindn)
-
- # impersonate domain admin
- admin_session_info = admin_session(lp, str(domainsid))
- samdb.set_session_info(admin_session_info)
- if domainguid is not None:
- domainguid_line = "objectGUID: %s\n-" % domainguid
- else:
- domainguid_line = ""
-
- descr = b64encode(get_domain_descriptor(domainsid))
- setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
- "DOMAINDN": names.domaindn,
- "DOMAINSID": str(domainsid),
- "DESCRIPTOR": descr,
- "DOMAINGUID": domainguid_line
- })
-
- setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
- "DOMAINDN": names.domaindn,
- "CREATTIME": str(samba.unix2nttime(int(time.time()))),
- "NEXTRID": str(next_rid),
- "DEFAULTSITE": names.sitename,
- "CONFIGDN": names.configdn,
- "POLICYGUID": policyguid,
- "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
- "SAMBA_VERSION_STRING": version
- })
+ # Set the domain functionality levels onto the database.
+ # Various module (the password_hash module in particular) need
+ # to know what level of AD we are emulating.
- # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
- if fill == FILL_FULL:
- logger.info("Adding configuration container")
- descr = b64encode(get_config_descriptor(domainsid))
- setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
- "CONFIGDN": names.configdn,
- "DESCRIPTOR": descr,
- })
+ # These will be fixed into the database via the database
+ # modifictions below, but we need them set from the start.
+ samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
+ samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
+ samdb.set_opaque_integer("domainControllerFunctionality",
+ domainControllerFunctionality)
- # The LDIF here was created when the Schema object was constructed
- logger.info("Setting up sam.ldb schema")
- samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
- samdb.modify_ldif(schema.schema_dn_modify)
- samdb.write_prefixes_from_schema()
- samdb.add_ldif(schema.schema_data, controls=["relax:0"])
- setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
- {"SCHEMADN": names.schemadn})
+ samdb.set_domain_sid(str(names.domainsid))
+ samdb.set_invocation_id(invocationid)
- # Now register this container in the root of the forest
- msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
- msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
- "subRefs")
+ logger.info("Adding DomainDN: %s" % names.domaindn)
- except:
- samdb.transaction_cancel()
- raise
+ # impersonate domain admin
+ admin_session_info = admin_session(lp, str(names.domainsid))
+ samdb.set_session_info(admin_session_info)
+ if names.domainguid is not None:
+ domainguid_line = "objectGUID: %s\n-" % names.domainguid
else:
- samdb.transaction_commit()
+ domainguid_line = ""
- samdb.transaction_start()
- try:
- samdb.invocation_id = invocationid
-
- # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
- if fill == FILL_FULL:
- logger.info("Setting up sam.ldb configuration data")
-
- partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
- sites_descr = b64encode(get_config_sites_descriptor(domainsid))
- ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(domainsid))
- protected1_descr = b64encode(get_config_delete_protected1_descriptor(domainsid))
- protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
- protected2_descr = b64encode(get_config_delete_protected2_descriptor(domainsid))
-
- setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
- "CONFIGDN": names.configdn,
- "NETBIOSNAME": names.netbiosname,
- "DEFAULTSITE": names.sitename,
- "DNSDOMAIN": names.dnsdomain,
- "DOMAIN": names.domain,
- "SCHEMADN": names.schemadn,
- "DOMAINDN": names.domaindn,
- "SERVERDN": names.serverdn,
- "FOREST_FUNCTIONALITY": str(forestFunctionality),
- "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
- "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
- "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
- "SERVICES_DESCRIPTOR": protected1_descr,
- "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
- "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
- "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
- "PARTITIONS_DESCRIPTOR": partitions_descr,
- "SITES_DESCRIPTOR": sites_descr,
- })
+ descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
+ setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "DOMAINSID": str(names.domainsid),
+ "DESCRIPTOR": descr,
+ "DOMAINGUID": domainguid_line
+ })
- logger.info("Setting up display specifiers")
- display_specifiers_ldif = read_ms_ldif(
- setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
- display_specifiers_ldif = substitute_var(display_specifiers_ldif,
- {"CONFIGDN": names.configdn})
- check_all_substituted(display_specifiers_ldif)
- samdb.add_ldif(display_specifiers_ldif)
-
- logger.info("Modifying display specifiers")
- setup_modify_ldif(samdb,
- setup_path("provision_configuration_modify.ldif"), {
+ setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "CREATTIME": str(samba.unix2nttime(int(time.time()))),
+ "NEXTRID": str(next_rid),
+ "DEFAULTSITE": names.sitename,
+ "CONFIGDN": names.configdn,
+ "POLICYGUID": policyguid,
+ "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
+ "SAMBA_VERSION_STRING": version,
+ "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
+ })
+
+ # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
+ if fill == FILL_FULL:
+ logger.info("Adding configuration container")
+ descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
+ setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
"CONFIGDN": names.configdn,
- "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
+ "DESCRIPTOR": descr,
})
- logger.info("Adding users container")
- users_desc = b64encode(get_domain_users_descriptor(domainsid))
- setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
+ # The LDIF here was created when the Schema object was constructed
+ ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
+ logger.info("Setting up sam.ldb schema")
+ samdb.add_ldif(schema.schema_dn_add,
+ controls=["relax:0", ignore_checks_oid])
+ samdb.modify_ldif(schema.schema_dn_modify,
+ controls=[ignore_checks_oid])
+ samdb.write_prefixes_from_schema()
+ samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
+ setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
+ {"SCHEMADN": names.schemadn},
+ controls=["relax:0", ignore_checks_oid])
+
+ # Now register this container in the root of the forest
+ msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
+ msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
+ "subRefs")
+
+ samdb.invocation_id = invocationid
+
+ # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
+ if fill == FILL_FULL:
+ logger.info("Setting up sam.ldb configuration data")
+
+ partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
+ sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
+ ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
+ protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
+ protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
+ protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
+
+ if "2008" in schema.base_schema:
+ # exclude 2012-specific changes if we're using a 2008 schema
+ incl_2012 = "#"
+ else:
+ incl_2012 = ""
+
+ setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
+ "CONFIGDN": names.configdn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "DNSDOMAIN": names.dnsdomain,
+ "DOMAIN": names.domain,
+ "SCHEMADN": names.schemadn,
"DOMAINDN": names.domaindn,
- "USERS_DESCRIPTOR": users_desc
+ "SERVERDN": names.serverdn,
+ "FOREST_FUNCTIONALITY": str(forestFunctionality),
+ "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
+ "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
+ "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
+ "SERVICES_DESCRIPTOR": protected1_descr,
+ "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
+ "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
+ "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
+ "PARTITIONS_DESCRIPTOR": partitions_descr,
+ "SITES_DESCRIPTOR": sites_descr,
})
- logger.info("Modifying users container")
- setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
- "DOMAINDN": names.domaindn})
- logger.info("Adding computers container")
- computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
- setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
- "DOMAINDN": names.domaindn,
- "COMPUTERS_DESCRIPTOR": computers_desc
+
+ setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
+ "CONFIGDN": names.configdn,
+ "INC2012" : incl_2012,
})
- logger.info("Modifying computers container")
+
+ logger.info("Setting up display specifiers")
+ display_specifiers_ldif = read_ms_ldif(
+ setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
+ display_specifiers_ldif = substitute_var(display_specifiers_ldif,
+ {"CONFIGDN": names.configdn})
+ check_all_substituted(display_specifiers_ldif)
+ samdb.add_ldif(display_specifiers_ldif)
+
+ logger.info("Modifying display specifiers and extended rights")
setup_modify_ldif(samdb,
- setup_path("provision_computers_modify.ldif"), {
- "DOMAINDN": names.domaindn})
- logger.info("Setting up sam.ldb data")
- infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
- lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(domainsid))
- system_desc = b64encode(get_domain_delete_protected1_descriptor(domainsid))
- builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
- controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
- setup_add_ldif(samdb, setup_path("provision.ldif"), {
- "CREATTIME": str(samba.unix2nttime(int(time.time()))),
- "DOMAINDN": names.domaindn,
- "NETBIOSNAME": names.netbiosname,
- "DEFAULTSITE": names.sitename,
- "CONFIGDN": names.configdn,
- "SERVERDN": names.serverdn,
- "RIDAVAILABLESTART": str(next_rid + 600),
- "POLICYGUID_DC": policyguid_dc,
- "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
- "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
- "SYSTEM_DESCRIPTOR": system_desc,
- "BUILTIN_DESCRIPTOR": builtin_desc,
- "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
+ setup_path("provision_configuration_modify.ldif"), {
+ "CONFIGDN": names.configdn,
+ "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
})
- # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
- if fill == FILL_FULL:
- setup_modify_ldif(samdb,
- setup_path("provision_configuration_references.ldif"), {
- "CONFIGDN": names.configdn,
- "SCHEMADN": names.schemadn})
+ logger.info("Adding users container")
+ users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
+ setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "USERS_DESCRIPTOR": users_desc
+ })
+ logger.info("Modifying users container")
+ setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
+ "DOMAINDN": names.domaindn})
+ logger.info("Adding computers container")
+ computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
+ setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "COMPUTERS_DESCRIPTOR": computers_desc
+ })
+ logger.info("Modifying computers container")
+ setup_modify_ldif(samdb,
+ setup_path("provision_computers_modify.ldif"), {
+ "DOMAINDN": names.domaindn})
+ logger.info("Setting up sam.ldb data")
+ infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
+ lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
+ system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
+ builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
+ controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
+ setup_add_ldif(samdb, setup_path("provision.ldif"), {
+ "CREATTIME": str(samba.unix2nttime(int(time.time()))),
+ "DOMAINDN": names.domaindn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "CONFIGDN": names.configdn,
+ "SERVERDN": names.serverdn,
+ "RIDAVAILABLESTART": str(next_rid + 600),
+ "POLICYGUID_DC": policyguid_dc,
+ "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
+ "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
+ "SYSTEM_DESCRIPTOR": system_desc,
+ "BUILTIN_DESCRIPTOR": builtin_desc,
+ "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
+ })
+
+ # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
+ if fill == FILL_FULL:
+ managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
+ setup_modify_ldif(samdb,
+ setup_path("provision_configuration_references.ldif"), {
+ "CONFIGDN": names.configdn,
+ "SCHEMADN": names.schemadn})
- logger.info("Setting up well known security principals")
- protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
- setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
- "CONFIGDN": names.configdn,
- "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
- })
+ logger.info("Setting up well known security principals")
+ protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
+ setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
+ "CONFIGDN": names.configdn,
+ "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
+ }, controls=["relax:0", "provision:0"])
- if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
- setup_modify_ldif(samdb,
- setup_path("provision_basedn_references.ldif"),
- {"DOMAINDN": names.domaindn})
+ if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
+ setup_modify_ldif(samdb,
+ setup_path("provision_basedn_references.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
+ })
- logger.info("Setting up sam.ldb users and groups")
- setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
- "DOMAINDN": names.domaindn,
- "DOMAINSID": str(domainsid),
- "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
- "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
- })
+ logger.info("Setting up sam.ldb users and groups")
+ setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "DOMAINSID": str(names.domainsid),
+ "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
+ "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
+ }, controls=["relax:0", "provision:0"])
+
+ logger.info("Setting up self join")
+ setup_self_join(samdb, admin_session_info, names=names, fill=fill,
+ invocationid=invocationid,
+ dns_backend=dns_backend,
+ dnspass=dnspass,
+ machinepass=machinepass,
+ domainsid=names.domainsid,
+ next_rid=next_rid,
+ dc_rid=dc_rid,
+ policyguid=policyguid,
+ policyguid_dc=policyguid_dc,
+ domainControllerFunctionality=domainControllerFunctionality,
+ ntdsguid=ntdsguid)
+
+ ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
+ names.ntdsguid = samdb.searchone(basedn=ntds_dn,
+ attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
+ assert isinstance(names.ntdsguid, str)
- logger.info("Setting up self join")
- setup_self_join(samdb, admin_session_info, names=names, fill=fill,
- invocationid=invocationid,
- dns_backend=dns_backend,
- dnspass=dnspass,
- machinepass=machinepass,
- domainsid=domainsid,
- next_rid=next_rid,
- dc_rid=dc_rid,
- policyguid=policyguid,
- policyguid_dc=policyguid_dc,
- domainControllerFunctionality=domainControllerFunctionality,
- ntdsguid=ntdsguid)
-
- ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
- names.ntdsguid = samdb.searchone(basedn=ntds_dn,
- attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
- assert isinstance(names.ntdsguid, str)
- except:
- samdb.transaction_cancel()
- raise
- else:
- samdb.transaction_commit()
- return samdb
+ return samdb
-FILL_FULL = "FULL"
-FILL_SUBDOMAIN = "SUBDOMAIN"
-FILL_NT4SYNC = "NT4SYNC"
-FILL_DRS = "DRS"
SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
-SYSVOL_SERVICE="sysvol"
+SYSVOL_SERVICE = "sysvol"
+
def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
for root, dirs, files in os.walk(path, topdown=False):
for name in files:
setntacl(lp, os.path.join(root, name), acl, domsid,
- use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
+ use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
for name in dirs:
setntacl(lp, os.path.join(root, name), acl, domsid,
- use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
+ use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
# Set ACL for GPO root folder
root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
- use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
+ use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
- res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
- attrs=["cn", "nTSecurityDescriptor"],
- expression="", scope=ldb.SCOPE_ONELEVEL)
+ res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
+ attrs=["cn", "nTSecurityDescriptor"],
+ expression="", scope=ldb.SCOPE_ONELEVEL)
for policy in res:
acl = ndr_unpack(security.descriptor,
def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
- domaindn, lp, use_ntvfs):
+ domaindn, lp, use_ntvfs):
"""Set the ACL for the sysvol share and the subfolders
:param samdb: An LDB object on the SAM db
s4_passdb = None
if not use_ntvfs:
+ s3conf = s3param.get_context()
+ s3conf.load(lp.configfile)
+
+ file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
+ try:
+ try:
+ smbd.set_simple_acl(file.name, 0o755, gid)
+ except OSError:
+ if not smbd.have_posix_acls():
+ # This clue is only strictly correct for RPM and
+ # Debian-like Linux systems, but hopefully other users
+ # will get enough clue from it.
+ raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
+ "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
+
+ raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
+ "Try the mounting the filesystem with the 'acl' option.")
+ try:
+ smbd.chown(file.name, uid, gid)
+ except OSError:
+ raise ProvisioningError("Unable to chown a file on your filesystem. "
+ "You may not be running provision as root.")
+ finally:
+ file.close()
+
# This will ensure that the smbd code we are running when setting ACLs
# is initialised with the smb.conf
s3conf = s3param.get_context()
if domain_info["dns_domain"].upper() != dnsdomain.upper():
raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
-
try:
if use_ntvfs:
os.chown(sysvol, -1, gid)
else:
canchown = True
+ # use admin sid dn as user dn, since admin should own most of the files,
+ # the operation will be much faster
+ userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
+
+ flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
+ auth.AUTH_SESSION_INFO_AUTHENTICATED |
+ auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
+
+ session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
+ session_info_flags=flags)
+
+ def _setntacl(path):
+ """A helper to reuse args"""
+ return setntacl(
+ lp, path, SYSVOL_ACL, str(domainsid),
+ use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
+ service=SYSVOL_SERVICE, session_info=session_info)
+
# Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
- setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
- skip_invalid_chown=True, passdb=s4_passdb,
- service=SYSVOL_SERVICE)
+ _setntacl(sysvol)
for root, dirs, files in os.walk(sysvol, topdown=False):
for name in files:
if use_ntvfs and canchown:
os.chown(os.path.join(root, name), -1, gid)
- setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
- use_ntvfs=use_ntvfs, skip_invalid_chown=True,
- passdb=s4_passdb, service=SYSVOL_SERVICE)
+ _setntacl(os.path.join(root, name))
for name in dirs:
if use_ntvfs and canchown:
os.chown(os.path.join(root, name), -1, gid)
- setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
- use_ntvfs=use_ntvfs, skip_invalid_chown=True,
- passdb=s4_passdb, service=SYSVOL_SERVICE)
+ _setntacl(os.path.join(root, name))
# Set acls on Policy folder and policies folders
set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
+
def acl_type(direct_db_access):
if direct_db_access:
return "DB"
else:
return "VFS"
+
def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
fsacl_sddl = fsacl.as_sddl(domainsid)
def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
- direct_db_access):
+ direct_db_access):
"""Set ACL on the sysvol/<dnsname>/Policies folder and the policy
folders beneath.
fsacl_sddl = fsacl.as_sddl(domainsid)
if fsacl_sddl != POLICIES_ACL:
raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
- res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
- attrs=["cn", "nTSecurityDescriptor"],
- expression="", scope=ldb.SCOPE_ONELEVEL)
+ res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
+ attrs=["cn", "nTSecurityDescriptor"],
+ expression="", scope=ldb.SCOPE_ONELEVEL)
for policy in res:
acl = ndr_unpack(security.descriptor,
def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
- lp):
+ lp):
"""Set the ACL for the sysvol share and the subfolders
:param samdb: An LDB object on the SAM db
# Check acls on Policy folder and policies folders
check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
- direct_db_access)
+ direct_db_access)
def interface_ips_v4(lp):
def provision_fill(samdb, secrets_ldb, logger, names, paths,
- domainsid, schema=None,
+ schema=None,
targetdir=None, samdb_fill=FILL_FULL,
hostip=None, hostip6=None,
next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
invocationid=None, machinepass=None, ntdsguid=None,
dns_backend=None, dnspass=None,
serverrole=None, dom_for_fun_level=None,
- am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
+ am_rodc=False, lp=None, use_ntvfs=False,
+ skip_sysvolacl=False, backend_store=None):
# create/adapt the group policy GUIDs
# Default GUID for default policy are described at
# "How Core Group Policy Works"
invocationid = str(uuid.uuid4())
if krbtgtpass is None:
- krbtgtpass = samba.generate_random_password(128, 255)
+ krbtgtpass = samba.generate_random_machine_password(128, 255)
if machinepass is None:
- machinepass = samba.generate_random_password(128, 255)
+ machinepass = samba.generate_random_machine_password(128, 255)
if dnspass is None:
dnspass = samba.generate_random_password(128, 255)
- samdb = fill_samdb(samdb, lp, names, logger=logger,
- domainsid=domainsid, schema=schema, domainguid=domainguid,
- policyguid=policyguid, policyguid_dc=policyguid_dc,
- fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
- invocationid=invocationid, machinepass=machinepass,
- dns_backend=dns_backend, dnspass=dnspass,
- ntdsguid=ntdsguid, serverrole=serverrole,
- dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
- next_rid=next_rid, dc_rid=dc_rid)
-
- if serverrole == "active directory domain controller":
+ samdb.transaction_start()
+ try:
+ samdb = fill_samdb(samdb, lp, names, logger=logger,
+ schema=schema,
+ policyguid=policyguid, policyguid_dc=policyguid_dc,
+ fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
+ invocationid=invocationid, machinepass=machinepass,
+ dns_backend=dns_backend, dnspass=dnspass,
+ ntdsguid=ntdsguid, serverrole=serverrole,
+ dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
+ next_rid=next_rid, dc_rid=dc_rid,
+ backend_store=backend_store)
# Set up group policies (domain policy and domain controller
# policy)
- create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
- policyguid_dc)
+ if serverrole == "active directory domain controller":
+ create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
+ policyguid_dc)
+ except:
+ samdb.transaction_cancel()
+ raise
+ else:
+ samdb.transaction_commit()
+
+ if serverrole == "active directory domain controller":
+ # Continue setting up sysvol for GPO. This appears to require being
+ # outside a transaction.
if not skip_sysvolacl:
setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
- paths.root_gid, domainsid, names.dnsdomain,
+ paths.root_gid, names.domainsid, names.dnsdomain,
names.domaindn, lp, use_ntvfs)
else:
logger.info("Setting acl on sysvol skipped")
secretsdb_self_join(secrets_ldb, domain=names.domain,
- realm=names.realm, dnsdomain=names.dnsdomain,
- netbiosname=names.netbiosname, domainsid=domainsid,
- machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
+ realm=names.realm, dnsdomain=names.dnsdomain,
+ netbiosname=names.netbiosname, domainsid=names.domainsid,
+ machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
# Now set up the right msDS-SupportedEncryptionTypes into the DB
# In future, this might be determined from some configuration
msg = ldb.Message(ldb.Dn(samdb,
samdb.searchone("distinguishedName",
expression="samAccountName=%s$" % names.netbiosname,
- scope=ldb.SCOPE_SUBTREE)))
+ scope=ldb.SCOPE_SUBTREE).decode('utf8')))
msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
name="msDS-SupportedEncryptionTypes")
samdb.modify(msg)
- except ldb.LdbError, (enum, estr):
+ except ldb.LdbError as e:
+ (enum, estr) = e.args
if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
# It might be that this attribute does not exist in this schema
raise
- setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
+ setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
dnspass=dnspass, os_level=dom_for_fun_level,
- targetdir=targetdir, site=DEFAULTSITE)
+ targetdir=targetdir, fill_level=samdb_fill,
+ backend_store=backend_store)
domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
attribute="objectGUID")
logger.info("Setting up sam.ldb rootDSE marking as synchronized")
setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
- { 'NTDSGUID' : names.ntdsguid })
+ {'NTDSGUID': names.ntdsguid})
# fix any dangling GUIDs from the provision
logger.info("Fixing provision GUIDs")
chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
- quiet=True)
+ quiet=True)
samdb.transaction_start()
try:
# a small number of GUIDs are missing because of ordering issues in the
'ipsecISAKMPReference',
'ipsecNegotiationPolicyReference',
'ipsecNFAReference'])
+ if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
+ attrs=['attributeId', 'governsId']) != 0:
+ raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
except:
samdb.transaction_cancel()
raise
"member server": "member server",
"standalone": "standalone server",
"standalone server": "standalone server",
- }
+}
def sanitize_server_role(role):
def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
- maxuid, maxgid):
+ maxuid, maxgid):
"""Create AD entries for the fake ypserver.
This is needed for being able to manipulate posix attrs via ADUC.
try:
logger.info("Setting up fake yp server settings")
setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
- "DOMAINDN": domaindn,
- "NETBIOSNAME": netbiosname,
- "NISDOMAIN": nisdomain,
- })
+ "DOMAINDN": domaindn,
+ "NETBIOSNAME": netbiosname,
+ "NISDOMAIN": nisdomain,
+ })
except:
samdb.transaction_cancel()
raise
samdb.transaction_commit()
+def directory_create_or_exists(path, mode=0o755):
+ if not os.path.exists(path):
+ try:
+ os.mkdir(path, mode)
+ except OSError as e:
+ if e.errno in [errno.EEXIST]:
+ pass
+ else:
+ raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
+
+
+def determine_host_ip(logger, lp, hostip=None):
+ if hostip is None:
+ logger.info("Looking up IPv4 addresses")
+ hostips = interface_ips_v4(lp)
+ if len(hostips) > 0:
+ hostip = hostips[0]
+ if len(hostips) > 1:
+ logger.warning("More than one IPv4 address found. Using %s",
+ hostip)
+ if hostip == "127.0.0.1":
+ hostip = None
+ if hostip is None:
+ logger.warning("No IPv4 address will be assigned")
+
+ return hostip
+
+
+def determine_host_ip6(logger, lp, hostip6=None):
+ if hostip6 is None:
+ logger.info("Looking up IPv6 addresses")
+ hostips = interface_ips_v6(lp)
+ if hostips:
+ hostip6 = hostips[0]
+ if len(hostips) > 1:
+ logger.warning("More than one IPv6 address found. Using %s", hostip6)
+ if hostip6 is None:
+ logger.warning("No IPv6 address will be assigned")
+
+ return hostip6
+
+
def provision(logger, session_info, smbconf=None,
- targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
- domaindn=None, schemadn=None, configdn=None, serverdn=None,
- domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
- next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
- krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
- dns_backend=None, dns_forwarder=None, dnspass=None,
- invocationid=None, machinepass=None, ntdsguid=None,
- root=None, nobody=None, users=None, backup=None, aci=None,
- serverrole=None, dom_for_fun_level=None, backend_type=None,
- sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
- useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
- use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
- ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
+ targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
+ domaindn=None, schemadn=None, configdn=None, serverdn=None,
+ domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
+ next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
+ krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
+ dns_backend=None, dns_forwarder=None, dnspass=None,
+ invocationid=None, machinepass=None, ntdsguid=None,
+ root=None, nobody=None, users=None, backup=None, aci=None,
+ serverrole=None, dom_for_fun_level=None, backend_type=None,
+ sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
+ useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
+ use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
+ ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
+ ldap_backend_extra_port=None, base_schema=None,
+ plaintext_secrets=False, backend_store=None):
"""Provision samba4
:note: caution, this wipes all existing data!
if backend_type is None:
backend_type = "ldb"
+ if backend_store is None:
+ backend_store = get_default_backend_store()
if domainsid is None:
domainsid = security.random_sid()
- else:
- domainsid = security.dom_sid(domainsid)
root_uid = findnss_uid([root or "root"])
nobody_uid = findnss_uid([nobody or "nobody"])
lp = samba.param.LoadParm()
lp.load(smbconf)
names = guess_names(lp=lp, hostname=hostname, domain=domain,
- dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
- configdn=configdn, schemadn=schemadn, serverdn=serverdn,
- sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
+ dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
+ configdn=configdn, schemadn=schemadn, serverdn=serverdn,
+ sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
paths = provision_paths_from_lp(lp, names.dnsdomain)
paths.bind_gid = bind_gid
- paths.root_uid = root_uid;
+ paths.root_uid = root_uid
paths.root_gid = root_gid
- if hostip is None:
- logger.info("Looking up IPv4 addresses")
- hostips = interface_ips_v4(lp)
- if len(hostips) > 0:
- hostip = hostips[0]
- if len(hostips) > 1:
- logger.warning("More than one IPv4 address found. Using %s",
- hostip)
- if hostip == "127.0.0.1":
- hostip = None
- if hostip is None:
- logger.warning("No IPv4 address will be assigned")
-
- if hostip6 is None:
- logger.info("Looking up IPv6 addresses")
- hostips = interface_ips_v6(lp)
- if hostips:
- hostip6 = hostips[0]
- if len(hostips) > 1:
- logger.warning("More than one IPv6 address found. Using %s", hostip6)
- if hostip6 is None:
- logger.warning("No IPv6 address will be assigned")
-
+ hostip = determine_host_ip(logger, lp, hostip)
+ hostip6 = determine_host_ip6(logger, lp, hostip6)
names.hostip = hostip
names.hostip6 = hostip6
+ names.domainguid = domainguid
+ names.domainsid = domainsid
+ names.forestsid = domainsid
if serverrole is None:
serverrole = lp.get("server role")
- if not os.path.exists(paths.private_dir):
- os.mkdir(paths.private_dir)
- if not os.path.exists(os.path.join(paths.private_dir, "tls")):
- os.mkdir(os.path.join(paths.private_dir, "tls"))
- if not os.path.exists(paths.state_dir):
- os.mkdir(paths.state_dir)
+ directory_create_or_exists(paths.private_dir, 0o700)
+ directory_create_or_exists(paths.binddns_dir, 0o770)
+ directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
+ directory_create_or_exists(paths.state_dir)
+ if not plaintext_secrets:
+ setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
if paths.sysvol and not os.path.exists(paths.sysvol):
- os.makedirs(paths.sysvol, 0775)
-
- if not use_ntvfs and serverrole == "active directory domain controller":
- s3conf = s3param.get_context()
- s3conf.load(lp.configfile)
-
- if paths.sysvol is None:
- raise MissingShareError("sysvol", paths.smbconf)
-
- file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
- try:
- try:
- smbd.set_simple_acl(file.name, 0755, root_gid)
- except Exception:
- if not smbd.have_posix_acls():
- # This clue is only strictly correct for RPM and
- # Debian-like Linux systems, but hopefully other users
- # will get enough clue from it.
- raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
+ os.makedirs(paths.sysvol, 0o775)
- raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
- try:
- smbd.chown(file.name, root_uid, root_gid)
- except Exception:
- raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
- finally:
- file.close()
-
- ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
+ ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
schema = Schema(domainsid, invocationid=invocationid,
- schemadn=names.schemadn)
+ schemadn=names.schemadn, base_schema=base_schema)
if backend_type == "ldb":
provision_backend = LDBBackend(backend_type, paths=paths,
- lp=lp,
- names=names, logger=logger)
+ lp=lp,
+ names=names, logger=logger)
elif backend_type == "existing":
# If support for this is ever added back, then the URI will need to be
# specified again
provision_backend = ExistingBackend(backend_type, paths=paths,
- lp=lp,
- names=names, logger=logger,
- ldap_backend_forced_uri=ldap_backend_forced_uri)
+ lp=lp,
+ names=names, logger=logger,
+ ldap_backend_forced_uri=ldap_backend_forced_uri)
elif backend_type == "fedora-ds":
provision_backend = FDSBackend(backend_type, paths=paths,
- lp=lp,
- names=names, logger=logger, domainsid=domainsid,
- schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
- slapd_path=slapd_path,
- root=root)
+ lp=lp,
+ names=names, logger=logger, domainsid=domainsid,
+ schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
+ slapd_path=slapd_path,
+ root=root)
elif backend_type == "openldap":
provision_backend = OpenLDAPBackend(backend_type, paths=paths,
- lp=lp,
- names=names, logger=logger, domainsid=domainsid,
- schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
- slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
- ldap_backend_extra_port=ldap_backend_extra_port,
- ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
- ldap_backend_forced_uri=ldap_backend_forced_uri)
+ lp=lp,
+ names=names, logger=logger, domainsid=domainsid,
+ schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
+ slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
+ ldap_backend_extra_port=ldap_backend_extra_port,
+ ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
+ ldap_backend_forced_uri=ldap_backend_forced_uri)
else:
raise ValueError("Unknown LDAP backend type selected")
logger.info("Setting up secrets.ldb")
secrets_ldb = setup_secretsdb(paths,
- session_info=session_info,
- backend_credentials=provision_backend.credentials, lp=lp)
+ session_info=session_info,
+ backend_credentials=provision_backend.credentials, lp=lp)
try:
logger.info("Setting up the registry")
samdb = setup_samdb(paths.samdb, session_info,
provision_backend, lp, names, logger=logger,
serverrole=serverrole,
- schema=schema, fill=samdb_fill, am_rodc=am_rodc)
+ schema=schema, fill=samdb_fill, am_rodc=am_rodc,
+ plaintext_secrets=plaintext_secrets,
+ backend_store=backend_store)
if serverrole == "active directory domain controller":
if paths.netlogon is None:
raise MissingShareError("sysvol", paths.smbconf)
if not os.path.isdir(paths.netlogon):
- os.makedirs(paths.netlogon, 0755)
+ os.makedirs(paths.netlogon, 0o755)
if adminpass is None:
adminpass = samba.generate_random_password(12, 32)
if samdb_fill == FILL_FULL:
provision_fill(samdb, secrets_ldb, logger, names, paths,
- schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
- hostip=hostip, hostip6=hostip6, domainsid=domainsid,
- next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
- krbtgtpass=krbtgtpass, domainguid=domainguid,
- policyguid=policyguid, policyguid_dc=policyguid_dc,
- invocationid=invocationid, machinepass=machinepass,
- ntdsguid=ntdsguid, dns_backend=dns_backend,
- dnspass=dnspass, serverrole=serverrole,
- dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
- lp=lp, use_ntvfs=use_ntvfs,
- skip_sysvolacl=skip_sysvolacl)
+ schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
+ hostip=hostip, hostip6=hostip6,
+ next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
+ krbtgtpass=krbtgtpass,
+ policyguid=policyguid, policyguid_dc=policyguid_dc,
+ invocationid=invocationid, machinepass=machinepass,
+ ntdsguid=ntdsguid, dns_backend=dns_backend,
+ dnspass=dnspass, serverrole=serverrole,
+ dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
+ lp=lp, use_ntvfs=use_ntvfs,
+ skip_sysvolacl=skip_sysvolacl,
+ backend_store=backend_store)
+
+ if not is_heimdal_built():
+ create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
+ logger.info("The Kerberos KDC configuration for Samba AD is "
+ "located at %s", paths.kdcconf)
create_krb5_conf(paths.krb5conf,
dnsdomain=names.dnsdomain, hostname=names.hostname,
realm=names.realm)
- logger.info("A Kerberos configuration suitable for Samba 4 has been "
+ logger.info("A Kerberos configuration suitable for Samba AD has been "
"generated at %s", paths.krb5conf)
+ logger.info("Merge the contents of this file with your system "
+ "krb5.conf or replace it with this one. Do not create a "
+ "symlink!")
if serverrole == "active directory domain controller":
create_dns_update_list(lp, logger, paths)
# Now commit the secrets.ldb to disk
secrets_ldb.transaction_commit()
- # the commit creates the dns.keytab, now chown it
- dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
- if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
+ # the commit creates the dns.keytab in the private directory
+ private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
+ bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
+
+ if os.path.isfile(private_dns_keytab_path):
+ if os.path.isfile(bind_dns_keytab_path):
+ try:
+ os.unlink(bind_dns_keytab_path)
+ except OSError as e:
+ logger.error("Failed to remove %s: %s" %
+ (bind_dns_keytab_path, e.strerror))
+
+ # link the dns.keytab to the bind-dns directory
try:
- os.chmod(dns_keytab_path, 0640)
- os.chown(dns_keytab_path, -1, paths.bind_gid)
- except OSError:
- if not os.environ.has_key('SAMBA_SELFTEST'):
- logger.info("Failed to chown %s to bind gid %u",
- dns_keytab_path, paths.bind_gid)
+ os.link(private_dns_keytab_path, bind_dns_keytab_path)
+ except OSError as e:
+ logger.error("Failed to create link %s -> %s: %s" %
+ (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
+
+ # chown the dns.keytab in the bind-dns directory
+ if paths.bind_gid is not None:
+ try:
+ os.chmod(paths.binddns_dir, 0o770)
+ os.chown(paths.binddns_dir, -1, paths.bind_gid)
+ except OSError:
+ if 'SAMBA_SELFTEST' not in os.environ:
+ logger.info("Failed to chown %s to bind gid %u",
+ paths.binddns_dir, paths.bind_gid)
+
+ try:
+ os.chmod(bind_dns_keytab_path, 0o640)
+ os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
+ except OSError:
+ if 'SAMBA_SELFTEST' not in os.environ:
+ logger.info("Failed to chown %s to bind gid %u",
+ bind_dns_keytab_path, paths.bind_gid)
result = ProvisionResult()
result.server_role = serverrole
if use_rfc2307:
provision_fake_ypserver(logger=logger, samdb=samdb,
- domaindn=names.domaindn, netbiosname=names.netbiosname,
- nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
+ domaindn=names.domaindn, netbiosname=names.netbiosname,
+ nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
return result
def provision_become_dc(smbconf=None, targetdir=None,
- realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
- serverdn=None, domain=None, hostname=None, domainsid=None,
- adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
- policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
- dns_backend=None, root=None, nobody=None, users=None,
- backup=None, serverrole=None, ldap_backend=None,
- ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
+ realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
+ serverdn=None, domain=None, hostname=None, domainsid=None,
+ adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
+ policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
+ dns_backend=None, root=None, nobody=None, users=None,
+ backup=None, serverrole=None, ldap_backend=None,
+ ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
logger = logging.getLogger("provision")
samba.set_debug_level(debuglevel)
res = provision(logger, system_session(),
- smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
- realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
- configdn=configdn, serverdn=serverdn, domain=domain,
- hostname=hostname, hostip=None, domainsid=domainsid,
- machinepass=machinepass,
- serverrole="active directory domain controller",
- sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
- use_ntvfs=use_ntvfs)
+ smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
+ realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
+ configdn=configdn, serverdn=serverdn, domain=domain,
+ hostname=hostname, hostip=None, domainsid=domainsid,
+ machinepass=machinepass,
+ serverrole="active directory domain controller",
+ sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
+ use_ntvfs=use_ntvfs)
res.lp.set("debuglevel", str(debuglevel))
return res
"DNSDOMAIN": dnsdomain,
"HOSTNAME": hostname,
"REALM": realm,
- })
+ })
class ProvisioningError(Exception):