samba-tool domain demote: Allow to operate on an RODC and a subdomain
authorAndrew Bartlett <abartlet@samba.org>
Thu, 22 Oct 2015 22:23:05 +0000 (11:23 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 26 Oct 2015 04:11:21 +0000 (05:11 +0100)
On an RODC the local database cannot be modified, and the flags to remove
are different, we need instead to remove UF_PARTIAL_SECRETS_ACCOUNT.

If we are in a subdomain, then db.get_root_basedn() points to the
forest root, not the root of our domain

If the removeDsServer() fails with WERR_DS_DRA_NO_REPLICA
this may be reasonably considered to be success in this case.

Finally, the remove_dc.remove_sysvol_references() is reused
for objects not under the computer account.

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
domain demote

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

index e1ae40de54f7e1b044df1bd5a74cb0131c877e5a..aa301f5810b7fe1f5a8b45e5ace28f2b98962f05 100644 (file)
@@ -71,7 +71,8 @@ from samba.dsdb import (
     DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
     UF_WORKSTATION_TRUST_ACCOUNT,
     UF_SERVER_TRUST_ACCOUNT,
-    UF_TRUSTED_FOR_DELEGATION
+    UF_TRUSTED_FOR_DELEGATION,
+    UF_PARTIAL_SECRETS_ACCOUNT
     )
 
 from samba.provision import (
@@ -734,37 +735,48 @@ class cmd_domain_demote(Command):
 
         self.errf.write("Deactivating inbound replication\n")
 
-        nmsg = ldb.Message()
-        nmsg.dn = msg[0].dn
+        if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
+            nmsg = ldb.Message()
+            nmsg.dn = msg[0].dn
 
-        dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
-        nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
-        samdb.modify(nmsg)
+            dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
+            nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
+            samdb.modify(nmsg)
 
-        if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
 
             self.errf.write("Asking partner server %s to synchronize from us\n"
                             % server)
             for part in (samdb.get_schema_basedn(),
                             samdb.get_config_basedn(),
                             samdb.get_root_basedn()):
+                nc = drsuapi.DsReplicaObjectIdentifier()
+                nc.dn = str(part)
+
+                req1 = drsuapi.DsReplicaSyncRequest1()
+                req1.naming_context = nc;
+                req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
+                req1.source_dsa_guid = misc.GUID(ntds_guid)
+
                 try:
-                    sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid, str(part), drsuapi.DRSUAPI_DRS_WRIT_REP)
-                except drsException, e:
-                    self.errf.write(
-                        "Error while demoting, "
+                    drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
+                except RuntimeError as (werr, string):
+                    if werr == 8452: #WERR_DS_DRA_NO_REPLICA
+                        pass
+                    else:
+                        self.errf.write(
+                            "Error while demoting, "
                         "re-enabling inbound replication\n")
-                    dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
-                    nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
-                    samdb.modify(nmsg)
-                    raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
+                        dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
+                        nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
+                        samdb.modify(nmsg)
+                        raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
         try:
             remote_samdb = SamDB(url="ldap://%s" % server,
                                 session_info=system_session(),
                                 credentials=creds, lp=lp)
 
             self.errf.write("Changing userControl and container\n")
-            res = remote_samdb.search(base=str(remote_samdb.get_root_basedn()),
+            res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
                                 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
                                             netbios_name.upper(),
                                 attrs=["userAccountControl"])
@@ -790,7 +802,7 @@ class cmd_domain_demote(Command):
 
         olduac = uac
 
-        uac ^= (UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION)
+        uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
         uac |= UF_WORKSTATION_TRUST_ACCOUNT
 
         msg = ldb.Message()
@@ -811,13 +823,12 @@ class cmd_domain_demote(Command):
             raise CommandError("Error while changing account control", e)
 
         parent = msg.dn.parent()
-        rdn = str(res[0].dn)
-        rdn = string.replace(rdn, ",%s" % str(parent), "")
+        rdn = "%s=%s" % (res[0].dn.get_rdn_name(), res[0].dn.get_rdn_value())
         # Let's move to the Computer container
         i = 0
-        newrdn = rdn
+        newrdn = str(rdn)
 
-        computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.get_root_basedn()))
+        computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
         res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
 
         if (len(res) != 0):
@@ -875,34 +886,36 @@ class cmd_domain_demote(Command):
         domain = remote_samdb.get_root_basedn()
 
         try:
-            sendRemoveDsServer(drsuapiBind, drsuapi_handle, server_dsa_dn, domain)
-        except drsException, e:
-            self.errf.write(
-                "Error while demoting, re-enabling inbound replication\n")
-            dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
-            nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
-            samdb.modify(nmsg)
+            req1 = drsuapi.DsRemoveDSServerRequest1()
+            req1.server_dn = str(server_dsa_dn)
+            req1.domain_dn = str(domain)
+            req1.commit = 1
+
+            drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
+        except RuntimeError as (werr, string):
+            if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
+                self.errf.write(
+                    "Error while demoting, re-enabling inbound replication\n")
+                dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
+                nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
+                samdb.modify(nmsg)
 
             msg = ldb.Message()
             msg.dn = newdn
 
             msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
-                                                    ldb.FLAG_MOD_REPLACE,
-                                                    "userAccountControl")
-            print str(dc_dn)
+                                                           ldb.FLAG_MOD_REPLACE,
+                                                           "userAccountControl")
             remote_samdb.modify(msg)
             remote_samdb.rename(newdn, dc_dn)
-            raise CommandError("Error while sending a removeDsServer", e)
+            if werr == 8452: #WERR_DS_DRA_NO_REPLICA
+                raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
+            else:
+                raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
 
-        for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System,CN=Configuration",
-                  "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % lp.get("realm"),
-                  "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"):
-            try:
-                remote_samdb.delete(ldb.Dn(remote_samdb,
-                                    "%s,%s,%s" % (str(rdn), s, str(remote_samdb.get_root_basedn()))))
-            except ldb.LdbError, l:
-                pass
+        remove_dc.remove_sysvol_references(remote_samdb, rdn)
 
+        # These are objects under the computer account that should be deleted
         for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
                   "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
                   "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",