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:
committed by
Andrew Bartlett
parent
1d3ae2d92f
commit
d120d7fe84
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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\)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ dn: @SAMBA_DSDB
|
||||
backendType: ${BACKEND_TYPE}
|
||||
serverRole: ${SERVER_ROLE}
|
||||
compatibleFeatures: sortedLinks
|
||||
${REQUIRED_FEATURES}
|
||||
|
||||
dn: @MODULES
|
||||
@LIST: samba_dsdb
|
||||
|
Reference in New Issue
Block a user