[PATCH] Fix server side DRSUAPI_DRS_GET_ANC handling (bug #12398)

Stefan Metzmacher metze at samba.org
Wed Feb 8 18:50:27 UTC 2017


Hi Bob,

here's the complete patchset that passed autobuild twice.

Can someone please review and push this?

Thanks!
metze

> On 08/02/17 08:47, Stefan Metzmacher wrote:
>> Am 02.02.2017 um 09:06 schrieb Stefan Metzmacher:
>>> Hi Bob,
>>>
>>>> Attached is the proposed patchset, including 6 of your patches. Could
>>>> you please give your sign off for (and change the commit message of if
>>>> necessary) 0004-torture-drs-add-a-test-for-DRSUAPI_DRS_GET_ANC?
>>> I'll have a look at this soon.
>> I've changed the logic a bit see
>> https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master4-getncchanges
> These changes seem alright as far as I can tell.
>> I'm running some autobuilds with it, if it passes
>> I'll cleanup the remaining commit messages.
>>
>> I think we should squash all GET_ANC related server patches
>> into my commit and add Pair-programmed-with tags for you,
>> is that ok?
> Yes, that's fine.
>>
>> metze
>>
> 
> Thanks,
> Bob
> 
-------------- next part --------------
From 3fb59aa2f2847c3e8bdedd030fca1b69b8b09e94 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 1 Dec 2016 11:49:07 +0100
Subject: [PATCH 01/19] s4:dsdb/repl: s/highestCommitedUsn/highestCommittedUSN

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/repl/drepl_service.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/dsdb/repl/drepl_service.h b/source4/dsdb/repl/drepl_service.h
index edba4c4..6b85ad6 100644
--- a/source4/dsdb/repl/drepl_service.h
+++ b/source4/dsdb/repl/drepl_service.h
@@ -89,7 +89,7 @@ struct dreplsrv_partition {
 	/* 
 	 * uptodate vector needs to be updated before and after each DsGetNCChanges() call
 	 *
-	 * - before: we need to use our own invocationId together with our highestCommitedUsn
+	 * - before: we need to use our own invocationId together with our highestCommittedUSN
 	 * - after: we need to merge in the remote uptodatevector, to avoid reading it again
 	 */
 	struct replUpToDateVectorCtr2 uptodatevector;
-- 
1.9.1


From 1625c254e533610a994be7d4248f6ec6a0469660 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 1 Dec 2016 11:49:25 +0100
Subject: [PATCH 02/19] s4:libnet: s/highestCommitedUSN/highestCommittedUSN

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/libnet/libnet_become_dc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/libnet/libnet_become_dc.c b/source4/libnet/libnet_become_dc.c
index fdd2a63..43a3209 100644
--- a/source4/libnet/libnet_become_dc.c
+++ b/source4/libnet/libnet_become_dc.c
@@ -84,7 +84,7 @@
  *		supportedLDAPVersion:	3
  *					2
  *		supportedLDAPPolicies:	...
- *		highestCommitedUSN:	...
+ *		highestCommittedUSN:	...
  *		supportedSASLMechanisms:GSSAPI
  *					GSS-SPNEGO
  *					EXTERNAL
-- 
1.9.1


From c639435069b04240effc7234a59989a2e2791de2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 29 Nov 2016 09:22:44 +0100
Subject: [PATCH 03/19] drsuapi.idl: add drsuapi_DrsMoreOptions with
 DRSUAPI_DRS_GET_TGT

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 librpc/idl/drsuapi.idl | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/librpc/idl/drsuapi.idl b/librpc/idl/drsuapi.idl
index c3af8a5..d2b3dcf 100644
--- a/librpc/idl/drsuapi.idl
+++ b/librpc/idl/drsuapi.idl
@@ -58,6 +58,10 @@ interface drsuapi
 		DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP  = 0x80000000
 	} drsuapi_DrsOptions;
 
+	typedef [public,bitmap32bit] bitmap {
+		DRSUAPI_DRS_GET_TGT                   = 0x00000001
+	} drsuapi_DrsMoreOptions;
+
 	/* see DRS_MSG_REPMOD_V1 */
 	typedef [public,bitmap32bit] bitmap {
 		DRSUAPI_DRS_UPDATE_FLAGS              = 0x00000001,
@@ -587,7 +591,7 @@ interface drsuapi
 		drsuapi_DsPartialAttributeSet *partial_attribute_set;
 		drsuapi_DsPartialAttributeSet *partial_attribute_set_ex;
 		drsuapi_DsReplicaOIDMapping_Ctr mapping_ctr;
-		uint32 more_flags;
+		drsuapi_DrsMoreOptions more_flags;
 	} drsuapi_DsGetNCChangesRequest10;
 
 	typedef [switch_type(uint32)] union {
-- 
1.9.1


From ef6c85fe1a6f17f9c98ec2170402c6045cd4adf0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 7 Feb 2017 16:22:41 +0100
Subject: [PATCH 04/19] drsuapi.idl: make drsuapi_DsGetNCChangesRequest10
 [public]

This allows ndr_print to work.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 librpc/idl/drsuapi.idl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/librpc/idl/drsuapi.idl b/librpc/idl/drsuapi.idl
index d2b3dcf..d08054f 100644
--- a/librpc/idl/drsuapi.idl
+++ b/librpc/idl/drsuapi.idl
@@ -577,7 +577,7 @@ interface drsuapi
 		drsuapi_DsReplicaOIDMapping_Ctr mapping_ctr;
 	} drsuapi_DsGetNCChangesRequest8;
 
-	typedef struct {
+	typedef [public] struct {
 		GUID destination_dsa_guid;
 		GUID source_dsa_invocation_id; /* the 'invocationId' field of the CN=NTDS Settings object */
 		[ref] drsuapi_DsReplicaObjectIdentifier *naming_context;
-- 
1.9.1


From 4cad53b21ca66a645b49512231aeca6b403df846 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 29 Nov 2016 14:27:57 +0100
Subject: [PATCH 05/19] python/join: set common replica_flags in
 dc_join.__init__()

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/join.py | 39 +++++++++++++--------------------------
 1 file changed, 13 insertions(+), 26 deletions(-)

diff --git a/python/samba/join.py b/python/samba/join.py
index c56f8d9..d4a040f 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -165,6 +165,11 @@ class dc_join(object):
 
         ctx.tmp_samdb = None
 
+        ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
+                             drsuapi.DRSUAPI_DRS_PER_SYNC |
+                             drsuapi.DRSUAPI_DRS_GET_ANC |
+                             drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
+
         # these elements are optional
         ctx.never_reveal_sid = None
         ctx.reveal_sid = None
@@ -891,13 +896,11 @@ class dc_join(object):
                 # Replicate first the critical object for the basedn
                 if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
                     print "Replicating critical objects from the base DN of the domain"
-                    ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC
+                    ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
                     repl.replicate(ctx.base_dn, source_dsa_invocation_id,
                                 destination_dsa_guid, rodc=ctx.RODC,
                                 replica_flags=ctx.domain_replica_flags)
                     ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
-                else:
-                    ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_GET_ANC
                 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
                                destination_dsa_guid, rodc=ctx.RODC,
                                replica_flags=ctx.domain_replica_flags)
@@ -1226,11 +1229,7 @@ def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_
     ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
     ctx.secure_channel_type = misc.SEC_CHAN_RODC
     ctx.RODC = True
-    ctx.replica_flags  =  (drsuapi.DRSUAPI_DRS_INIT_SYNC |
-                           drsuapi.DRSUAPI_DRS_PER_SYNC |
-                           drsuapi.DRSUAPI_DRS_GET_ANC |
-                           drsuapi.DRSUAPI_DRS_NEVER_SYNCED |
-                           drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
+    ctx.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
                            drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
     ctx.domain_replica_flags = ctx.replica_flags
     if domain_critical_only:
@@ -1260,12 +1259,8 @@ def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_na
     ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
     ctx.secure_channel_type = misc.SEC_CHAN_BDC
 
-    ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP |
-                         drsuapi.DRSUAPI_DRS_INIT_SYNC |
-                         drsuapi.DRSUAPI_DRS_PER_SYNC |
-                         drsuapi.DRSUAPI_DRS_GET_ANC |
-                         drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS |
-                         drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
+    ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
+                          drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
     ctx.domain_replica_flags = ctx.replica_flags
     if domain_critical_only:
         ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
@@ -1285,12 +1280,8 @@ def join_clone(logger=None, server=None, creds=None, lp=None,
     lp.set("realm", ctx.realm)
     logger.info("realm is %s" % ctx.realm)
 
-    ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP |
-                         drsuapi.DRSUAPI_DRS_INIT_SYNC |
-                         drsuapi.DRSUAPI_DRS_PER_SYNC |
-                         drsuapi.DRSUAPI_DRS_GET_ANC |
-                         drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS |
-                         drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
+    ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
+                          drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
     if not include_secrets:
         ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
     ctx.domain_replica_flags = ctx.replica_flags
@@ -1341,12 +1332,8 @@ def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
     ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
     ctx.secure_channel_type = misc.SEC_CHAN_BDC
 
-    ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP |
-                         drsuapi.DRSUAPI_DRS_INIT_SYNC |
-                         drsuapi.DRSUAPI_DRS_PER_SYNC |
-                         drsuapi.DRSUAPI_DRS_GET_ANC |
-                         drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS |
-                         drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
+    ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
+                          drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
     ctx.domain_replica_flags = ctx.replica_flags
 
     ctx.do_join()
-- 
1.9.1


From 8e244a772cff7c46fe92e218ad4cddce1c414ca8 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 29 Nov 2016 14:29:59 +0100
Subject: [PATCH 06/19] python/join: use DRSUAPI_DRS_GET_NC_SIZE for the
 initial replication

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/join.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/python/samba/join.py b/python/samba/join.py
index d4a040f..4eb8c58 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -168,6 +168,7 @@ class dc_join(object):
         ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
                              drsuapi.DRSUAPI_DRS_PER_SYNC |
                              drsuapi.DRSUAPI_DRS_GET_ANC |
+                             drsuapi.DRSUAPI_DRS_GET_NC_SIZE |
                              drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
 
         # these elements are optional
-- 
1.9.1


From f96abfe861cce6d856652fad3d6a2813a60f6450 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 7 Feb 2017 17:06:47 +0100
Subject: [PATCH 07/19] torture/drs: remove pointless nc_object_count
 replication checks in test_link_utdv_hwm()

nc_object_count and nc_linked_attributes_count are only filled if
DRSUAPI_DRS_GET_NC_SIZE is requested. And they should contain
the total number. This is only useful for the initial replication.

Samba ignores DRSUAPI_DRS_GET_NC_SIZE currently but that will change in
the following commits.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail                       |  2 ++
 source4/torture/drs/python/getnc_exop.py | 27 +++++++++------------------
 2 files changed, 11 insertions(+), 18 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index d96e238..b453b9c 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -312,3 +312,5 @@
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_add_duplicate_different_type.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_rank_none.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_security_descriptor.*
+# nc_object_count is updated without GET_NC_SIZE
+^samba4.drs.getnc_exop.python.*getnc_exop.DrsReplicaSyncTestCase.test_link_utdv_hwm.*
diff --git a/source4/torture/drs/python/getnc_exop.py b/source4/torture/drs/python/getnc_exop.py
index d9e06f2..70fabfb 100644
--- a/source4/torture/drs/python/getnc_exop.py
+++ b/source4/torture/drs/python/getnc_exop.py
@@ -171,8 +171,7 @@ class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
 
         (hwm0, utdv0) = self._check_replication([ou2,dc3,cn3,ou1],
                             drsuapi.DRSUAPI_DRS_WRIT_REP,
-                            expected_links=[ou1_managedBy_dc3],
-                            nc_object_count=4)
+                            expected_links=[ou1_managedBy_dc3])
 
         m = ldb.Message()
         m.dn = ldb.Dn(self.ldb_dc1, dc3)
@@ -185,54 +184,46 @@ class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP,
             expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0,
-            nc_object_count=1)
+            highwatermark=hwm0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP|
             drsuapi.DRSUAPI_DRS_GET_ANC,
             expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0,
-            nc_object_count=1)
+            highwatermark=hwm0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
             expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0,
-            nc_object_count=1)
+            highwatermark=hwm0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
             drsuapi.DRSUAPI_DRS_GET_ANC,
             expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0,
