The branch, v4-13-test has been updated via b9b1d98af4c smbXsrv_{open,session,tcon}: protect smbXsrv_{open,session,tcon}_global_traverse_fn against invalid records via 7065f203a9f gensec_krb5: restore ipv6 support for kpasswd via 82e0f3e7997 netcmd: Use next_free_rid() function to calculate a SID for restoring a backup via e5c3a675464 python/tests/dsdb: Add tests for RID allocation functions via afad2fd9e24 dsdb: Add next_free_rid() function to allocate a RID without modifying the database via b3d59842fd9 netcmd: Add tests for performing an offline backup immediately after joining a domain via 00444ac64f5 netcmd: Ignore rIDUsedPool attribute in offline domain backup test via 445fb770c77 netcmd: Fix error-checking condition via 303a0ecdd9d netcmd: Avoid database corruption by opting not to create database files during an offline domain backup via 54c353e9ad6 netcmd: Determine which files are to be copied for an offline domain backup via 4a68b1cb2dc netcmd: Add test for an offline backup of nested directories via 6569d0b9967 netcmd: Add test for an offline backup of a directory containing hardlinks via d0bde5703b2 samba-tool: Give better error information when the 'domain backup restore' fails with a duplicate SID via 6e284db7877 samba-tool domain backup: Confirm the sidForRestore we will put into the backup is free from b01c4526fef s3: smbd: Fix uninitialized memory read in process_symlink_open() when used with vfs_shadow_copy2().
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-13-test - Log ----------------------------------------------------------------- commit b9b1d98af4c7cd2326e12e1c3b734056663932d1 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jul 5 17:17:30 2021 +0200 smbXsrv_{open,session,tcon}: protect smbXsrv_{open,session,tcon}_global_traverse_fn against invalid records I saw systems with locking.tdb records being part of: ctdb catdb smbXsrv_tcon_global.tdb It's yet unknown how that happened, but we should not panic in srvsvc_* calls because the info0 pointer was NULL. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14752 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Tue Jul 6 11:08:43 UTC 2021 on sn-devel-184 (cherry picked from commit 00bab5b3c821f272153a25ded9743460887a7907) Autobuild-User(v4-13-test): Karolin Seeger <ksee...@samba.org> Autobuild-Date(v4-13-test): Tue Jul 13 13:18:20 UTC 2021 on sn-devel-184 commit 7065f203a9fa0618e9a72043ec925eee7c7cdd01 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Jul 2 09:37:25 2021 +0200 gensec_krb5: restore ipv6 support for kpasswd We need to offer as much space we have in order to get the address out of tsocket_address_bsd_sockaddr(). This fixes a regression in commit 43c808f2ff907497dfff0988ff90a48fdcfc16ef. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14750 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> (cherry picked from commit 0388a8f33bdde49f1cc805a0291859203c1a52b4) commit 82e0f3e79975ffdffd5afca77b6458a33488eff7 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Thu May 27 15:35:35 2021 +1200 netcmd: Use next_free_rid() function to calculate a SID for restoring a backup This means we won't get errors if the DC doesn't have a rIDNextRID attribute, but we will still error if there is no RID Set or if all its pools are exhausted. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14669 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> (cherry picked from commit 59d293b60608172ae61551c642d13d3b215924e4) commit e5c3a675464208bffad08a0e923406c9a2d4b0a5 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Mon May 24 16:46:28 2021 +1200 python/tests/dsdb: Add tests for RID allocation functions BUG: https://bugzilla.samba.org/show_bug.cgi?id=14669 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> (cherry picked from commit 7c7cad81844950c3efe9a540a47b9d4e1ce1b2a1) commit afad2fd9e2499f6ddacae9ddace22c81e9de7da0 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Mon May 24 12:59:59 2021 +1200 dsdb: Add next_free_rid() function to allocate a RID without modifying the database If used to generate SIDs for objects, care should be taken, as the possibility for having duplicate objectSIDs can arise. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14669 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> (cherry picked from commit cc98e03e7a0f2bf7a1ace2950fe6500f53640c1b) commit b3d59842fd99c8d72dbc6f65259efad05bd5d897 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Mon May 24 14:58:40 2021 +1200 netcmd: Add tests for performing an offline backup immediately after joining a domain This currently fails due to the DC not having a rIDNextRID attribute, which is required for the restore process. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14669 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> (cherry picked from commit b7e6a1c5da7283c49586dc29f85ab19e0e57b0f6) commit 00444ac64f5b9ece88a62100b9b98c6fe75f6a60 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Wed May 26 13:40:30 2021 +1200 netcmd: Ignore rIDUsedPool attribute in offline domain backup test The RID Set of the newly created DC account has all its values initialised to zero. If the rIDUsedPool attribute was previously non-zero, then the restore process will cause its value to change. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14669 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> (cherry picked from commit 658e5a6cc20b57f48477affd370fe25458178b92) commit 445fb770c77b488c3ff7e426d985206967f5d825 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Mon May 24 16:40:55 2021 +1200 netcmd: Fix error-checking condition This condition probably meant to check the argument of the most recently thrown exception, rather than the previous one again. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14669 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> (cherry picked from commit e8c242bed19432d96e78dc345ab5f06422c5b104) commit 303a0ecdd9d6c3072824380b4faa88cb0821f117 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Mar 16 22:20:21 2021 +1300 netcmd: Avoid database corruption by opting not to create database files during an offline domain backup If backup dirs contain hardlinks, the backup process could previously attempt to open an LMDB database already opened during the backup, causing it to be recreated as a new TDB database. This commit ensures that new database files are not created during this operation, and that the main SamDB database is not modified. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14027 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> (cherry picked from commit 4cf773591d49166b8c7ef8d637d7edfe755d48aa) commit 54c353e9ad65a8a764963bddd6396dab4779bfe3 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Mar 16 16:22:40 2021 +1300 netcmd: Determine which files are to be copied for an offline domain backup The old behaviour attempted to check for and remove files with duplicate names, but did not do so due to a bug, and would have left undetermined which files were given priority when duplicate filenames were present. Now when hardlinks are present, only one instance of each file is chosen, with files in the private directory having priority. If one backup dir is nested inside another, the files contained in the nested directory are only added once. Additionally, the BIND DNS database is omitted from the backup. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14027 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> (cherry picked from commit 3723148e7aa7e6d4a48a1a38112f121f52b6ee6f) commit 4a68b1cb2dc9b32f49423b96c1fd935f5886d057 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Thu Mar 18 10:52:52 2021 +1300 netcmd: Add test for an offline backup of nested directories This test verifies that when performing an offline backup of a domain where one of the directories to be backed up is nested inside another, the contained files are only included once in the backup. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14027 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> (cherry picked from commit f994783f4279884ec4d2ee3e7db80fb7af267d1c) commit 6569d0b99672e8752eb13704056094a729d929c5 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Mar 16 16:13:05 2021 +1300 netcmd: Add test for an offline backup of a directory containing hardlinks This test verifies that when performing an offline backup of a domain where the directories to be backed up contain hardlinks, only one instance of each file is backed up, and that files in the private directory take precedence. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14027 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> (cherry picked from commit 0e5738887524b467bfebcf657bcb00ed71827784) commit d0bde5703b25fb34a0b672422c052909b1acbbef Author: Andrew Bartlett <abart...@samba.org> Date: Fri Nov 13 15:26:07 2020 +1300 samba-tool: Give better error information when the 'domain backup restore' fails with a duplicate SID BUG: https://bugzilla.samba.org/show_bug.cgi?id=14575 Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> Autobuild-User(master): Gary Lockyer <g...@samba.org> Autobuild-Date(master): Thu Nov 26 21:15:40 UTC 2020 on sn-devel-184 (cherry picked from commit 8ad82ae66157c893a2b84d353ec4d9feb4815ede) commit 6e284db78779aae20b49300ae304c5b643f31b7d Author: Andrew Bartlett <abart...@samba.org> Date: Wed Nov 18 12:11:10 2020 +1300 samba-tool domain backup: Confirm the sidForRestore we will put into the backup is free Otherwise the administrator might only find there is a problem once they attempt to restore the domain! BUG: https://bugzilla.samba.org/show_bug.cgi?id=14575 Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> (cherry picked from commit 15609cb91986b3e29c5b1a3b6c69c04829f43eb4) ----------------------------------------------------------------------- Summary of changes: python/samba/netcmd/domain_backup.py | 173 +++++++++++----- python/samba/samdb.py | 105 ++++++++++ python/samba/tests/domain_backup_offline.py | 162 ++++++++++++--- python/samba/tests/dsdb.py | 305 +++++++++++++++++++++++++++- source3/smbd/smbXsrv_open.c | 9 + source3/smbd/smbXsrv_session.c | 7 + source3/smbd/smbXsrv_tcon.c | 7 + source4/auth/gensec/gensec_krb5.c | 6 +- source4/selftest/tests.py | 2 +- 9 files changed, 692 insertions(+), 84 deletions(-) Changeset truncated at 500 lines: diff --git a/python/samba/netcmd/domain_backup.py b/python/samba/netcmd/domain_backup.py index a3dc7fb454f..a629b31d70f 100644 --- a/python/samba/netcmd/domain_backup.py +++ b/python/samba/netcmd/domain_backup.py @@ -27,6 +27,7 @@ import tdb import samba.getopt as options from samba.samdb import SamDB, get_default_backend_store import ldb +from ldb import LdbError from samba.samba3 import libsmb_samba_internal as libsmb from samba.samba3 import param as s3param from samba.ntacls import backup_online, backup_restore, backup_offline @@ -60,53 +61,50 @@ from samba.ndr import ndr_pack # This ensures that the restored DC's SID won't clash with any other RIDs # already in use in the domain def get_sid_for_restore(samdb, logger): - # Find the DN of the RID set of the server - res = samdb.search(base=ldb.Dn(samdb, samdb.get_serverName()), - scope=ldb.SCOPE_BASE, attrs=["serverReference"]) - server_ref_dn = ldb.Dn(samdb, str(res[0]['serverReference'][0])) - res = samdb.search(base=server_ref_dn, - scope=ldb.SCOPE_BASE, - attrs=['rIDSetReferences']) - rid_set_dn = ldb.Dn(samdb, str(res[0]['rIDSetReferences'][0])) - - # Get the alloc pools and next RID of the RID set - res = samdb.search(base=rid_set_dn, - scope=ldb.SCOPE_SUBTREE, - expression="(rIDNextRID=*)", - attrs=['rIDAllocationPool', - 'rIDPreviousAllocationPool', - 'rIDNextRID']) - - # Decode the bounds of the RID allocation pools + # Allocate a new RID without modifying the database. This should be safe, + # because we acquire the RID master role after creating an account using + # this RID during the restore process. Acquiring the RID master role + # creates a new RID pool which we will fetch RIDs from, so we shouldn't get + # duplicates. try: - rid = int(res[0].get('rIDNextRID')[0]) - except IndexError: - logger.info("The RID pool for this DC is not initalized " - "(e.g. it may be a fairly new DC).") - logger.info("To initialize it, create a temporary user on this DC " - "(you can delete it later).") - raise CommandError("Cannot create backup - " - "please initialize this DC's RID pool first.") - - def split_val(num): - high = (0xFFFFFFFF00000000 & int(num)) >> 32 - low = 0x00000000FFFFFFFF & int(num) - return low, high - pool_l, pool_h = split_val(res[0].get('rIDPreviousAllocationPool')[0]) - npool_l, npool_h = split_val(res[0].get('rIDAllocationPool')[0]) - - # Calculate next RID based on pool bounds - if rid == npool_h: - raise CommandError('Out of RIDs, finished AllocPool') - if rid == pool_h: - if pool_h == npool_h: - raise CommandError('Out of RIDs, finished PrevAllocPool.') - rid = npool_l - else: - rid += 1 + rid = samdb.next_free_rid() + except LdbError as err: + logger.info("A SID could not be allocated for restoring the domain. " + "Either no RID Set was found on this DC, " + "or the RID Set was not usable.") + logger.info("To initialise this DC's RID pools, obtain a RID Set from " + "this domain's RID master, or run samba-tool dbcheck " + "to fix the existing RID Set.") + raise CommandError("Cannot create backup", err) # Construct full SID sid = dom_sid(samdb.get_domain_sid()) + sid_for_restore = str(sid) + '-' + str(rid) + + # Confirm the SID is not already in use + try: + res = samdb.search(scope=ldb.SCOPE_BASE, + base='<SID=%s>' % sid_for_restore, + attrs=[], + controls=['show_deleted:1', + 'show_recycled:1']) + if len(res) != 1: + # This case makes no sense, but neither does a corrupt RID set + raise CommandError("Cannot create backup - " + "this DC's RID pool is corrupt, " + "the next SID (%s) appears to be in use." % + sid_for_restore) + raise CommandError("Cannot create backup - " + "this DC's RID pool is corrupt, " + "the next SID %s points to existing object %s. " + "Please run samba-tool dbcheck on the source DC." % + (sid_for_restore, res[0].dn)) + except ldb.LdbError as e: + (enum, emsg) = e.args + if enum != ldb.ERR_NO_SUCH_OBJECT: + # We want NO_SUCH_OBJECT, anything else is a serious issue + raise + return str(sid) + '-' + str(rid) @@ -278,7 +276,8 @@ class cmd_domain_backup_online(samba.netcmd.Command): shutil.rmtree(paths.sysvol) # Edit the downloaded sam.ldb to mark it as a backup - samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp) + samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp, + flags=ldb.FLG_DONT_CREATE_DB) time_str = get_timestamp() add_backup_marker(samdb, "backupDate", time_str) add_backup_marker(samdb, "sidForRestore", new_sid) @@ -502,7 +501,8 @@ class cmd_domain_backup_restore(cmd_fsmo_seize): # open a DB connection to the restored DB private_dir = os.path.join(targetdir, 'private') samdb_path = os.path.join(private_dir, 'sam.ldb') - samdb = SamDB(url=samdb_path, session_info=system_session(), lp=lp) + samdb = SamDB(url=samdb_path, session_info=system_session(), lp=lp, + flags=ldb.FLG_DONT_CREATE_DB) backup_type = self.get_backup_type(samdb) if site is None: @@ -550,7 +550,50 @@ class cmd_domain_backup_restore(cmd_fsmo_seize): attrs=['sidForRestore']) sid = res[0].get('sidForRestore')[0] logger.info('Creating account with SID: ' + str(sid)) - ctx.join_add_objects(specified_sid=dom_sid(str(sid))) + try: + ctx.join_add_objects(specified_sid=dom_sid(str(sid))) + except LdbError as e: + (enum, emsg) = e.args + if enum != ldb.ERR_CONSTRAINT_VIOLATION: + raise + + dup_res = [] + try: + dup_res = samdb.search(base=ldb.Dn(samdb, "<SID=%s>" % sid), + scope=ldb.SCOPE_BASE, + attrs=['objectGUID'], + controls=["show_deleted:0", + "show_recycled:0"]) + except LdbError as dup_e: + (dup_enum, _) = dup_e.args + if dup_enum != ldb.ERR_NO_SUCH_OBJECT: + raise + + if (len(dup_res) != 1): + raise + + objectguid = samdb.schema_format_value("objectGUID", + dup_res[0]["objectGUID"][0]) + objectguid = objectguid.decode('utf-8') + logger.error("The RID Pool on the source DC for the backup in %s " + "may be corrupt " + "or in conflict with SIDs already allocated " + "in the domain. " % backup_file) + logger.error("Running 'samba-tool dbcheck' on the source " + "DC (and obtaining a new backup) may correct the issue.") + logger.error("Alternatively please obtain a new backup " + "against a different DC.") + logger.error("The SID we wish to use (%s) is recorded in " + "@SAMBA_DSDB as the sidForRestore attribute." + % sid) + + raise CommandError("Domain restore failed because there " + "is already an existing object (%s) " + "with SID %s and objectGUID %s. " + "This conflicts with " + "the new DC account we want to add " + "for the restored domain. " % ( + dup_res[0].dn, sid, objectguid)) m = ldb.Message() m.dn = ldb.Dn(samdb, '@ROOTDSE') @@ -568,7 +611,8 @@ class cmd_domain_backup_restore(cmd_fsmo_seize): host_ip, host_ip6, site) secrets_path = os.path.join(private_dir, 'secrets.ldb') - secrets_ldb = Ldb(secrets_path, session_info=system_session(), lp=lp) + secrets_ldb = Ldb(secrets_path, session_info=system_session(), lp=lp, + flags=ldb.FLG_DONT_CREATE_DB) secretsdb_self_join(secrets_ldb, domain=ctx.domain_name, realm=ctx.realm, dnsdomain=ctx.dnsdomain, netbiosname=ctx.myname, domainsid=ctx.domsid, @@ -860,7 +904,8 @@ class cmd_domain_backup_rename(samba.netcmd.Command): # connect to the local DB (making sure we use the new/renamed config) lp.load(paths.smbconf) - samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp) + samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp, + flags=ldb.FLG_DONT_CREATE_DB) # Edit the cloned sam.ldb to mark it as a backup time_str = get_timestamp() @@ -948,7 +993,8 @@ class cmd_domain_backup_offline(samba.netcmd.Command): # on the secrets.ldb file before backing up that file and secrets.tdb def backup_secrets(self, private_dir, lp, logger): secrets_path = os.path.join(private_dir, 'secrets') - secrets_obj = Ldb(secrets_path + '.ldb', lp=lp) + secrets_obj = Ldb(secrets_path + '.ldb', lp=lp, + flags=ldb.FLG_DONT_CREATE_DB) logger.info('Starting transaction on ' + secrets_path) secrets_obj.transaction_start() self.offline_tdb_copy(secrets_path + '.ldb') @@ -973,7 +1019,7 @@ class cmd_domain_backup_offline(samba.netcmd.Command): else: logger.info('Starting transaction on ' + sam_ldb_path) copy_function = self.offline_tdb_copy - sam_obj = Ldb(sam_ldb_path, lp=lp) + sam_obj = Ldb(sam_ldb_path, lp=lp, flags=ldb.FLG_DONT_CREATE_DB) sam_obj.transaction_start() logger.info(' backing up ' + sam_ldb_path) @@ -1025,9 +1071,14 @@ class cmd_domain_backup_offline(samba.netcmd.Command): check_targetdir(logger, targetdir) - samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp) + samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp, + flags=ldb.FLG_RDONLY) sid = get_sid_for_restore(samdb, logger) + # Iterating over the directories in this specific order ensures that + # when the private directory contains hardlinks that are also contained + # in other directories to be backed up (such as in paths.binddns_dir), + # the hardlinks in the private directory take precedence. backup_dirs = [paths.private_dir, paths.state_dir, os.path.dirname(paths.smbconf)] # etc dir logger.info('running backup on dirs: {0}'.format(' '.join(backup_dirs))) @@ -1040,22 +1091,31 @@ class cmd_domain_backup_offline(samba.netcmd.Command): continue if working_dir.endswith('.sock') or '.sock/' in working_dir: continue + # The BIND DNS database can be regenerated, so it doesn't need + # to be backed up. + if working_dir.startswith(os.path.join(paths.binddns_dir, 'dns')): + continue for filename in filenames: - if filename in all_files: + full_path = os.path.join(working_dir, filename) + + # Ignore files that have already been added. This prevents + # duplicates if one backup dir is a subdirectory of another, + # or if backup dirs contain hardlinks. + if any(os.path.samefile(full_path, file) for file in all_files): continue # Assume existing backup files are from a previous backup. # Delete and ignore. if filename.endswith(self.backup_ext): - os.remove(os.path.join(working_dir, filename)) + os.remove(full_path) continue # Sock files are autogenerated at runtime, ignore. if filename.endswith('.sock'): continue - all_files.append(os.path.join(working_dir, filename)) + all_files.append(full_path) # Backup secrets, sam.ldb and their downstream files self.backup_secrets(paths.private_dir, lp, logger) @@ -1067,7 +1127,8 @@ class cmd_domain_backup_offline(samba.netcmd.Command): # Writing to a .bak file only works because the DN being # written to happens to be top level. samdb = SamDB(url=paths.samdb + self.backup_ext, - session_info=system_session(), lp=lp) + session_info=system_session(), lp=lp, + flags=ldb.FLG_DONT_CREATE_DB) time_str = get_timestamp() add_backup_marker(samdb, "backupDate", time_str) add_backup_marker(samdb, "sidForRestore", sid) @@ -1079,7 +1140,7 @@ class cmd_domain_backup_offline(samba.netcmd.Command): if not os.path.exists(path + self.backup_ext): if path.endswith('.ldb'): logger.info('Starting transaction on solo db: ' + path) - ldb_obj = Ldb(path, lp=lp) + ldb_obj = Ldb(path, lp=lp, flags=ldb.FLG_DONT_CREATE_DB) ldb_obj.transaction_start() logger.info(' running tdbbackup on the same file') self.offline_tdb_copy(path) diff --git a/python/samba/samdb.py b/python/samba/samdb.py index d903babb406..d13c5e3b7a2 100644 --- a/python/samba/samdb.py +++ b/python/samba/samdb.py @@ -1285,6 +1285,111 @@ schemaUpdateNow: 1 '''return a new RID from the RID Pool on this DSA''' return dsdb._dsdb_allocate_rid(self) + def next_free_rid(self): + '''return the next free RID from the RID Pool on this DSA. + + :note: This function is not intended for general use, and care must be + taken if it is used to generate objectSIDs. The returned RID is not + formally reserved for use, creating the possibility of duplicate + objectSIDs. + ''' + rid, _ = self.free_rid_bounds() + return rid + + def free_rid_bounds(self): + '''return the low and high bounds (inclusive) of RIDs that are + available for use in this DSA's current RID pool. + + :note: This function is not intended for general use, and care must be + taken if it is used to generate objectSIDs. The returned range of + RIDs is not formally reserved for use, creating the possibility of + duplicate objectSIDs. + ''' + # Get DN of this server's RID Set + server_name_dn = ldb.Dn(self, self.get_serverName()) + res = self.search(base=server_name_dn, + scope=ldb.SCOPE_BASE, + attrs=["serverReference"]) + try: + server_ref = res[0]["serverReference"] + except KeyError: + raise ldb.LdbError( + ldb.ERR_NO_SUCH_ATTRIBUTE, + "No RID Set DN - " + "Cannot find attribute serverReference of %s " + "to calculate reference dn" % server_name_dn) from None + server_ref_dn = ldb.Dn(self, server_ref[0].decode("utf-8")) + + res = self.search(base=server_ref_dn, + scope=ldb.SCOPE_BASE, + attrs=["rIDSetReferences"]) + try: + rid_set_refs = res[0]["rIDSetReferences"] + except KeyError: + raise ldb.LdbError( + ldb.ERR_NO_SUCH_ATTRIBUTE, + "No RID Set DN - " + "Cannot find attribute rIDSetReferences of %s " + "to calculate reference dn" % server_ref_dn) from None + rid_set_dn = ldb.Dn(self, rid_set_refs[0].decode("utf-8")) + + # Get the alloc pools and next RID of this RID Set + res = self.search(base=rid_set_dn, + scope=ldb.SCOPE_BASE, + attrs=["rIDAllocationPool", + "rIDPreviousAllocationPool", + "rIDNextRID"]) + + uint32_max = 2**32 - 1 + uint64_max = 2**64 - 1 + + try: + alloc_pool = int(res[0]["rIDAllocationPool"][0]) + except KeyError: + alloc_pool = uint64_max + if alloc_pool == uint64_max: + raise ldb.LdbError(ldb.ERR_OPERATIONS_ERROR, + "Bad RID Set %s" % rid_set_dn) + + try: + prev_pool = int(res[0]["rIDPreviousAllocationPool"][0]) + except KeyError: + prev_pool = uint64_max + try: + next_rid = int(res[0]["rIDNextRID"][0]) + except KeyError: + next_rid = uint32_max + + # If we never used a pool, set up our first pool + if prev_pool == uint64_max or next_rid == uint32_max: + prev_pool = alloc_pool + next_rid = prev_pool & uint32_max + + next_rid += 1 + + # Now check if our current pool is still usable + prev_pool_lo = prev_pool & uint32_max + prev_pool_hi = prev_pool >> 32 + if next_rid > prev_pool_hi: + # We need a new pool, check if we already have a new one + # Otherwise we return an error code. + if alloc_pool == prev_pool: + raise ldb.LdbError(ldb.ERR_OPERATIONS_ERROR, + "RID pools out of RIDs") + + # Now use the new pool + prev_pool = alloc_pool + prev_pool_lo = prev_pool & uint32_max + prev_pool_hi = prev_pool >> 32 + next_rid = prev_pool_lo + + if next_rid < prev_pool_lo or next_rid > prev_pool_hi: + raise ldb.LdbError(ldb.ERR_OPERATIONS_ERROR, + "Bad RID chosen %d from range %d-%d" % + (next_rid, prev_pool_lo, prev_pool_hi)) + + return next_rid, prev_pool_hi + def normalize_dn_in_domain(self, dn): '''return a new DN expanded by adding the domain DN diff --git a/python/samba/tests/domain_backup_offline.py b/python/samba/tests/domain_backup_offline.py index 8b7209ec24d..21f42c6dab8 100644 --- a/python/samba/tests/domain_backup_offline.py +++ b/python/samba/tests/domain_backup_offline.py @@ -19,8 +19,12 @@ import tarfile import os import shutil import tempfile -from samba.tests import BlackboxTestCase +from samba.tests import BlackboxTestCase, BlackboxProcessError from samba.netcmd import CommandError +from samba.param import LoadParm +from samba.join import join_DC +from samba.credentials import Credentials +from samba.logger import get_samba_logger # The backup tests require that a completely clean LoadParm object gets used # for the restore. Otherwise the same global LP gets re-used, and the LP @@ -31,6 +35,81 @@ from samba.netcmd import CommandError # so that we never inadvertently use .runcmd() by accident. class DomainBackupOfflineCmp(BlackboxTestCase): + def test_domain_backup_offline_nested_tdb(self): + self.nested_testcase('tdb') + + def test_domain_backup_offline_nested_mdb(self): + self.nested_testcase('mdb') + + def nested_testcase(self, backend): + self.prov_dir = self.provision(backend) + self.extract_dir = None + + src = os.path.join(self.prov_dir, "private") + dst = os.path.join(self.prov_dir, "state", "private") + + # Move private directory inside state directory + shutil.move(src, dst) + + smbconf = os.path.join(self.prov_dir, "etc", "smb.conf") + + # Update the conf file + lp = LoadParm(filename_for_non_global_lp=smbconf) + lp.set("private dir", dst) + lp.dump(False, smbconf) + + backup_file = self.backup(self.prov_dir) + + # Ensure each file is only present once in the tar file + tf = tarfile.open(backup_file) + names = tf.getnames() + self.assertEqual(len(names), len(set(names))) + + def test_domain_backup_offline_join_restore_tdb(self): + self.join_restore_testcase('tdb') + + def test_domain_backup_offline_join_restore_mdb(self): + self.join_restore_testcase('mdb') + + def join_restore_testcase(self, backend): + self.prov_dir = self.join(backend) + self.extract_dir = None + + try: + backup_file = self.backup(self.prov_dir) + except BlackboxProcessError as e: + self.fail(e) + + self.extract_dir = self.restore(backup_file) + + def test_domain_backup_offline_hard_link_tdb(self): + self.hard_link_testcase('tdb') + + def test_domain_backup_offline_hard_link_mdb(self): + self.hard_link_testcase('mdb') + + def hard_link_testcase(self, backend): + self.prov_dir = self.provision(backend) + self.extract_dir = None + + # Create hard links in the private and state directories + os.link(os.path.join(self.prov_dir, "private", "krb5.conf"), + os.path.join(self.prov_dir, "state", "krb5.conf")) + + backup_file = self.backup(self.prov_dir) + + # Extract the backup + self.extract_dir = tempfile.mkdtemp(dir=self.tempdir) + tf = tarfile.open(backup_file) + tf.extractall(self.extract_dir) + + # Ensure that the hard link in the private directory was backed up, + # while the one in the state directory was not. + self.assertTrue(os.path.exists(os.path.join(self.extract_dir, + "private", "krb5.conf"))) + self.assertFalse(os.path.exists(os.path.join(self.extract_dir, + "statedir", "krb5.conf"))) + -- Samba Shared Repository