diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index 963d8822e6c..d2dd06a3d48 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -43,7 +43,7 @@ from samba.net import Net, LIBNET_JOIN_AUTOMATIC import samba.ntacls from samba.join import join_RODC, join_DC, join_subdomain from samba.auth import system_session -from samba.samdb import SamDB +from samba.samdb import SamDB, get_default_backend_store from samba.ndr import ndr_unpack, ndr_pack, ndr_print from samba.dcerpc import drsuapi from samba.dcerpc import drsblobs @@ -258,6 +258,10 @@ class cmd_domain_provision(Command): Option("--plaintext-secrets", action="store_true", help="Store secret/sensitive values as plain text on disk" + "(default is to encrypt secret/ensitive values)"), + Option("--backend-store", type="choice", metavar="BACKENDSTORE", + choices=["tdb", "mdb"], + help="Specify the database backend to be used " + "(default is %s)" % get_default_backend_store()), ] openldap_options = [ @@ -328,7 +332,8 @@ class cmd_domain_provision(Command): ldap_backend_forced_uri=None, ldap_dryrun_mode=None, base_schema=None, - plaintext_secrets=False): + plaintext_secrets=False, + backend_store=None): self.logger = self.get_logger("provision") if quiet: @@ -476,6 +481,8 @@ class cmd_domain_provision(Command): domain_sid = security.dom_sid(domain_sid) session = system_session() + if backend_store is None: + backend_store = get_default_backend_store() try: result = provision(self.logger, session, smbconf=smbconf, targetdir=targetdir, @@ -498,7 +505,8 @@ class cmd_domain_provision(Command): ldap_backend_forced_uri=ldap_backend_forced_uri, nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode, base_schema=base_schema, - plaintext_secrets=plaintext_secrets) + plaintext_secrets=plaintext_secrets, + backend_store=backend_store) except ProvisioningError as e: raise CommandError("Provision failed", e) diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py index f36f277742e..aaf839cc3f8 100644 --- a/python/samba/provision/__init__.py +++ b/python/samba/provision/__init__.py @@ -123,6 +123,7 @@ 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" @@ -814,7 +815,8 @@ 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, plaintext_secrets=False): + 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. @@ -847,11 +849,16 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info, 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 + 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 }) @@ -1245,7 +1252,7 @@ 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, - plaintext_secrets=False): + plaintext_secrets=False, backend_store=None): """Setup a complete SAM Database. :note: This will wipe the main SAM database file! @@ -1254,7 +1261,8 @@ 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, plaintext_secrets=plaintext_secrets) + 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 @@ -1293,7 +1301,8 @@ def setup_samdb(path, session_info, provision_backend, lp, names, 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): + dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None, + backend_store=None): if next_rid is None: next_rid = 1000 @@ -1831,7 +1840,8 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, 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" @@ -1863,7 +1873,8 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, 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) + next_rid=next_rid, dc_rid=dc_rid, + backend_store=backend_store) # Set up group policies (domain policy and domain controller # policy) @@ -1913,7 +1924,8 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, 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, fill_level=samdb_fill) + targetdir=targetdir, fill_level=samdb_fill, + backend_store=backend_store) domainguid = samdb.searchone(basedn=samdb.get_default_basedn(), attribute="objectGUID") @@ -2033,7 +2045,7 @@ def provision(logger, session_info, smbconf=None, 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): + plaintext_secrets=False, backend_store=None): """Provision samba4 :note: caution, this wipes all existing data! @@ -2050,6 +2062,8 @@ def provision(logger, session_info, smbconf=None, 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() @@ -2233,7 +2247,8 @@ def provision(logger, session_info, smbconf=None, provision_backend, lp, names, logger=logger, serverrole=serverrole, schema=schema, fill=samdb_fill, am_rodc=am_rodc, - plaintext_secrets=plaintext_secrets) + plaintext_secrets=plaintext_secrets, + backend_store=backend_store) if serverrole == "active directory domain controller": if paths.netlogon is None: @@ -2264,7 +2279,8 @@ def provision(logger, session_info, smbconf=None, 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) + 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"))) diff --git a/python/samba/provision/sambadns.py b/python/samba/provision/sambadns.py index 4cc15b06700..ce1b7692ff9 100644 --- a/python/samba/provision/sambadns.py +++ b/python/samba/provision/sambadns.py @@ -29,6 +29,7 @@ from base64 import b64encode import subprocess import samba from samba.tdb_util import tdb_copy +from samba.mdb_util import mdb_copy from samba.ndr import ndr_pack, ndr_unpack from samba import setup_file from samba.dcerpc import dnsp, misc, security @@ -58,6 +59,7 @@ from samba.provision.common import ( FILL_DRS, ) +from samba.samdb import get_default_backend_store def get_domainguid(samdb, domaindn): res = samdb.search(base=domaindn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) @@ -787,12 +789,19 @@ def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid): # Find the partitions and corresponding filenames partfile = {} - res = samdb.search(base="@PARTITION", scope=ldb.SCOPE_BASE, attrs=["partition"]) + res = samdb.search(base="@PARTITION", + scope=ldb.SCOPE_BASE, + attrs=["partition", "backendStore"]) for tmp in res[0]["partition"]: (nc, fname) = tmp.split(':') partfile[nc.upper()] = fname + backend_store = get_default_backend_store() + if "backendStore" in res[0]: + backend_store = res[0]["backendStore"][0] + # Create empty domain partition + domaindn = names.domaindn.upper() domainpart_file = os.path.join(dns_dir, partfile[domaindn]) try: @@ -800,7 +809,8 @@ def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid): file(domainpart_file, 'w').close() # Fill the basedn and @OPTION records in domain partition - dom_ldb = samba.Ldb(domainpart_file) + dom_url = "%s://%s" % (backend_store, domainpart_file) + dom_ldb = samba.Ldb(dom_url) domainguid_line = "objectGUID: %s\n-" % domainguid descr = b64encode(get_domain_descriptor(domainsid)) setup_add_ldif(dom_ldb, setup_path("provision_basedn.ldif"), { @@ -859,8 +869,12 @@ def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid): os.path.join(dns_dir, "sam.ldb")) for nc in partfile: pfile = partfile[nc] - tdb_copy(os.path.join(private_dir, pfile), - os.path.join(dns_dir, pfile)) + if backend_store == "mdb": + mdb_copy(os.path.join(private_dir, pfile), + os.path.join(dns_dir, pfile)) + else: + tdb_copy(os.path.join(private_dir, pfile), + os.path.join(dns_dir, pfile)) except: logger.error( "Failed to setup database for BIND, AD based DNS cannot be used") @@ -875,7 +889,7 @@ def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid): os.chown(dpath, -1, paths.bind_gid) os.chmod(dpath, 0o770) for f in files: - if f.endswith('.ldb') or f.endswith('.tdb'): + if f.endswith(('.ldb', '.tdb', 'ldb-lock')): fpath = os.path.join(dirname, f) os.chown(fpath, -1, paths.bind_gid) os.chmod(fpath, 0o660) @@ -1069,7 +1083,7 @@ def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn, def setup_ad_dns(samdb, secretsdb, names, paths, lp, logger, dns_backend, os_level, dnspass=None, hostip=None, hostip6=None, - targetdir=None, fill_level=FILL_FULL): + targetdir=None, fill_level=FILL_FULL, backend_store=None): """Provision DNS information (assuming GC role) :param samdb: LDB object connected to sam.ldb file @@ -1164,12 +1178,14 @@ def setup_ad_dns(samdb, secretsdb, names, paths, lp, logger, if dns_backend.startswith("BIND9_"): setup_bind9_dns(samdb, secretsdb, names, paths, lp, logger, dns_backend, os_level, site=site, dnspass=dnspass, hostip=hostip, - hostip6=hostip6, targetdir=targetdir) + hostip6=hostip6, targetdir=targetdir, + backend_store=backend_store) def setup_bind9_dns(samdb, secretsdb, names, paths, lp, logger, dns_backend, os_level, site=None, dnspass=None, hostip=None, - hostip6=None, targetdir=None, key_version_number=None): + hostip6=None, targetdir=None, key_version_number=None, + backend_store=None): """Provision DNS information (assuming BIND9 backend in DC role) :param samdb: LDB object connected to sam.ldb file @@ -1216,7 +1232,8 @@ def setup_bind9_dns(samdb, secretsdb, names, paths, lp, logger, ntdsguid=names.ntdsguid) if dns_backend == "BIND9_DLZ" and os_level >= DS_DOMAIN_FUNCTION_2003: - create_samdb_copy(samdb, logger, paths, names, names.domainsid, domainguid) + create_samdb_copy(samdb, logger, paths, + names, names.domainsid, domainguid) create_named_conf(paths, realm=names.realm, dnsdomain=names.dnsdomain, dns_backend=dns_backend, diff --git a/python/samba/samdb.py b/python/samba/samdb.py index 348bd212256..d7faa236f88 100644 --- a/python/samba/samdb.py +++ b/python/samba/samdb.py @@ -37,6 +37,9 @@ from samba.dcerpc import security __docformat__ = "restructuredText" +def get_default_backend_store(): + return "tdb" + class SamDB(samba.Ldb): """The SAM database.""" diff --git a/source4/setup/provision_partitions.ldif b/source4/setup/provision_partitions.ldif index 728f32739d4..4cd57943228 100644 --- a/source4/setup/provision_partitions.ldif +++ b/source4/setup/provision_partitions.ldif @@ -2,5 +2,6 @@ dn: @PARTITION replicateEntries: @ATTRIBUTES replicateEntries: @INDEXLIST replicateEntries: @OPTIONS +${BACKEND_STORE} ${LDAP_BACKEND_LINE}