-            nc_object_count=1)
+            highwatermark=hwm0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP,
             expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0,
-            nc_object_count=4)
+            uptodateness_vector=utdv0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP|
             drsuapi.DRSUAPI_DRS_GET_ANC,
             expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0,
-            nc_object_count=4)
+            uptodateness_vector=utdv0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
             expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0,
-            nc_object_count=1)
+            uptodateness_vector=utdv0)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
             drsuapi.DRSUAPI_DRS_GET_ANC,
             expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0,
-            nc_object_count=1)
+            uptodateness_vector=utdv0)
 
     def test_FSMONotOwner(self):
         """Test role transfer with against DC not owner of the role"""
-- 
1.9.1


From 146db710b368be1b52fe34edd644baf802aa6335 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 29 Nov 2016 13:23:23 +0100
Subject: [PATCH 08/19] getncchanges: only set
 nc_{object,linked_attributes}_count with DRSUAPI_DRS_GET_NC_SIZE

The main change is that we return 0 values if DRSUAPI_DRS_GET_NC_SIZE is not
present in order to get the same result as a Windows server in that case.

If DRSUAPI_DRS_GET_NC_SIZE is return the number of links we found so far
during the cycle in addition the number of objects returned in this cycle.
Both values doesn't match what Windows returns, but doing that
correctly and efficient is a task for another day.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail                        |  2 --
 source4/rpc_server/drsuapi/getncchanges.c | 18 ++++++++++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index b453b9c..d96e238 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -312,5 +312,3 @@
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_add_duplicate_different_type.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_rank_none.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_security_descriptor.*
-# nc_object_count is updated without GET_NC_SIZE
-^samba4.drs.getnc_exop.python.*getnc_exop.DrsReplicaSyncTestCase.test_link_utdv_hwm.*
diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 705c8cf..c8f2e91 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -2305,8 +2305,6 @@ allowed:
 
 	getnc_state->num_processed = i;
 
