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"
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
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
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)
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)
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)
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 = [
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:
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)
"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")
]
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)
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!")
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)
from base64 import b64encode
import errno
import os
+import stat
import re
import pwd
import grp
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")
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.
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")
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")
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.
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!
# 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
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!
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)
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:
# * --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\)
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);
"rdn_name",
"subtree_delete",
"repl_meta_data",
+ "encrypted_secrets",
"operational",
"unique_object_sids",
"subtree_rename",
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;
}
backendType: ${BACKEND_TYPE}
serverRole: ${SERVER_ROLE}
compatibleFeatures: sortedLinks
+${REQUIRED_FEATURES}
dn: @MODULES
@LIST: samba_dsdb