From e72f8c6c0ea96bb2a70c265f96103ba5066b1ea6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 1 Dec 2016 11:49:07 +0100 Subject: [PATCH 01/19] s4:dsdb/repl: s/highestCommitedUsn/highestCommittedUSN Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett (cherry picked from commit 7888c250da03b0deaafed0932f9d6bcb1dd296a4) --- 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 37c7eb8b338da94f6019fd2922b2e1a09ff75757 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 1 Dec 2016 11:49:25 +0100 Subject: [PATCH 02/19] s4:libnet: s/highestCommitedUSN/highestCommittedUSN Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett (cherry picked from commit 2ef7594eca19b4448f04b138e97768965dc34242) --- 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 e42db51a1af343938a504ed7ee6a301cc064e461 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 0c77567a4e23bc87c249066319724413e9f9916e) --- 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 fbf0733867df8bbee680e3c53e14a3e13f847050 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit f5d3b863c75af5373a2044b184a8c9be1f32e60a) --- 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 b345497d37569230b58ab196f8a4c3f07b65eabb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 330ef9e572b9cf408ccb7721e86dc2afff245637) --- 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 7bb6e08013fe7312ed2e8bb4a9e3a2b1710ec2a7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit dd6d119c801076547fc0d087a2b959100146ba95) --- 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 3aaf197c5b3cb22f79eac7d1649961eb89376d26 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 41bc007d49e8bb304cdfb07231d402fa9ad828d7) --- 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 483c2371bea64badfd8619c19206f20467f263c7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit e935a04afb017ff52b6caf9bfa66888ab541c894) --- 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 ad9d9d587740fd0b8e99a672a36fae913993691e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 23e45b493803b3b2b548b4a3dbec0525feeb928f) --- 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 df6cc9e8b5b782ac95ae98f0722e6748638a2691 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 488eed6977e6bb04be2b1a736115d8887516ebbe) --- 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 d38ce2ef9401e21ab91b7181536d1d69e7d0e1e9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 1e15cdaa01fdee60f8bd57db41f062ace77c216d) --- 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 6a1f52266f97ee01eb4097f5447141dd17876c93 Mon Sep 17 00:00:00 2001 From: Garming Sam 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 Signed-off-by: Garming Sam Signed-off-by: Bob Campbell Reviewed-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett (cherry picked from commit 1a328bf404d9b3e6db4b2db963b186223297463a) --- 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; inum_elements; i++) { struct ldb_message_element *el = &msg->elements[i]; -- 1.9.1 From a7b145ec7ff28e8e1bda9c09d7b3244b2bc9c05e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 7d8c409792e93490bc4c357436200289df54d3ce) --- 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 507b1c5ee8704eecec7ea3fc252bdff5c08d36b0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 51386342d5f6df2d9aaf801a4137300569458f3d) --- 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 aff539414391c71c75e8b793f0ac8cadfcf001b7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit c31777a7010e8fb914ae8450a476d7f9ee2a4c39) --- 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 e7bc73a1e61114700a3269ae9c145bc70420c2c1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 02f11b925c44ecc0b333f1a5593b7d01dc05560a) --- 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 307278751489b9b5d06fc5faed382bebf94319df Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Reviewed-by: Andrew Bartlett (cherry picked from commit c61d0c8957bf4eade5da93f4f22ae5f3ce2e7403) --- 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 4b9fbba251a572711e12344053faefa8701396f1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher 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 Signed-off-by: Stefan Metzmacher Signed-off-by: Bob Campbell Reviewed-by: Andrew Bartlett (cherry picked from commit 5109e777f75670958d193a269bbb575cdf0a67ce) --- 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_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 f26d703d3db8887e9090152794e8a89f0ae271f9 Mon Sep 17 00:00:00 2001 From: Bob Campbell 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 Signed-off-by: Bob Campbell Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Thu Feb 9 03:16:09 CET 2017 on sn-devel-144 (cherry picked from commit 5c918e29429f4a0c5a4383b33eaa10a66dfdd2f2) --- 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