-	r->out.ctr->ctr6.nc_object_count = getnc_state->num_records;
-
 	/* the client can us to call UpdateRefs on its behalf to
 	   re-establish monitoring of the NC */
 	if ((req10->replica_flags & (DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_REF_GCSPN)) &&
@@ -2435,6 +2433,22 @@ allowed:
 		}
 	}
 
+	if (req10->replica_flags & DRSUAPI_DRS_GET_NC_SIZE) {
+		/*
+		 * TODO: This implementation is wrong
+		 * we should find out the total number of
+		 * objects and links in the whole naming context
+		 * at the start of the cycle and return these
+		 * values in each message.
+		 *
+		 * For now we keep our current strategy and return
+		 * the number of objects for this cycle and the number
+		 * of links we found so far during the cycle.
+		 */
+		r->out.ctr->ctr6.nc_object_count = getnc_state->num_records;
+		r->out.ctr->ctr6.nc_linked_attributes_count = getnc_state->la_count;
+	}
+
 	if (!r->out.ctr->ctr6.more_data) {
 		talloc_steal(mem_ctx, getnc_state->la_list);
 
-- 
1.9.1


From 8cbb8f16571395cc3fc16b1c8cb5dec372a55414 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 1 Dec 2016 11:50:34 +0100
Subject: [PATCH 09/19] getncchanges: pass struct ldb_message as const

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index c8f2e91..654e7ac 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -88,7 +88,7 @@ static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHigh
   build a DsReplicaObjectIdentifier from a ldb msg
  */
 static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
-								       struct ldb_message *msg)
+								       const struct ldb_message *msg)
 {
 	struct drsuapi_DsReplicaObjectIdentifier *identifier;
 	struct dom_sid *sid;
@@ -189,7 +189,7 @@ fail:
   drsuapi_DsGetNCChanges for one object
 */
 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
-					  struct ldb_message *msg,
+					  const struct ldb_message *msg,
 					  struct ldb_context *sam_ctx,
 					  struct ldb_dn *ncRoot_dn,
 					  bool   is_schema_nc,
@@ -446,7 +446,7 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
 				    struct ldb_context *sam_ctx,
 				    const struct dsdb_schema *schema,
 				    const struct dsdb_attribute *sa,
-				    struct ldb_message *msg,
+				    const struct ldb_message *msg,
 				    struct dsdb_dn *dsdb_dn,
 				    struct drsuapi_DsReplicaLinkedAttribute **la_list,
 				    uint32_t *la_count,
@@ -575,7 +575,7 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
 				       struct dsdb_schema *schema,
 				       uint64_t highest_usn,
 				       uint32_t replica_flags,
-				       struct ldb_message *msg,
+				       const struct ldb_message *msg,
 				       struct drsuapi_DsReplicaLinkedAttribute **la_list,
 				       uint32_t *la_count,
 				       struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
-- 
1.9.1


From 18d201d69bd70b6e38a4646fb86b7d488af4573f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 29 Nov 2016 11:09:46 +0100
Subject: [PATCH 10/19] getncchanges: remember the ncRoot_guid on the
 getncchanges state

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 654e7ac..2860b90 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -44,6 +44,7 @@ struct drsuapi_getncchanges_state {
 	uint32_t num_records;
 	uint32_t num_processed;
 	struct ldb_dn *ncRoot_dn;
+	struct GUID ncRoot_guid;
 	bool is_schema_nc;
 	uint64_t min_usn;
 	uint64_t max_usn;
@@ -1932,6 +1933,19 @@ allowed:
 		}
 		b_state->getncchanges_state = getnc_state;
 		getnc_state->ncRoot_dn = drs_ObjectIdentifier_to_dn(getnc_state, sam_ctx, ncRoot);
+		if (getnc_state->ncRoot_dn == NULL) {
+			return WERR_NOT_ENOUGH_MEMORY;
+		}
+
+		ret = dsdb_find_guid_by_dn(b_state->sam_ctx_system,
+					   getnc_state->ncRoot_dn,
+					   &getnc_state->ncRoot_guid);
+		if (ret != LDB_SUCCESS) {
+			DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
+				 ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
+			return WERR_DS_DRA_INTERNAL_ERROR;
+		}
+		ncRoot->guid = getnc_state->ncRoot_guid;
 
 		/* find out if we are to replicate Schema NC */
 		ret = ldb_dn_compare_base(ldb_get_schema_basedn(b_state->sam_ctx),
@@ -2009,6 +2023,8 @@ allowed:
 		return WERR_DS_DRA_INVALID_PARAMETER;
 	}
 
+	ncRoot->guid = getnc_state->ncRoot_guid;
+
 	/* we need the session key for encrypting password attributes */
 	status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -2119,14 +2135,10 @@ allowed:
 	}
 
 	r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
