1
0
mirror of https://github.com/samba-team/samba.git synced 2025-07-29 15:42:04 +03:00

provision: Changes to support encrypted_secrets module

Changes to provision and join to create a database with
encrypted_secrets enabled and a key file generated.

Also adds the --plaintext-secrets option to join and provision commands
to allow the creation of unencrypted databases.

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Gary Lockyer
2017-12-15 07:24:14 +13:00
committed by Andrew Bartlett
parent 1d3ae2d92f
commit d120d7fe84
6 changed files with 102 additions and 31 deletions

View File

@ -55,7 +55,8 @@ class dc_join(object):
def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
netbios_name=None, targetdir=None, domain=None,
machinepass=None, use_ntvfs=False, dns_backend=None,
promote_existing=False, clone_only=False):
promote_existing=False, clone_only=False,
plaintext_secrets=False):
if site is None:
site = "Default-First-Site-Name"
@ -67,6 +68,7 @@ class dc_join(object):
ctx.site = site
ctx.targetdir = targetdir
ctx.use_ntvfs = use_ntvfs
ctx.plaintext_secrets = plaintext_secrets
ctx.promote_existing = promote_existing
ctx.promote_from_dn = None
@ -837,7 +839,8 @@ class dc_join(object):
hostname=ctx.myname, domainsid=ctx.domsid,
machinepass=ctx.acct_pass, serverrole="active directory domain controller",
sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend)
use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend,
plaintext_secrets=ctx.plaintext_secrets)
print "Provision OK for domain DN %s" % presult.domaindn
ctx.local_samdb = presult.samdb
ctx.lp = presult.lp
@ -1398,11 +1401,12 @@ class dc_join(object):
def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
targetdir=None, domain=None, domain_critical_only=False,
machinepass=None, use_ntvfs=False, dns_backend=None,
promote_existing=False):
promote_existing=False, plaintext_secrets=False):
"""Join as a RODC."""
ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, domain,
machinepass, use_ntvfs, dns_backend, promote_existing)
machinepass, use_ntvfs, dns_backend, promote_existing,
plaintext_secrets)
lp.set("workgroup", ctx.domain_name)
logger.info("workgroup is %s" % ctx.domain_name)
@ -1449,10 +1453,11 @@ def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_
def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
targetdir=None, domain=None, domain_critical_only=False,
machinepass=None, use_ntvfs=False, dns_backend=None,
promote_existing=False):
promote_existing=False, plaintext_secrets=False):
"""Join as a DC."""
ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, domain,
machinepass, use_ntvfs, dns_backend, promote_existing)
machinepass, use_ntvfs, dns_backend, promote_existing,
plaintext_secrets)
lp.set("workgroup", ctx.domain_name)
logger.info("workgroup is %s" % ctx.domain_name)
@ -1498,10 +1503,10 @@ def join_clone(logger=None, server=None, creds=None, lp=None,
def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None,
netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False,
dns_backend=None):
dns_backend=None, plaintext_secrets=False):
"""Join as a DC."""
ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, parent_domain,
machinepass, use_ntvfs, dns_backend)
machinepass, use_ntvfs, dns_backend, plaintext_secrets)
ctx.subdomain = True
if adminpass is None:
ctx.adminpass = samba.generate_random_password(12, 32)

View File

