#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;
}
}
+static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **msg,
+ struct ldb_dn *object_dn,
+ const struct dsdb_attribute *sa,
+ struct replPropertyMetaData1 *meta_data,
+ struct ldb_message *revealed_users)
+{
+ enum ndr_err_code ndr_err;
+ int ldb_err;
+ unsigned i;
+ char *attr_str = NULL;
+ char *attr_hex = NULL;
+ DATA_BLOB attr_blob;
+ struct ldb_message_element *existing = NULL, *el_add = NULL, *el_del = NULL;
+ const char * const * secret_attributes = ldb_get_opaque(sam_ctx, "LDB_SECRET_ATTRIBUTE_LIST");
+
+ if (!ldb_attr_in_list(secret_attributes,
+ sa->lDAPDisplayName)) {
+ return WERR_OK;
+ }
+
+
+ ndr_err = ndr_push_struct_blob(&attr_blob, mem_ctx, meta_data, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaData1);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ attr_hex = hex_encode_talloc(mem_ctx, attr_blob.data, attr_blob.length);
+ if (attr_hex == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ attr_str = talloc_asprintf(mem_ctx, "B:%zd:%s:%s", attr_blob.length*2, attr_hex, ldb_dn_get_linearized(object_dn));
+ if (attr_str == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
+ if (existing != NULL) {
+ /* Replace the old value (if one exists) with the current one */
+ for (i = 0; i < existing->num_values; i++) {
+ struct dsdb_dn *existing_dn = dsdb_dn_parse_trusted(mem_ctx, sam_ctx, &existing->values[i], DSDB_SYNTAX_BINARY_DN);
+ if (ldb_dn_compare(object_dn, existing_dn->dn) == 0) {
+ struct replPropertyMetaData1 existing_meta_data;
+ ndr_err = ndr_pull_struct_blob_all_noalloc(&existing_dn->extra_part,
+ &existing_meta_data,
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (existing_meta_data.attid == sa->attributeID_id) {
+ ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
+ if (ldb_err != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
+ if (el_del->values == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ el_del->values[0] = existing->values[i];
+ el_del->num_values = 1;
+ }
+ }
+ }
+ }
+
+ ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_ADD, &el_add);
+ if (ldb_err != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ el_add->values = talloc_array((*msg)->elements, struct ldb_val, 1);
+ if (el_add->values == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ el_add->values[0] = data_blob_string_const(attr_str);
+ el_add->num_values = 1;
+
+ return WERR_OK;
+}
+
/*
drsuapi_DsGetNCChanges for one object
*/
struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
enum drsuapi_DsExtendedOperation extended_op,
bool force_object_return,
- uint32_t *local_pas)
+ uint32_t *local_pas,
+ struct ldb_dn *machine_dn)
{
const struct ldb_val *md_value;
uint32_t i, n;
struct replPropertyMetaDataBlob md;
uint32_t rid = 0;
+ int ldb_err;
enum ndr_err_code ndr_err;
uint32_t *attids;
const char *rdn;
const struct dsdb_attribute *rdn_sa;
+ uint64_t uSNChanged;
unsigned int instanceType;
struct dsdb_syntax_ctx syntax_ctx;
+ struct ldb_result *res = NULL;
+ struct ldb_message *revealed_list_msg = NULL, *existing_revealed_list_msg = NULL;
+ WERROR werr;
+ int ret;
/* make dsdb sytanx context for conversions */
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;
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)) {
dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
+
+ if (extended_op == DRSUAPI_EXOP_REPL_SECRET) {
+ /* Get the existing revealed users for the destination */
+ static const char *machine_attrs[] = { "msDS-RevealedUsers", NULL };
+
+ revealed_list_msg = ldb_msg_new(sam_ctx);
+ if (revealed_list_msg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ revealed_list_msg->dn = machine_dn;
+
+ ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, 0);
+ if (ldb_err != LDB_SUCCESS || res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ existing_revealed_list_msg = res->msgs[0];
+ }
+
for (n=i=0; i<md.ctr.ctr1.count; i++) {
const struct dsdb_attribute *sa;
bool force_attribute = false;
force_attribute = true;
DEBUG(4,("Forcing attribute %s in %s\n",
sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ werr = getncchanges_update_revealed_list(sam_ctx, obj,
+ &revealed_list_msg,
+ msg->dn, sa,
+ &md.ctr.ctr1.array[i],
+ existing_revealed_list_msg);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
}
/* filter by uptodateness_vector */
}
/* filter by partial_attribute_set */
- if (partial_attribute_set) {
+ if (partial_attribute_set && !force_attribute) {
uint32_t *result = NULL;
BINARY_ARRAY_SEARCH_V(local_pas, partial_attribute_set->num_attids, sa->attributeID_id,
uint32_t_cmp, result);
obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
attids[n] = md.ctr.ctr1.array[i].attid;
+
n++;
}
+ if (revealed_list_msg != NULL) {
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction start - %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = ldb_modify(sam_ctx, revealed_list_msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to commit revealed links - %s\n",
+ ldb_errstring(sam_ctx)));
+ ldb_transaction_cancel(sam_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+
/* ignore it if its an empty change. Note that renames always
* change the 'name' attribute, so they won't be ignored by
* this
*/
for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
struct ldb_message_element *el;
- WERROR werr;
const struct dsdb_attribute *sa;
sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
{
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) {
}
}
+ if (uSNChanged <= highest_usn) {
+ return WERR_OK;
+ }
+
tmp_ctx = talloc_new(mem_ctx);
if (tmp_ctx == NULL) {
return WERR_NOT_ENOUGH_MEMORY;
return WERR_DS_DRA_INTERNAL_ERROR;
}
- if (local_usn < highest_usn) {
+ if (local_usn <= highest_usn) {
continue;
}
uint64_t usn;
};
-/*
- 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
*/
struct drsuapi_DsGetNCChangesRequest10 *req10,
struct dom_sid *user_sid,
struct drsuapi_DsGetNCChangesCtr6 *ctr6,
- bool has_get_all_changes)
+ bool has_get_all_changes,
+ struct ldb_dn **machine_dn)
{
struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
struct ldb_dn *obj_dn = NULL;
+ struct ldb_dn *ntds_dn = NULL, *server_dn = NULL;
struct ldb_dn *rodc_dn, *krbtgt_link_dn;
int ret;
- const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", NULL };
+ const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", "objectGUID", NULL };
const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
struct ldb_result *rodc_res, *obj_res;
const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids;
return WERR_DS_DRA_SOURCE_DISABLED;
}
+ /*
+ * Before we accept or deny, fetch the machine DN for the destination
+ * DSA GUID.
+ *
+ * If we are the RODC, we will check that this matches the SID.
+ */
+ ret = dsdb_find_dn_by_guid(b_state->sam_ctx_system, mem_ctx,
+ &req10->destination_dsa_guid, 0,
+ &ntds_dn);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ server_dn = ldb_dn_get_parent(mem_ctx, ntds_dn);
+ if (server_dn == NULL) {
+ goto failed;
+ }
+
+ ret = samdb_reference_dn(b_state->sam_ctx_system, mem_ctx, server_dn,
+ "serverReference", machine_dn);
+
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
/*
* In MS-DRSR.pdf 5.99 IsGetNCChangesPermissionGranted
*
goto allowed;
}
+ /*
+ * Must be an RODC account at this point, verify machine DN matches the
+ * SID account
+ */
+ if (ldb_dn_compare(rodc_res->msgs[0]->dn, *machine_dn) != 0) {
+ goto denied;
+ }
+
/* an RODC is allowed to get its own krbtgt account secrets */
krbtgt_link_dn = samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
rodc_res->msgs[0], "msDS-KrbTgtLink", NULL);
DEBUG(2,(__location__ ": Denied single object with secret replication for %s by RODC %s\n",
ldb_dn_get_linearized(obj_dn), ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
- return WERR_DS_DRA_ACCESS_DENIED;
+ return WERR_DS_DRA_SECRETS_DENIED;
allowed:
DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
return WERR_DS_DRA_BAD_DN;
}
-
/*
handle a DRSUAPI_EXOP_REPL_OBJ call
*/
}
}
+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
struct dsdb_schema_prefixmap *pfm_remote = NULL;
bool full = true;
uint32_t *local_pas = NULL;
+ struct ldb_dn *machine_dn = NULL; /* Only used for REPL SECRET EXOP */
DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
b_state = h->data;
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
- if (is_secret_request && req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
+ if (is_secret_request) {
werr = drs_security_access_check_nc_root(b_state->sam_ctx,
mem_ctx,
dce_call->conn->auth_state.session_info->security_token,
req10->naming_context,
GUID_DRS_GET_ALL_CHANGES);
if (!W_ERROR_IS_OK(werr)) {
- return werr;
+ /* Only bail if this is not a EXOP_REPL_SECRET */
+ if (req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
+ return werr;
+ }
} else {
has_get_all_changes = true;
}
werr = getncchanges_repl_secret(b_state, mem_ctx, req10,
user_sid,
&r->out.ctr->ctr6,
- has_get_all_changes);
+ has_get_all_changes,
+ &machine_dn);
r->out.result = werr;
W_ERROR_NOT_OK_RETURN(werr);
break;
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,
/* 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,
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) {
(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[] = {
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);
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,
req10->uptodateness_vector,
req10->extended_op,
max_wait_reached,
- local_pas);
+ local_pas, machine_dn);
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
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",
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,
+ machine_dn);
+ 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)));