-	*r->out.ctr->ctr6.naming_context = *ncRoot;
-
-	if (dsdb_find_guid_by_dn(sam_ctx, getnc_state->ncRoot_dn,
-				 &r->out.ctr->ctr6.naming_context->guid) != LDB_SUCCESS) {
-		DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
-			 ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
-		return WERR_DS_DRA_INTERNAL_ERROR;
+	if (r->out.ctr->ctr6.naming_context == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
 	}
+	*r->out.ctr->ctr6.naming_context = *ncRoot;
 
 	/* find the SID if there is one */
 	dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
-- 
1.9.1


From dddb69cfa06118b8ecf6bec4fa928d7e8cc8cabe Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 30 Nov 2016 09:11:31 +0100
Subject: [PATCH 11/19] getncchanges: don't process DRSUAPI_DRS_CRITICAL_ONLY
 for EXOPs

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 2860b90..92235ac 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -1471,10 +1471,16 @@ static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
 	enum ldb_scope scope = LDB_SCOPE_SUBTREE;
 	//const char *extra_filter;
 	struct drsuapi_getncchanges_state *getnc_state = b_state->getncchanges_state;
+	bool critical_only = false;
+
+	if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
+		critical_only = true;
+	}
 
 	if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ ||
 	    req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
 		scope = LDB_SCOPE_BASE;
+		critical_only = false;
 	}
 
 	//extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
@@ -1490,7 +1496,7 @@ static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
 		search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
 	}
 
-	if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
+	if (critical_only) {
 		search_filter = talloc_asprintf(mem_ctx,
 						"(&%s(isCriticalSystemObject=TRUE))",
 						search_filter);
-- 
1.9.1


From d6a3b7a40f56c1907b805c5171f22cca4c61bfba Mon Sep 17 00:00:00 2001
From: Garming Sam <garming at catalyst.net.nz>
Date: Wed, 14 Dec 2016 16:04:32 +1300
Subject: [PATCH 12/19] getncchanges: do not replicate links for non critical
 objects if DRSUAPI_DRS_CRITICAL_ONLY is set

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Pair-programmed-with: Bob Campbell <bobcampbell at catalyst.net.nz>

Signed-off-by: Garming Sam <garming at catalyst.net.nz>
Signed-off-by: Bob Campbell <bobcampbell at catalyst.net.nz>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 92235ac..b0da27b 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -582,8 +582,20 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
 				       struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
 {
 	unsigned int i;
-	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+	TALLOC_CTX *tmp_ctx = NULL;
 	uint64_t uSNChanged = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
+	bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
+
+	if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
+		if (!is_critical) {
+			return WERR_OK;
+		}
+	}
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
 
 	for (i=0; i<msg->num_elements; i++) {
 		struct ldb_message_element *el = &msg->elements[i];
-- 
1.9.1


From de63101a6306c481c5dfdc41ff397dbdd13cab80 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 8 Feb 2017 10:24:56 +0100
Subject: [PATCH 13/19] getncchanges: remove unused c++ comments/code in
 getncchanges_collect_objects()

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index b0da27b..9a564bc 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -1481,7 +1481,6 @@ static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
 	int ret;
 	char* search_filter;
 	enum ldb_scope scope = LDB_SCOPE_SUBTREE;
-	//const char *extra_filter;
 	struct drsuapi_getncchanges_state *getnc_state = b_state->getncchanges_state;
 	bool critical_only = false;
 
@@ -1495,10 +1494,6 @@ static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
 		critical_only = false;
 	}
 
-	//extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
-
-	//getnc_state->min_usn = req10->highwatermark.highest_usn;
-
 	/* Construct response. */
 	search_filter = talloc_asprintf(mem_ctx,
 					"(uSNChanged>=%llu)",
-- 
1.9.1


From e1c441344a2c962fe4e9422bf522adc159e76402 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 7 Feb 2017 12:34:45 +0100
Subject: [PATCH 14/19] getncchanges: fix highest_usn off by one calculation in
 get_nc_changes_add_links()

highest_usn is the the highest usn the destination dsa already knows about.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 9a564bc..f0e5567 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -646,7 +646,7 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
 				return WERR_DS_DRA_INTERNAL_ERROR;
 			}
 
-			if (local_usn < highest_usn) {
+			if (local_usn <= highest_usn) {
 				continue;
 			}
 
-- 
1.9.1


From cc58f11f83b3c1d3f1e8fbaa53988f1b6abf9623 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 7 Feb 2017 12:28:33 +0100
Subject: [PATCH 15/19] getncchanges: improve get_nc_changes_build_object() by
 checking uSNChanged

This will make a difference once we handle DRSUAPI_DRS_GET_ANC correctly.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index f0e5567..6fe115d 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -212,6 +212,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
 	uint32_t *attids;
 	const char *rdn;
 	const struct dsdb_attribute *rdn_sa;
+	uint64_t uSNChanged;
 	unsigned int instanceType;
 	struct dsdb_syntax_ctx syntax_ctx;
 
@@ -219,6 +220,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
 	dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
 	syntax_ctx.is_schema_nc = is_schema_nc;
 
+	uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
 	instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
 	if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
 		obj->is_nc_prefix = true;
@@ -249,6 +251,11 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
 		return WERR_OK;
 	}
 
+	if (uSNChanged <= highest_usn) {
+		/* nothing to send */
+		return WERR_OK;
+	}
+
 	ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
 				       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
 	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-- 
1.9.1


From c06ed5981dfdcd44a3ca7d80ce030e33e5cfcde9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 7 Feb 2017 12:28:33 +0100
Subject: [PATCH 16/19] getncchanges: improve get_nc_changes_add_links() by
 checking uSNChanged

This will make a difference once we handle DRSUAPI_DRS_GET_ANC correctly.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 6fe115d..62b6e3a 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -590,7 +590,7 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
 {
 	unsigned int i;
 	TALLOC_CTX *tmp_ctx = NULL;
-	uint64_t uSNChanged = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
+	uint64_t uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
 	bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
 
 	if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
@@ -599,6 +599,10 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
 		}
 	}
 