@ -247,6 +247,9 @@ class cmd_domain_provision(Command):
Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
Option("--plaintext-secrets", action="store_true",
help="Store secret/sensitive values as plain text on disk" +
"(default is to encrypt secret/ensitive values)"),
]
openldap_options = [
@ -316,7 +319,8 @@ class cmd_domain_provision(Command):
ldap_backend_extra_port=None,
ldap_backend_forced_uri=None,
ldap_dryrun_mode=None,
base_schema=None):
base_schema=None,
plaintext_secrets=False):
self.logger = self.get_logger("provision")
if quiet:
@ -485,7 +489,8 @@ class cmd_domain_provision(Command):
ldap_backend_extra_port=ldap_backend_extra_port,
ldap_backend_forced_uri=ldap_backend_forced_uri,
nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
base_schema=base_schema)
base_schema=base_schema,
plaintext_secrets=plaintext_secrets)
except ProvisioningError, e:
raise CommandError("Provision failed", e)
@ -638,6 +643,9 @@ class cmd_domain_join(Command):
"BIND9_DLZ uses samba4 AD to store zone information, "
"NONE skips the DNS setup entirely (this DC will not be a DNS server)",
default="SAMBA_INTERNAL"),
Option("--plaintext-secrets", action="store_true",
help="Store secret/sensitive values as plain text on disk" +
"(default is to encrypt secret/ensitive values)"),
Option("--quiet", help="Be quiet", action="store_true"),
Option("--verbose", help="Be verbose", action="store_true")
]
@ -655,7 +663,7 @@ class cmd_domain_join(Command):
versionopts=None, server=None, site=None, targetdir=None,
domain_critical_only=False, parent_domain=None, machinepass=None,
use_ntvfs=False, dns_backend=None, adminpass=None,
quiet=False, verbose=False):
quiet=False, verbose=False, plaintext_secrets=False):
lp = sambaopts.get_loadparm()
creds = credopts.get_credentials(lp)
net = Net(creds, lp, server=credopts.ipaddress)
@ -686,13 +694,16 @@ class cmd_domain_join(Command):
join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
site=site, netbios_name=netbios_name, targetdir=targetdir,
domain_critical_only=domain_critical_only,
machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
machinepass=machinepass, use_ntvfs=use_ntvfs,
dns_backend=dns_backend,
plaintext_secrets=plaintext_secrets)
elif role == "RODC":
join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
site=site, netbios_name=netbios_name, targetdir=targetdir,
domain_critical_only=domain_critical_only,
machinepass=machinepass, use_ntvfs=use_ntvfs,
dns_backend=dns_backend)
dns_backend=dns_backend,
plaintext_secrets=plaintext_secrets)
elif role == "SUBDOMAIN":
if not adminpass:
logger.info("Administrator password will be set randomly!")
@ -705,7 +716,8 @@ class cmd_domain_join(Command):
netbios_name=netbios_name, netbios_domain=netbios_domain,
targetdir=targetdir, machinepass=machinepass,
use_ntvfs=use_ntvfs, dns_backend=dns_backend,
adminpass=adminpass)
adminpass=adminpass,
plaintext_secrets=plaintext_secrets)
else:
raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)

View File

