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

Stefan Metzmacher metze at samba.org
Wed Nov 30 06:12:00 UTC 2016


Hi Andrew,

here's a patch to fix https://bugzilla.samba.org/show_bug.cgi?id=12398

The problem is that the combination DRSUAPI_DRS_CRITICAL_ONLY and
DRSUAPI_DRS_GET_ANC. E.g. if the administrator account was moved
to an OU, samba-tool domain join DC doesn't work, as the server
doesn't include all ancestors.

Please review and push.

Thanks!
metze
-------------- next part --------------
From 8e7b2da0ba5842362138d23f155fba9af6f1555d 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] s4:rpc_server/drsuapi: 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 the administrator account to an OU.

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 | 209 +++++++++++++++++++++++++++---
 1 file changed, 191 insertions(+), 18 deletions(-)

diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 70ec04c..d9bb23a 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -37,6 +37,9 @@
 #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 {
@@ -707,16 +710,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,
@@ -1667,6 +1660,68 @@ static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_sta
 	}
 }
 
+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
 
@@ -1711,6 +1766,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
 	struct dsdb_schema_prefixmap *pfm_remote = NULL;
 	bool full = true;
 	uint32_t *local_pas = NULL;
+	struct db_context *anc_cache = NULL;
 
 	DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
 	b_state = h->data;
@@ -2058,11 +2114,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,
@@ -2174,6 +2225,16 @@ allowed:
 				   uint32_t_ptr_cmp);
 	}
 
+	/* 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) {
+		anc_cache = db_open_rbt(mem_ctx);
+		if (anc_cache == NULL) {
+			return WERR_NOT_ENOUGH_MEMORY;
+		}
+	}
+
 	for (i=getnc_state->num_processed;
 	     i<getnc_state->num_records &&
 		     !null_scope &&
@@ -2181,6 +2242,7 @@ allowed:
 		     && !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[] = {
@@ -2192,6 +2254,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);
@@ -2272,10 +2335,118 @@ allowed:
 			continue;
 		}
 
-		r->out.ctr->ctr6.object_count++;
+		new_objs = obj;
 
-		*currentObject = obj;
-		currentObject = &obj->next_object;
+		if (anc_cache != NULL) {
+			werr = dcesrv_drsuapi_anc_cache_add(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;
+
+			werr = dcesrv_drsuapi_anc_cache_exists(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,
+							   true, /* ??? max_wait_reached */
+							   local_pas);
+			if (!W_ERROR_IS_OK(werr)) {
+				return werr;
+			}
+
+			werr = dcesrv_drsuapi_anc_cache_add(anc_cache,
+					next_anc_guid);
+			if (!W_ERROR_IS_OK(werr)) {
+				return werr;
+			}
+
+			/*
+			 * prepend it to the list
+			 */
+			anc_obj->next_object = new_objs;
+			new_objs = anc_obj;
+
+			/*
+			 * 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)));
 
@@ -2418,6 +2589,8 @@ allowed:
 		}
 	}
 
+	TALLOC_FREE(anc_cache);
+
 	if (!r->out.ctr->ctr6.more_data) {
 		talloc_steal(mem_ctx, getnc_state->la_list);
 
-- 
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/20161130/383e651e/signature.sig>


More information about the samba-technical mailing list