+	if (uSNChanged <= highest_usn) {
+		return WERR_OK;
+	}
+
 	tmp_ctx = talloc_new(mem_ctx);
 	if (tmp_ctx == NULL) {
 		return WERR_NOT_ENOUGH_MEMORY;
-- 
1.9.1


From daeff4ad1672cb97cc42520ac0a9d335dfba1923 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 7 Feb 2017 12:37:16 +0100
Subject: [PATCH 17/19] getncchanges: calculate getnc_state->min_usn
 calculation based on the uptodateness vector

This should improve initial replication of a fresh destination dsa with
a zero highwatermark.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/drsuapi/getncchanges.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 62b6e3a..45f00ea 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -2063,10 +2063,33 @@ allowed:
 	if (getnc_state->guids == NULL) {
 		const char *extra_filter;
 		struct ldb_result *search_res = NULL;
+		static const struct drsuapi_DsReplicaCursorCtrEx empty_udv;
+		const struct drsuapi_DsReplicaCursorCtrEx *udv = NULL;
 
 		extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
 
+		if (req10->uptodateness_vector != NULL) {
+			udv = req10->uptodateness_vector;
+		} else {
+			udv = &empty_udv;
+		}
+
 		getnc_state->min_usn = req10->highwatermark.highest_usn;
+		for (i = 0; i < udv->count; i++) {
+			bool match;
+			const struct drsuapi_DsReplicaCursor *cur =
+				&udv->cursors[i];
+
+			match = GUID_equal(&invocation_id,
+					   &cur->source_dsa_invocation_id);
+			if (!match) {
+				continue;
+			}
+			if (cur->highest_usn > getnc_state->min_usn) {
+				getnc_state->min_usn = cur->highest_usn;
+			}
+			break;
+		}
 		getnc_state->max_usn = getnc_state->min_usn;
 
 		getnc_state->final_udv = talloc_zero(getnc_state,
-- 
1.9.1


From c768b79af0f73c822c1976ae3a371edc9fcdb3e9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 29 Nov 2016 11:12:22 +0100
Subject: [PATCH 18/19] getncchanges: implement DRSUAPI_DRS_GET_ANC more
 correctly

The most important case is the combination of
DRSUAPI_DRS_CRITICAL_ONLY and DRSUAPI_DRS_GET_ANC.

With DRSUAPI_DRS_GET_ANC we need to make sure all ancestors
included even if they're not marked with
isCriticalSystemObject=TRUE.

I guess we still don't behave exactly as Windows, but it's much
better than before and fixes the initial replication if
someone moved the administrator account to an OU.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Pair-Programmed-With: Bob Campbell <bobcampbell at catalyst.net.nz>

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Bob Campbell <bobcampbell at catalyst.net.nz>
---
 source4/rpc_server/drsuapi/getncchanges.c | 301 ++++++++++++++++++++++++++----
 1 file changed, 265 insertions(+), 36 deletions(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 45f00ea..09b6d89 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -37,9 +37,13 @@
 #include "lib/util/tsort.h"
 #include "auth/session.h"
 #include "dsdb/common/util.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "librpc/gen_ndr/ndr_misc.h"
 
 /* state of a partially completed getncchanges call */
 struct drsuapi_getncchanges_state {
+	struct db_context *anc_cache;
 	struct GUID *guids;
 	uint32_t num_records;
 	uint32_t num_processed;
@@ -748,16 +752,6 @@ struct drsuapi_changed_objects {
 };
 
 /*
-  sort the objects we send by tree order
- */
-static int site_res_cmp_anc_order(struct drsuapi_changed_objects *m1,
-				  struct drsuapi_changed_objects *m2,
-				  struct drsuapi_getncchanges_state *getnc_state)
-{
-	return ldb_dn_compare(m2->dn, m1->dn);
-}
-
-/*
   sort the objects we send first by uSNChanged
  */
 static int site_res_cmp_usn_order(struct drsuapi_changed_objects *m1,
@@ -1709,6 +1703,95 @@ static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_sta
 	}
 }
 
+static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
+						uint64_t max_usn,
+						struct drsuapi_DsReplicaHighWaterMark *hwm)
+{
+	uint64_t uSN = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
+
+	if (uSN > max_usn) {
+		/*
+		 * Only report the max_usn we had at the start
+		 * of the replication cycle.
+		 *
+		 * If this object has changed lately we better
+		 * let the destination dsa refetch the change.
+		 * This is better than the risk of loosing some
+		 * objects or linked attributes.
+		 */
+		return;
+	}
+
+	if (uSN <= hwm->tmp_highest_usn) {
+		return;
+	}
+
+	hwm->tmp_highest_usn = uSN;
+	hwm->reserved_usn = 0;
+}
+
+static WERROR dcesrv_drsuapi_anc_cache_add(struct db_context *anc_cache,
+					   const struct GUID *guid)
+{
+	enum ndr_err_code ndr_err;
+	uint8_t guid_buf[16] = { 0, };
+	DATA_BLOB b = {
+		.data = guid_buf,
+		.length = sizeof(guid_buf),
+	};
+	TDB_DATA key = {
+		.dptr = b.data,
+		.dsize = b.length,
+	};
+	TDB_DATA val = {
+		.dptr = NULL,
+		.dsize = 0,
+	};
+	NTSTATUS status;
+
+	ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
+			(ndr_push_flags_fn_t)ndr_push_GUID);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		return WERR_DS_DRA_INTERNAL_ERROR;
+	}
+
+	status = dbwrap_store(anc_cache, key, val, TDB_REPLACE);
+	if (!NT_STATUS_IS_OK(status)) {
+		return WERR_DS_DRA_INTERNAL_ERROR;
+	}
+
+	return WERR_OK;
+}
+
+static WERROR dcesrv_drsuapi_anc_cache_exists(struct db_context *anc_cache,
+					      const struct GUID *guid)
+{
+	enum ndr_err_code ndr_err;
+	uint8_t guid_buf[16] = { 0, };
+	DATA_BLOB b = {
+		.data = guid_buf,
+		.length = sizeof(guid_buf),
+	};
+	TDB_DATA key = {
+		.dptr = b.data,
+		.dsize = b.length,
+	};
+	bool exists;
+
+	ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
+			(ndr_push_flags_fn_t)ndr_push_GUID);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		return WERR_DS_DRA_INTERNAL_ERROR;
+	}
+
+	exists = dbwrap_exists(anc_cache, key);
+	if (!exists) {
+		return WERR_OBJECT_NOT_FOUND;
+	}
+
+	return WERR_OBJECT_NAME_EXISTS;
+}
+
 /* 
   drsuapi_DsGetNCChanges
 
@@ -2138,11 +2221,6 @@ allowed:
 		/* RID_ALLOC returns 3 objects in a fixed order */
 		if (req10->extended_op == DRSUAPI_EXOP_FSMO_RID_ALLOC) {
 			/* Do nothing */
-		} else if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) {
-			LDB_TYPESAFE_QSORT(changes,
-					   getnc_state->num_records,
-					   getnc_state,
-					   site_res_cmp_anc_order);
 		} else {
 			LDB_TYPESAFE_QSORT(changes,
 					   getnc_state->num_records,
@@ -2165,6 +2243,15 @@ allowed:
 
 		talloc_free(search_res);
 		talloc_free(changes);
+
+		if (req10->extended_op != DRSUAPI_EXOP_NONE) {
+			/* Do nothing */
+		} else if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) {
+			getnc_state->anc_cache = db_open_rbt(getnc_state);
+			if (getnc_state->anc_cache == NULL) {
+				return WERR_NOT_ENOUGH_MEMORY;
+			}
+		}
 	}
 
 	if (req10->uptodateness_vector) {
@@ -2256,7 +2343,7 @@ allowed:
 		     (r->out.ctr->ctr6.object_count < max_objects)
 		     && !max_wait_reached;
 	    i++) {
-		int uSN;
+		struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
 		struct drsuapi_DsReplicaObjectListItemEx *obj;
 		struct ldb_message *msg;
 		static const char * const msg_attrs[] = {
@@ -2268,6 +2355,7 @@ allowed:
 					    NULL };
 		struct ldb_result *msg_res;
 		struct ldb_dn *msg_dn;
+		const struct GUID *next_anc_guid = NULL;
 
 		obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
 		W_ERROR_HAVE_NO_MEMORY(obj);
@@ -2293,6 +2381,24 @@ allowed:
 
 		msg = msg_res->msgs[0];
 
+		/*
+		 * If it has already been added as an ancestor of
+		 * an object, we don't need to do anything more,
+		 * as we've already added the links.
+		 */
+		if (getnc_state->anc_cache != NULL) {
+			werr = dcesrv_drsuapi_anc_cache_exists(getnc_state->anc_cache,
+							       &getnc_state->guids[i]);
+			if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
+				dcesrv_drsuapi_update_highwatermark(msg,
+						getnc_state->max_usn,
+						&r->out.ctr->ctr6.new_highwatermark);
+				/* no attributes to send */
+				talloc_free(obj);
+				continue;
+			}
+		}
+
 		max_wait_reached = (time(NULL) - start > max_wait);
 
 		werr = get_nc_changes_build_object(obj, msg,
@@ -2322,23 +2428,9 @@ allowed:
 			return werr;
 		}
 