@ -29,6 +29,7 @@ __docformat__ = "restructuredText"
from base64 import b64encode
import errno
import os
import stat
import re
import pwd
import grp
@ -554,6 +555,9 @@ def provision_paths_from_lp(lp, dnsdomain):
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")
@ -792,7 +796,7 @@ def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
def setup_samdb_partitions(samdb_path, logger, lp, session_info,
provision_backend, names, serverrole,
erase=False):
erase=False, plaintext_secrets=False):
"""Setup the partitions for the SAM database.
Alternatively, provision() may call this, and then populate the database.
@ -821,6 +825,10 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info,
if provision_backend.type != "ldb":
ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
required_features = "# No required features"
if not plaintext_secrets:
required_features = "requiredFeatures: encryptedSecrets"
samdb.transaction_start()
try:
logger.info("Setting up sam.ldb partitions and settings")
@ -831,7 +839,8 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info,
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")
@ -1006,6 +1015,30 @@ def setup_privileges(path, session_info, lp):
privilege_ldb.erase()
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.
@ -1193,7 +1226,8 @@ def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
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):
"""Setup a complete SAM Database.
:note: This will wipe the main SAM database file!
@ -1202,7 +1236,7 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
# Also wipes the database
setup_samdb_partitions(path, logger=logger, lp=lp,
provision_backend=provision_backend, session_info=session_info,
names=names, serverrole=serverrole)
names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets)
# Load the database, but don's load the global schema and don't connect
# quite yet
@ -1975,7 +2009,8 @@ def provision(logger, session_info, smbconf=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):
ldap_backend_extra_port=None, base_schema=None,
plaintext_secrets=False):
"""Provision samba4
:note: caution, this wipes all existing data!
@ -2101,6 +2136,8 @@ def provision(logger, session_info, smbconf=None,
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)
@ -2172,7 +2209,8 @@ def provision(logger, session_info, smbconf=None,
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)
if serverrole == "active directory domain controller":
if paths.netlogon is None:

View File

@ -4,9 +4,5 @@
# * --plaintext-secrets option correctly provisions a domain
# * the dsdb operational module correctly handles unencrypted secrets
# * secrets are not stored as encrypted text when this option is specified
^samba.tests.encrypted_secrets.samba.tests.encrypted_secrets.EncryptedSecretsTests.test_encrypted_secrets\(fl2000dc:local\)
^samba.tests.encrypted_secrets.samba.tests.encrypted_secrets.EncryptedSecretsTests.test_required_features\(fl2000dc:local\)
# Tests that will pass as the remaining patches in the set are added
^samba.tests.encrypted_secrets.samba.tests.encrypted_secrets.EncryptedSecretsTests.test_encrypted_secrets
^samba.tests.encrypted_secrets.samba.tests.encrypted_secrets.EncryptedSecretsTests.test_required_features
#^samba.tests.encrypted_secrets.samba.tests.encrypted_secrets.EncryptedSecretsTests.test_encrypted_secrets\(fl2000dc:local\)
#^samba.tests.encrypted_secrets.samba.tests.encrypted_secrets.EncryptedSecretsTests.test_required_features\(fl2000dc:local\)

View File

@ -228,6 +228,21 @@ static int set_ldap_credentials(struct ldb_context *ldb, bool use_external)
return LDB_SUCCESS;
}
static bool check_required_features(struct ldb_message_element *el)
{
if (el != NULL) {
int k;
DATA_BLOB esf = data_blob_string_const(
SAMBA_ENCRYPTED_SECRETS_FEATURE);
for (k = 0; k < el->num_values; k++) {
if (data_blob_cmp(&esf, &el->values[k]) != 0) {
return false;
}
}
}
return true;
}
static int samba_dsdb_init(struct ldb_module *module)
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
@ -294,6 +309,7 @@ static int samba_dsdb_init(struct ldb_module *module)
"rdn_name",
"subtree_delete",
"repl_meta_data",
"encrypted_secrets",
"operational",
"unique_object_sids",
"subtree_rename",
@ -375,11 +391,14 @@ static int samba_dsdb_init(struct ldb_module *module)
backendType = ldb_msg_find_attr_as_string(res->msgs[0], "backendType", "ldb");
requiredFeatures = ldb_msg_find_element(res->msgs[0], SAMBA_REQUIRED_FEATURES_ATTR);
if (requiredFeatures != NULL) {
ldb_set_errstring(ldb, "This Samba database was created with "
"a newer Samba version and is marked with "
"requiredFeatures in @SAMBA_DSDB. "
"This database can not safely be read by this Samba version");
if (!check_required_features(requiredFeatures)) {
ldb_set_errstring(
ldb,
"This Samba database was created with "
"a newer Samba version and is marked "
"with extra requiredFeatures in "
"@SAMBA_DSDB. This database can not "
"safely be read by this Samba version");
return LDB_ERR_OPERATIONS_ERROR;
}

View File

@ -28,6 +28,7 @@ dn: @SAMBA_DSDB
backendType: ${BACKEND_TYPE}
serverRole: ${SERVER_ROLE}
compatibleFeatures: sortedLinks
${REQUIRED_FEATURES}
dn: @MODULES
@LIST: samba_dsdb