samba-tool domain demote: Add support for removing by NTDS GUID
authorAndrew Bartlett <abartlet@samba.org>
Tue, 20 Oct 2015 00:48:07 +0000 (13:48 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 26 Oct 2015 04:11:22 +0000 (05:11 +0100)
This would help remove a DC that is a conflict record, for example

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
python/samba/netcmd/domain.py
python/samba/remove_dc.py
python/samba/tests/blackbox/samba_tool_drs.py

index 689371aaafa06ad479278799c4c7080a7d9e01c8..6726538fc1b8f0fa26c2455b0840b273be69df66 100644 (file)
@@ -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"),
         ]
index 90ad7c6eafc5ee7da8bd80e87966ec1c687b184c..055a5166e583ba71c33639b2fd57563794e37534 100644 (file)
@@ -16,6 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+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 = "<GUID=%s>" % 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()
 
index 7ca7c33ede13b021eaab96f32a85c99f51551226..6056645e267d8509d35e2eeb6f4c41d7fcd3b389 100644 (file)
@@ -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", "<GUID=%s>" % 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"))