-		uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
-		if (uSN > getnc_state->max_usn) {
-			/*
-			 * Only report the max_usn we had at the start
-			 * of the replication cycle.
-			 *
-			 * If this object has changed lately we better
-			 * let the destination dsa refetch the change.
-			 * This is better than the risk of loosing some
-			 * objects or linked attributes.
-			 */
-			uSN = 0;
-		}
-		if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
-			r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
-			r->out.ctr->ctr6.new_highwatermark.reserved_usn = 0;
-		}
+		dcesrv_drsuapi_update_highwatermark(msg,
+					getnc_state->max_usn,
+					&r->out.ctr->ctr6.new_highwatermark);
 
 		if (obj->meta_data_ctr == NULL) {
 			DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
@@ -2348,10 +2440,147 @@ allowed:
 			continue;
 		}
 
-		r->out.ctr->ctr6.object_count++;
+		new_objs = obj;
+
+		if (getnc_state->anc_cache != NULL) {
+			werr = dcesrv_drsuapi_anc_cache_add(getnc_state->anc_cache,
+							    &getnc_state->guids[i]);
+			if (!W_ERROR_IS_OK(werr)) {
+				return werr;
+			}
+
+			next_anc_guid = obj->parent_object_guid;
+		}
+
+		while (next_anc_guid != NULL) {
+			struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
+			struct ldb_message *anc_msg = NULL;
+			struct ldb_result *anc_res = NULL;
+			struct ldb_dn *anc_dn = NULL;
 
-		*currentObject = obj;
-		currentObject = &obj->next_object;
+			werr = dcesrv_drsuapi_anc_cache_exists(getnc_state->anc_cache,
+							       next_anc_guid);
+			if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
+				/*
+				 * We don't need to send it twice.
+				 */
+				break;
+			}
+			if (W_ERROR_IS_OK(werr)) {
+				return WERR_INTERNAL_ERROR;
+			}
+			if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
+				return werr;
+			}
+			werr = WERR_OK;
+
+			anc_obj = talloc_zero(mem_ctx,
+					struct drsuapi_DsReplicaObjectListItemEx);
+			if (anc_obj == NULL) {
+				return WERR_NOT_ENOUGH_MEMORY;
+			}
+
+			anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
+					GUID_string(anc_obj, next_anc_guid));
+			if (anc_dn == NULL) {
+				return WERR_NOT_ENOUGH_MEMORY;
+			}
+
+			ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
+							      &anc_res, anc_dn,
+							      LDB_SCOPE_BASE,
+							      msg_attrs, NULL);
+			if (ret != LDB_SUCCESS) {
+				const char *anc_str = NULL;
+				const char *obj_str = NULL;
+
+				anc_str = ldb_dn_get_extended_linearized(anc_obj,
+									 anc_dn,
+									 1);
+				obj_str = ldb_dn_get_extended_linearized(anc_obj,
+									 msg->dn,
+									 1),
+
+				DBG_ERR("getncchanges: failed to fetch ANC "
+					"DN %s for DN %s - %s\n",
+					anc_str, obj_str,
+					ldb_errstring(sam_ctx));
+				return WERR_DS_DRA_INCONSISTENT_DIT;
+			}
+
+			anc_msg = anc_res->msgs[0];
+
+			werr = get_nc_changes_build_object(anc_obj, anc_msg,
+							   sam_ctx,
+							   getnc_state->ncRoot_dn,
+							   getnc_state->is_schema_nc,
+							   schema, &session_key,
+							   getnc_state->min_usn,
+							   req10->replica_flags,
+							   req10->partial_attribute_set,
+							   req10->uptodateness_vector,
+							   req10->extended_op,
+							   false, /* force_object_return */
+							   local_pas);
+			if (!W_ERROR_IS_OK(werr)) {
+				return werr;
+			}
+
+			werr = get_nc_changes_add_links(sam_ctx, getnc_state,
+							getnc_state->ncRoot_dn,
+							getnc_state->is_schema_nc,
+							schema, getnc_state->min_usn,
+							req10->replica_flags,
+							anc_msg,
+							&getnc_state->la_list,
+							&getnc_state->la_count,
+							req10->uptodateness_vector);
+			if (!W_ERROR_IS_OK(werr)) {
+				return werr;
+			}
+
+			/*
+			 * Regardless of if we actually use it or not,
+			 * we add it to the cache so we don't look at it again
+			 */
+			werr = dcesrv_drsuapi_anc_cache_add(getnc_state->anc_cache,
+							    next_anc_guid);
+			if (!W_ERROR_IS_OK(werr)) {
+				return werr;
+			}
+
+			/*
+			 * Any ancestors which are below the highwatermark
+			 * or uptodateness_vector shouldn't be added,
+			 * but we still look further up the
+			 * tree for ones which have been changed recently.
+			 */
+			if (anc_obj->meta_data_ctr != NULL) {
+				/*
+				 * prepend it to the list
+				 */
+				anc_obj->next_object = new_objs;
+				new_objs = anc_obj;
+			}
+
+			anc_msg = NULL;
+			TALLOC_FREE(anc_res);
+			TALLOC_FREE(anc_dn);
+
+			/*
+			 * We may need to resolve more...
+			 */
+			next_anc_guid = anc_obj->parent_object_guid;
+		}
+
+		*currentObject = new_objs;
+		while (new_objs != NULL) {
+			r->out.ctr->ctr6.object_count += 1;
+			if (new_objs->next_object == NULL) {
+				currentObject = &new_objs->next_object;
+			}
+			new_objs = new_objs->next_object;
+		}
 
 		DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));
 
