From: Andrew Bartlett Date: Tue, 20 Oct 2015 00:48:07 +0000 (+1300) Subject: samba-tool domain demote: Add support for removing by NTDS GUID X-Git-Url: http://git.samba.org/?a=commitdiff_plain;h=55a13e17b36adc69eb4ec7d706cb9a55906f8275;p=obnox%2Fsamba%2Fsamba-obnox.git samba-tool domain demote: Add support for removing by NTDS GUID This would help remove a DC that is a conflict record, for example Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam --- diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index 689371aaafa..6726538fc1b 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -671,7 +671,8 @@ class cmd_domain_demote(Command): Option("--server", help="writable DC to write demotion changes on", type=str), Option("-H", "--URL", help="LDB URL for database or target server", type=str, metavar="URL", dest="H"), - Option("--remove-other-dead-server", help="Dead DC to remove ALL references to (rather than this DC)", type=str), + Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) " + "to remove ALL references to (rather than this DC)", type=str), Option("--quiet", help="Be quiet", action="store_true"), Option("--verbose", help="Be verbose", action="store_true"), ] diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 90ad7c6eafc..055a5166e58 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -16,6 +16,7 @@ # along with this program. If not, see . # +import uuid import ldb from ldb import LdbError from samba.ndr import ndr_unpack @@ -357,46 +358,69 @@ def remove_dc(samdb, logger, dc_name): samdb.transaction_start() - msgs = samdb.search(base=samdb.get_config_basedn(), - attrs=["serverReference"], - expression="(&(objectClass=server)(cn=%s))" - % ldb.binary_encode(dc_name)) - if (len(msgs) == 0): - raise DemoteException("%s is not an AD DC in %s" - % (dc_name, samdb.domain_dns_name())) - server_dn = msgs[0].dn + server_dn = None - ntds_dn = ldb.Dn(samdb, "CN=NTDS Settings") - ntds_dn.add_base(msgs[0].dn) + # Allow the name to be a the nTDS-DSA GUID + try: + ntds_guid = uuid.UUID(hex=dc_name) + ntds_dn = "" % ntds_guid + except ValueError: + try: + server_msgs = samdb.search(base=samdb.get_config_basedn(), + attrs=[], + expression="(&(objectClass=server)" + "(cn=%s))" + % ldb.binary_encode(dc_name)) + except LdbError as (enum, estr): + raise DemoteException("Failure checking if %s is an server " + "object in %s: " + % (dc_name, samdb.domain_dns_name()), estr) + + if (len(server_msgs) == 0): + raise DemoteException("%s is not an AD DC in %s" + % (dc_name, samdb.domain_dns_name())) + server_dn = server_msgs[0].dn + + ntds_dn = ldb.Dn(samdb, "CN=NTDS Settings") + ntds_dn.add_base(server_dn) + pass # Confirm this is really an ntdsDSA object try: - msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, - expression="(objectClass=ntdsdsa)") + ntds_msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, + expression="(objectClass=ntdsdsa)") except LdbError as (enum, estr): if enum == ldb.ERR_NO_SUCH_OBJECT: - offline_remove_server(samdb, logger, - msgs[0].dn, - remove_computer_obj=True, - remove_server_obj=True, - remove_sysvol_obj=True, - remove_dns_names=True, - remove_dns_account=True) - - samdb.transaction_commit() - return - else: + ntds_msgs = [] pass - - offline_remove_ntds_dc(samdb, logger, - msgs[0].dn, - remove_computer_obj=True, - remove_server_obj=True, - remove_connection_obj=True, - seize_stale_fsmo=True, - remove_sysvol_obj=True, - remove_dns_names=True, - remove_dns_account=True) + else: + raise DemoteException("Failure checking if %s is an NTDS DSA in %s: " + % (ntds_dn, samdb.domain_dns_name()), estr) + + # If the NTDS Settings child DN wasn't found or wasnt an ntdsDSA + # object, just remove the server object located above + if (len(ntds_msgs) == 0): + if server_dn is None: + raise DemoteException("%s is not an AD DC in %s" + % (dc_name, samdb.domain_dns_name())) + + offline_remove_server(samdb, logger, + server_dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_sysvol_obj=True, + remove_dns_names=True, + remove_dns_account=True) + else: + offline_remove_ntds_dc(samdb, logger, + ntds_msgs[0].dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_connection_obj=True, + seize_stale_fsmo=True, + remove_sysvol_obj=True, + remove_dns_names=True, + remove_dns_account=True) samdb.transaction_commit() diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index 7ca7c33ede1..6056645e267 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -130,6 +130,43 @@ class SambaToolDrsTests(samba.tests.BlackboxTestCase): def get_krbtgt_pw(): krbtgt_pw = samdb.searchone("unicodePwd", "cn=krbtgt,CN=users,%s" % nc_name) self.assertRaises(KeyError, get_krbtgt_pw) + + server_dn = samdb.searchone("serverReferenceBL", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + ntds_guid = samdb.searchone("objectGUID", "cn=ntds settings,%s" % server_dn) + + res = samdb.search(base=str(server_nc_name), + expression="(&(objectclass=user)(cn=dns-%s))" % (self.dc2), + attrs=[], scope=ldb.SCOPE_SUBTREE) + if len(res) == 1: + dns_obj = res[0] + else: + dns_obj = None + + # While we have this cloned, try demoting the other server on the clone, by GUID + out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" + % (ntds_guid, + self.tempdir)) + + # Check some of the objects that should have been removed + def check_machine_obj(): + samdb.searchone("CN", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + self.assertRaises(ldb.LdbError, check_machine_obj) + + def check_server_obj(): + samdb.searchone("CN", server_dn) + self.assertRaises(ldb.LdbError, check_server_obj) + + def check_ntds_guid(): + samdb.searchone("CN", "" % ntds_guid) + self.assertRaises(ldb.LdbError, check_ntds_guid) + + if dns_obj is not None: + # Check some of the objects that should have been removed + def check_dns_account_obj(): + samdb.search(base=dns_obj.dn, scope=ldb.SCOPE_BASE, + attrs=[]) + self.assertRaises(ldb.LdbError, check_dns_account_obj) + shutil.rmtree(os.path.join(self.tempdir, "private")) shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock"))