-- 
1.9.1


From 01d483dbaef478b6b12b0eb95e759aaf68ccd2b0 Mon Sep 17 00:00:00 2001
From: Bob Campbell <bobcampbell at catalyst.net.nz>
Date: Mon, 12 Dec 2016 16:00:35 +1300
Subject: [PATCH 19/19] torture/drs: expand test for DRSUAPI_DRS_GET_ANC

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12398

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Bob Campbell <bobcampbell at catalyst.net.nz>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/drs/python/getnc_exop.py | 323 ++++++++++++++++++++++++++++---
 1 file changed, 301 insertions(+), 22 deletions(-)

diff --git a/source4/torture/drs/python/getnc_exop.py b/source4/torture/drs/python/getnc_exop.py
index 70fabfb..26786be 100644
--- a/source4/torture/drs/python/getnc_exop.py
+++ b/source4/torture/drs/python/getnc_exop.py
@@ -34,6 +34,7 @@ import drs_base
 from drs_base import AbstractLink
 
 import samba.tests
+import random
 
 import ldb
 from ldb import SCOPE_BASE
@@ -134,7 +135,67 @@ class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
         self.assertEqual(ctr6.linked_attributes, [])
         self.assertEqual(ctr6.drs_error[0], 0)
 
+    def test_do_single_repl(self):
+        """
+        Make sure that DRSU_EXOP_REPL_OBJ never replicates more than
+        one object, even when we use DRS_GET_ANC.
+        """
+
+        ou1 = "OU=get_anc1,%s" % self.ou
+        self.ldb_dc1.add({
+            "dn": ou1,
+            "objectclass": "organizationalUnit"
+            })
+        ou1_id = self._get_indentifier(self.ldb_dc1, ou1)
+        ou2 = "OU=get_anc2,%s" % ou1
+        self.ldb_dc1.add({
+            "dn": ou2,
+            "objectclass": "organizationalUnit"
+            })
+        ou2_id = self._get_indentifier(self.ldb_dc1, ou2)
+        dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2)
+        self.ldb_dc1.add({
+            "dn": dc3,
+            "objectclass": "computer",
+            "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT)
+            })
+        dc3_id = self._get_indentifier(self.ldb_dc1, dc3)
+
+        req8 = self._exop_req8(dest_dsa=None,
+                               invocation_id=self.ldb_dc1.get_invocation_id(),
+                               nc_dn_str=ou1,
+                               exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
+                               replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
+        (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
+        self._check_ctr6(ctr, [ou1])
+
+        # DRSUAPI_DRS_WRIT_REP means that we should only replicate the dn we give (dc3).
+        # DRSUAPI_DRS_GET_ANC means that we should also replicate its ancestors, but
+        # Windows doesn't do this if we use both.
+        req8 = self._exop_req8(dest_dsa=None,
+                               invocation_id=self.ldb_dc1.get_invocation_id(),
+                               nc_dn_str=dc3,
+                               exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
+                               replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP |
+                                             drsuapi.DRSUAPI_DRS_GET_ANC)
+        (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
+        self._check_ctr6(ctr, [dc3])
+
+        # Even though the ancestor of ou2 (ou1) has changed since last hwm, and we're
+        # sending DRSUAPI_DRS_GET_ANC, the expected response is that it will only try
+        # and replicate the single object still.
+        req8 = self._exop_req8(dest_dsa=None,
+                               invocation_id=self.ldb_dc1.get_invocation_id(),
+                               nc_dn_str=ou2,
+                               exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
+                               replica_flags=drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
+                                             drsuapi.DRSUAPI_DRS_GET_ANC)
+        (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
+        self._check_ctr6(ctr, [ou2])
+
     def test_link_utdv_hwm(self):
+        """Test verify the DRS_GET_ANC behavior."""
+
         ou1 = "OU=get_anc1,%s" % self.ou
         self.ldb_dc1.add({
             "dn": ou1,
@@ -154,6 +215,111 @@ class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
             "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT)
             })
         dc3_id = self._get_indentifier(self.ldb_dc1, dc3)
+
+        (hwm1, utdv1) = self._check_replication([ou1,ou2,dc3],
+                                                drsuapi.DRSUAPI_DRS_WRIT_REP)
+
+        self._check_replication([ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+
+        self._check_replication([ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        m = ldb.Message()
+        m.dn = ldb.Dn(self.ldb_dc1, ou1)
+        m["displayName"] = ldb.MessageElement("OU1", ldb.FLAG_MOD_ADD, "displayName")
+        self.ldb_dc1.modify(m)
+
+        (hwm2, utdv2) = self._check_replication([ou2,dc3,ou1],
+                            drsuapi.DRSUAPI_DRS_WRIT_REP)
+
+        self._check_replication([ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+
+        self._check_replication([ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([ou1],
+            drsuapi.DRSUAPI_DRS_WRIT_REP,
+            highwatermark=hwm1)
+
+        self._check_replication([ou1],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            highwatermark=hwm1)
+
+        self._check_replication([ou1],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            uptodateness_vector=utdv1)
+
+        m = ldb.Message()
+        m.dn = ldb.Dn(self.ldb_dc1, ou2)
+        m["displayName"] = ldb.MessageElement("OU2", ldb.FLAG_MOD_ADD, "displayName")
+        self.ldb_dc1.modify(m)
+
+        (hwm3, utdv3) = self._check_replication([dc3,ou1,ou2],
+                            drsuapi.DRSUAPI_DRS_WRIT_REP)
+
+        self._check_replication([ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+
+        self._check_replication([ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([ou1,ou2],
+            drsuapi.DRSUAPI_DRS_WRIT_REP,
+            highwatermark=hwm1)
+
+        self._check_replication([ou1,ou2],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            highwatermark=hwm1)
+
+        self._check_replication([ou1,ou2],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            uptodateness_vector=utdv1)
+
+        m = ldb.Message()
+        m.dn = ldb.Dn(self.ldb_dc1, self.ou)
+        m["displayName"] = ldb.MessageElement("OU", ldb.FLAG_MOD_ADD, "displayName")
+        self.ldb_dc1.modify(m)
+
+        (hwm4, utdv4) = self._check_replication([dc3,ou1,ou2,self.ou],
+                            drsuapi.DRSUAPI_DRS_WRIT_REP)
+
+        self._check_replication([self.ou,ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+
+        self._check_replication([self.ou,ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([self.ou,ou2],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            uptodateness_vector=utdv2)
+
         cn3 = "CN=get_anc3,%s" % ou2
         self.ldb_dc1.add({
             "dn": cn3,
@@ -161,69 +327,182 @@ class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
             })
         cn3_id = self._get_indentifier(self.ldb_dc1, cn3)
 
+        (hwm5, utdv5) = self._check_replication([dc3,ou1,ou2,self.ou,cn3],
+                            drsuapi.DRSUAPI_DRS_WRIT_REP)
+
+        self._check_replication([self.ou,ou1,ou2,dc3,cn3],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+
+        self._check_replication([self.ou,ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
         m = ldb.Message()
-        m.dn = ldb.Dn(self.ldb_dc1, ou1)
+        m.dn = ldb.Dn(self.ldb_dc1, ou2)
         m["managedBy"] = ldb.MessageElement(dc3, ldb.FLAG_MOD_ADD, "managedBy")
         self.ldb_dc1.modify(m)
-        ou1_managedBy_dc3 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
+        ou2_managedBy_dc3 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
+                                drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
+                                ou2_id.guid, dc3_id.guid)
+
+        (hwm6, utdv6) = self._check_replication([dc3,ou1,self.ou,cn3,ou2],
+                            drsuapi.DRSUAPI_DRS_WRIT_REP,
+                            expected_links=[ou2_managedBy_dc3])
+
+        # Can fail against Windows due to equal precedence of dc3, cn3
+        self._check_replication([self.ou,ou1,ou2,dc3,cn3],
+            drsuapi.DRSUAPI_DRS_WRIT_REP|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            expected_links=[ou2_managedBy_dc3])
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+
+        self._check_replication([self.ou,ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC)
+
+        self._check_replication([],
+                          drsuapi.DRSUAPI_DRS_WRIT_REP,
+                          uptodateness_vector=utdv5,
+                          expected_links=[ou2_managedBy_dc3])
+
+        self._check_replication([],
+                          drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+                          uptodateness_vector=utdv5)
+
+        self._check_replication([],
+                          drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+                          uptodateness_vector=utdv5)
+
+        m = ldb.Message()
+        m.dn = ldb.Dn(self.ldb_dc1, dc3)
+        m["managedBy"] = ldb.MessageElement(ou1, ldb.FLAG_MOD_ADD, "managedBy")
+        self.ldb_dc1.modify(m)
+        dc3_managedBy_ou1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
                                 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
-                                ou1_id.guid, dc3_id.guid)
+                                dc3_id.guid, ou1_id.guid)
 
-        (hwm0, utdv0) = self._check_replication([ou2,dc3,cn3,ou1],
+        (hwm7, utdv7) = self._check_replication([ou1,self.ou,cn3,ou2,dc3],
                             drsuapi.DRSUAPI_DRS_WRIT_REP,
-                            expected_links=[ou1_managedBy_dc3])
+                            expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1])
+
+        # Can fail against Windows due to equal precedence of dc3, cn3
+        #self._check_replication([self.ou,ou1,ou2,dc3,cn3],
+        #    drsuapi.DRSUAPI_DRS_WRIT_REP|
+        #    drsuapi.DRSUAPI_DRS_GET_ANC,
+        #    expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1])
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            expected_links=[dc3_managedBy_ou1])
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            expected_links=[dc3_managedBy_ou1])
+
+        self._check_replication([self.ou,ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            expected_links=[dc3_managedBy_ou1])
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
+            expected_links=[dc3_managedBy_ou1])
 
         m = ldb.Message()
         m.dn = ldb.Dn(self.ldb_dc1, dc3)
-        m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_ADD, "managedBy")
+        m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_REPLACE, "managedBy")
         self.ldb_dc1.modify(m)
+        dc3_managedBy_ou1.flags &= ~drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
         dc3_managedBy_ou2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
                                 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
                                 dc3_id.guid, ou2_id.guid)
 
+        (hwm8, utdv8) = self._check_replication([ou1,self.ou,cn3,ou2,dc3],
+                            drsuapi.DRSUAPI_DRS_WRIT_REP,
+                            expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2])
+
+        # Can fail against Windows due to equal precedence of dc3, cn3
+        #self._check_replication([self.ou,ou1,ou2,dc3,cn3],
+        #    drsuapi.DRSUAPI_DRS_WRIT_REP|
+        #    drsuapi.DRSUAPI_DRS_GET_ANC,
+        #    expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2])
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2])
+
+        self._check_replication([self.ou,ou1,ou2,dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
+            drsuapi.DRSUAPI_DRS_GET_ANC,
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2])
+
+        self._check_replication([dc3],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2])
+
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP,
-            expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            highwatermark=hwm7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP|
             drsuapi.DRSUAPI_DRS_GET_ANC,
-            expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            highwatermark=hwm7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
-            expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            highwatermark=hwm7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
             drsuapi.DRSUAPI_DRS_GET_ANC,
-            expected_links=[dc3_managedBy_ou2],
-            highwatermark=hwm0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            highwatermark=hwm7)
+
+        self._check_replication([],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            highwatermark=hwm7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP,
-            expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            uptodateness_vector=utdv7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_WRIT_REP|
             drsuapi.DRSUAPI_DRS_GET_ANC,
-            expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            uptodateness_vector=utdv7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
-            expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            uptodateness_vector=utdv7)
 
         self._check_replication([],
             drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
             drsuapi.DRSUAPI_DRS_GET_ANC,
-            expected_links=[dc3_managedBy_ou2],
-            uptodateness_vector=utdv0)
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            uptodateness_vector=utdv7)
+
+        self._check_replication([],
+            drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
+            more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
+            expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
+            uptodateness_vector=utdv7)
 
     def test_FSMONotOwner(self):
         """Test role transfer with against DC not owner of the role"""
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20170208/d3453e3f/signature.sig>


More information about the samba-technical mailing list