X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Frpc_server%2Fdrsuapi%2Fgetncchanges.c;h=cc41abbc2271001cc83e334f68e361d313385af3;hb=51289a6f9bf25189386dd3f66b5b547f02348508;hp=8c6fd669c06be9105dad661e47cc2bed0f5570d4;hpb=570237f0f331c3e05aac3054a98a41ab8f335738;p=samba.git diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c index 8c6fd669c06..cc41abbc227 100644 --- a/source4/rpc_server/drsuapi/getncchanges.c +++ b/source4/rpc_server/drsuapi/getncchanges.c @@ -32,18 +32,27 @@ #include "libcli/security/session.h" #include "rpc_server/drsuapi/dcesrv_drsuapi.h" #include "rpc_server/dcerpc_server_proto.h" +#include "rpc_server/common/sid_helper.h" #include "../libcli/drsuapi/drsuapi.h" #include "lib/util/binsearch.h" #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" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_DRS_REPL /* 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; struct ldb_dn *ncRoot_dn; + struct GUID ncRoot_guid; bool is_schema_nc; uint64_t min_usn; uint64_t max_usn; @@ -53,13 +62,15 @@ struct drsuapi_getncchanges_state { struct drsuapi_DsReplicaCursor2CtrEx *final_udv; struct drsuapi_DsReplicaLinkedAttribute *la_list; uint32_t la_count; - bool la_sorted; + struct la_for_sorting *la_sorted; uint32_t la_idx; }; +/* We must keep the GUIDs in NDR form for sorting */ struct la_for_sorting { struct drsuapi_DsReplicaLinkedAttribute *link; - struct GUID target_guid; + uint8_t target_guid[16]; + uint8_t source_guid[16]; }; static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1, @@ -86,7 +97,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; @@ -128,33 +139,306 @@ static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv, return true; } return false; - } -static int attid_cmp(enum drsuapi_DsAttributeId a1, enum drsuapi_DsAttributeId a2) +static int uint32_t_cmp(uint32_t a1, uint32_t a2) { if (a1 == a2) return 0; - return ((uint32_t)a1) > ((uint32_t)a2) ? 1 : -1; + return a1 > a2 ? 1 : -1; +} + +static int uint32_t_ptr_cmp(uint32_t *a1, uint32_t *a2, void *unused) +{ + if (*a1 == *a2) return 0; + return *a1 > *a2 ? 1 : -1; +} + +static WERROR getncchanges_attid_remote_to_local(const struct dsdb_schema *schema, + const struct dsdb_syntax_ctx *ctx, + enum drsuapi_DsAttributeId remote_attid_as_enum, + enum drsuapi_DsAttributeId *local_attid_as_enum, + const struct dsdb_attribute **_sa) +{ + WERROR werr; + const struct dsdb_attribute *sa = NULL; + + if (ctx->pfm_remote == NULL) { + DEBUG(7, ("No prefixMap supplied, falling back to local prefixMap.\n")); + goto fail; + } + + werr = dsdb_attribute_drsuapi_remote_to_local(ctx, + remote_attid_as_enum, + local_attid_as_enum, + _sa); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n")); + goto fail; + } + + return werr; +fail: + + sa = dsdb_attribute_by_attributeID_id(schema, remote_attid_as_enum); + if (sa == NULL) { + return WERR_DS_DRA_SCHEMA_MISMATCH; + } else { + if (local_attid_as_enum != NULL) { + *local_attid_as_enum = sa->attributeID_id; + } + if (_sa != NULL) { + *_sa = sa; + } + return WERR_OK; + } } /* - check if an attribute is in a partial_attribute_set + * Similar to function in repl_meta_data without the extra + * dependencies. */ -static bool check_partial_attribute_set(const struct dsdb_attribute *sa, - struct drsuapi_DsPartialAttributeSet *pas) +static WERROR get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el, + struct parsed_dn **pdn) { - enum drsuapi_DsAttributeId *result; - BINARY_ARRAY_SEARCH_V(pas->attids, pas->num_attids, (enum drsuapi_DsAttributeId)sa->attributeID_id, - attid_cmp, result); - return result != NULL; + /* Here we get a list of 'struct parsed_dns' without the parsing */ + int i; + *pdn = talloc_zero_array(mem_ctx, struct parsed_dn, + el->num_values); + if (!*pdn) { + return WERR_DS_DRA_INTERNAL_ERROR; + } + + for (i = 0; i < el->num_values; i++) { + (*pdn)[i].v = &el->values[i]; + } + + return WERR_OK; } +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 GUID *object_guid, + const struct dsdb_attribute *sa, + struct replPropertyMetaData1 *meta_data, + struct ldb_message *revealed_users) +{ + enum ndr_err_code ndr_err; + int ldb_err; + 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 */ + struct parsed_dn *link_dns; + struct parsed_dn *exact = NULL, *unused = NULL; + WERROR werr; + uint8_t attid[4]; + DATA_BLOB partial_meta; + + werr = get_parsed_dns_trusted(mem_ctx, existing, &link_dns); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + /* Construct a partial metadata blob to match on in the DB */ + SIVAL(attid, 0, sa->attributeID_id); + partial_meta.length = 4; + partial_meta.data = attid; + + /* Binary search using GUID and attribute id for uniqueness */ + ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values, + object_guid, object_dn, + partial_meta, 4, + &exact, &unused, + DSDB_SYNTAX_BINARY_DN, true); + + if (ldb_err != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed parsed DN find - %s\n", + ldb_errstring(sam_ctx))); + return WERR_DS_DRA_INTERNAL_ERROR; + } + + if (exact != NULL) { + /* Perform some verification of the blob */ + struct replPropertyMetaData1 existing_meta_data; + ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_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] = *exact->v; + el_del->num_values = 1; + } else { + return WERR_DS_DRA_INTERNAL_ERROR; + } + } + } + + 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; +} + +/* + * This function filter attributes for build_object based on the + * uptodatenessvector and partial attribute set. + * + * Any secret attributes are forced here for REPL_SECRET, and audited at this + * point with msDS-RevealedUsers. + */ +static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItemEx *obj, + struct replPropertyMetaDataBlob md, + struct ldb_context *sam_ctx, + const struct ldb_message *msg, + const struct GUID *guid, + uint32_t *count, + uint64_t highest_usn, + const struct dsdb_attribute *rdn_sa, + struct dsdb_schema *schema, + struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector, + struct drsuapi_DsPartialAttributeSet *partial_attribute_set, + uint32_t *local_pas, + uint32_t *attids, + bool exop_secret, + struct ldb_message **revealed_list_msg, + struct ldb_message *existing_revealed_list_msg) +{ + uint32_t i, n; + WERROR werr; + for (n=i=0; iattributeID_id) continue; + + sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid); + if (!sa) { + DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n", + (unsigned int)md.ctr.ctr1.array[i].attid, + ldb_dn_get_linearized(msg->dn))); + return WERR_DS_DRA_INTERNAL_ERROR; + } + + if (sa->linkID) { + struct ldb_message_element *el; + el = ldb_msg_find_element(msg, sa->lDAPDisplayName); + if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) { + /* don't send upgraded links inline */ + continue; + } + } + + if (exop_secret && + !dsdb_attr_in_rodc_fas(sa)) { + 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, guid, sa, + &md.ctr.ctr1.array[i], + existing_revealed_list_msg); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + } + + /* filter by uptodateness_vector */ + if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType && + !force_attribute && + udv_filter(uptodateness_vector, + &md.ctr.ctr1.array[i].originating_invocation_id, + md.ctr.ctr1.array[i].originating_usn)) { + continue; + } + + /* filter by 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); + if (result == NULL) { + continue; + } + } + + obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time; + obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version; + 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++; + } + + *count = n; + + return WERR_OK; +} /* 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, @@ -165,23 +449,32 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem struct drsuapi_DsPartialAttributeSet *partial_attribute_set, struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector, enum drsuapi_DsExtendedOperation extended_op, - bool force_object_return) + bool force_object_return, + uint32_t *local_pas, + struct ldb_dn *machine_dn, + const struct GUID *guid) { 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; + 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; @@ -212,6 +505,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)) { @@ -240,68 +538,84 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem obj->object.identifier = get_object_identifier(obj, msg); if (obj->object.identifier == NULL) { - return WERR_NOMEM; + return WERR_NOT_ENOUGH_MEMORY; } 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); - for (n=i=0; iattributeID_id) continue; + if (extended_op == DRSUAPI_EXOP_REPL_SECRET) { + /* Get the existing revealed users for the destination */ + struct ldb_message *revealed_list_msg = NULL; + struct ldb_message *existing_revealed_list_msg = NULL; + 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; - sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid); - if (!sa) { - DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n", - (unsigned int)md.ctr.ctr1.array[i].attid, - ldb_dn_get_linearized(msg->dn))); + 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; } - if (sa->linkID) { - struct ldb_message_element *el; - el = ldb_msg_find_element(msg, sa->lDAPDisplayName); - if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) { - /* don't send upgraded links inline */ - continue; - } + ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN); + if (ldb_err != LDB_SUCCESS || res->count != 1) { + ldb_transaction_cancel(sam_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; } - if (extended_op == DRSUAPI_EXOP_REPL_SECRET && - !dsdb_attr_in_rodc_fas(sa)) { - force_attribute = true; - DEBUG(4,("Forcing attribute %s in %s\n", - sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn))); + existing_revealed_list_msg = res->msgs[0]; + + werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, + guid, &n, highest_usn, + rdn_sa, schema, + uptodateness_vector, + partial_attribute_set, local_pas, + attids, + true, + &revealed_list_msg, + existing_revealed_list_msg); + if (!W_ERROR_IS_OK(werr)) { + ldb_transaction_cancel(sam_ctx); + return werr; } - /* filter by uptodateness_vector */ - if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType && - !force_attribute && - udv_filter(uptodateness_vector, - &md.ctr.ctr1.array[i].originating_invocation_id, - md.ctr.ctr1.array[i].originating_usn)) { - continue; + if (revealed_list_msg != NULL) { + ret = ldb_modify(sam_ctx, revealed_list_msg); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to alter revealed links - %s\n", + ldb_errstring(sam_ctx))); + ldb_transaction_cancel(sam_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; + } } - /* filter by partial_attribute_set */ - if (partial_attribute_set && !check_partial_attribute_set(sa, partial_attribute_set)) { - continue; + 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; + } + } else { + werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid, + &n, highest_usn, rdn_sa, + schema, uptodateness_vector, + partial_attribute_set, local_pas, + attids, + false, + NULL, + NULL); + if (!W_ERROR_IS_OK(werr)) { + return werr; } - - obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time; - obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version; - 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++; } /* ignore it if its an empty change. Note that renames always @@ -329,7 +643,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute, obj->object.attribute_ctr.num_attributes); if (obj->object.attribute_ctr.attributes == NULL) { - return WERR_NOMEM; + return WERR_NOT_ENOUGH_MEMORY; } /* @@ -338,7 +652,6 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem */ for (i=0; iobject.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]); @@ -405,10 +718,11 @@ 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) + uint32_t *la_count, + bool is_schema_nc) { struct drsuapi_DsReplicaLinkedAttribute *la; bool active; @@ -480,7 +794,7 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx, return WERR_OK; } } - la->attid = sa->attributeID_id; + la->attid = dsdb_attribute_get_attid(sa, is_schema_nc); la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0; status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION"); @@ -529,17 +843,34 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx, static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ldb_dn *ncRoot_dn, + bool is_schema_nc, 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) { unsigned int i; - TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - uint64_t uSNChanged = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1); + TALLOC_CTX *tmp_ctx = NULL; + 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 (!is_critical) { + return WERR_OK; + } + } + + if (uSNChanged <= highest_usn) { + 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]; @@ -562,8 +893,10 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx, for (j=0; jnum_values; j++) { struct dsdb_dn *dsdb_dn; uint64_t local_usn; - NTSTATUS status; + uint64_t originating_usn; + NTSTATUS status, status2; WERROR werr; + struct GUID originating_invocation_id; dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid); if (dsdb_dn == NULL) { @@ -588,12 +921,28 @@ 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; } - werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema, sa, msg, - dsdb_dn, la_list, la_count); + status = dsdb_get_extended_dn_guid(dsdb_dn->dn, + &originating_invocation_id, + "RMD_INVOCID"); + status2 = dsdb_get_extended_dn_uint64(dsdb_dn->dn, + &originating_usn, + "RMD_ORIGINATING_USN"); + + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2)) { + if (udv_filter(uptodateness_vector, + &originating_invocation_id, + originating_usn)) { + continue; + } + } + + werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema, + sa, msg, dsdb_dn, la_list, + la_count, is_schema_nc); if (!W_ERROR_IS_OK(werr)) { talloc_free(tmp_ctx); return werr; @@ -636,8 +985,8 @@ static int linked_attribute_compare(const struct la_for_sorting *la1, void *opaque) { int c; - c = GUID_compare(&la1->link->identifier->guid, - &la2->link->identifier->guid); + c = memcmp(la1->source_guid, + la2->source_guid, sizeof(la2->source_guid)); if (c != 0) { return c; } @@ -652,7 +1001,8 @@ static int linked_attribute_compare(const struct la_for_sorting *la1, DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1; } - return GUID_compare(&la1->target_guid, &la2->target_guid); + return memcmp(la1->target_guid, + la2->target_guid, sizeof(la2->target_guid)); } struct drsuapi_changed_objects { @@ -661,16 +1011,6 @@ struct drsuapi_changed_objects { 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 */ @@ -795,113 +1135,14 @@ static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state, talloc_free(ext_res); - DEBUG(2,("Allocated RID pool for server %s\n", - GUID_string(mem_ctx, &req10->destination_dsa_guid))); - - ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; - - return WERR_OK; -} - -/* - return an array of SIDs from a ldb_message given an attribute name - assumes the SIDs are in extended DN format - */ -static WERROR samdb_result_sid_array_dn(struct ldb_context *sam_ctx, - struct ldb_message *msg, - TALLOC_CTX *mem_ctx, - const char *attr, - const struct dom_sid ***sids) -{ - struct ldb_message_element *el; - unsigned int i; - - el = ldb_msg_find_element(msg, attr); - if (!el) { - *sids = NULL; - return WERR_OK; - } - - (*sids) = talloc_array(mem_ctx, const struct dom_sid *, el->num_values + 1); - W_ERROR_HAVE_NO_MEMORY(*sids); - - for (i=0; inum_values; i++) { - struct ldb_dn *dn = ldb_dn_from_ldb_val(mem_ctx, sam_ctx, &el->values[i]); - NTSTATUS status; - struct dom_sid *sid; - - sid = talloc(*sids, struct dom_sid); - W_ERROR_HAVE_NO_MEMORY(sid); - status = dsdb_get_extended_dn_sid(dn, sid, "SID"); - if (!NT_STATUS_IS_OK(status)) { - return WERR_INTERNAL_DB_CORRUPTION; - } - (*sids)[i] = sid; - } - (*sids)[i] = NULL; - - return WERR_OK; -} - - -/* - return an array of SIDs from a ldb_message given an attribute name - assumes the SIDs are in NDR form - */ -static WERROR samdb_result_sid_array_ndr(struct ldb_context *sam_ctx, - struct ldb_message *msg, - TALLOC_CTX *mem_ctx, - const char *attr, - const struct dom_sid ***sids) -{ - struct ldb_message_element *el; - unsigned int i; - - el = ldb_msg_find_element(msg, attr); - if (!el) { - *sids = NULL; - return WERR_OK; - } - - (*sids) = talloc_array(mem_ctx, const struct dom_sid *, el->num_values + 1); - W_ERROR_HAVE_NO_MEMORY(*sids); - - for (i=0; inum_values; i++) { - enum ndr_err_code ndr_err; - struct dom_sid *sid; - - sid = talloc(*sids, struct dom_sid); - W_ERROR_HAVE_NO_MEMORY(sid); + DEBUG(2,("Allocated RID pool for server %s\n", + GUID_string(mem_ctx, &req10->destination_dsa_guid))); - ndr_err = ndr_pull_struct_blob(&el->values[i], sid, sid, - (ndr_pull_flags_fn_t)ndr_pull_dom_sid); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - return WERR_INTERNAL_DB_CORRUPTION; - } - (*sids)[i] = sid; - } - (*sids)[i] = NULL; + ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; return WERR_OK; } -/* - see if any SIDs in list1 are in list2 - */ -static bool sid_list_match(const struct dom_sid **list1, const struct dom_sid **list2) -{ - unsigned int i, j; - /* do we ever have enough SIDs here to worry about O(n^2) ? */ - for (i=0; list1[i]; i++) { - for (j=0; list2[j]; j++) { - if (dom_sid_equal(list1[i], list2[j])) { - return true; - } - } - } - return false; -} - /* handle a DRSUAPI_EXOP_REPL_SECRET call */ @@ -910,17 +1151,21 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state, 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; + struct ldb_result *rodc_res = NULL, *obj_res = NULL; const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids; + const struct dom_sid *object_sid = NULL; WERROR werr; + const struct dom_sid *additional_sids[] = { NULL, NULL }; DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n", drs_ObjectIdentifier_to_string(mem_ctx, ncRoot))); @@ -936,7 +1181,32 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state, if (b_state->sam_ctx_system == NULL) { /* this operation needs system level access */ ctr6->extended_ret = DRSUAPI_EXOP_ERR_ACCESS_DENIED; - return WERR_DS_DRA_SOURCE_DISABLED; + return WERR_DS_DRA_ACCESS_DENIED; + } + + /* + * 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; } /* @@ -959,13 +1229,13 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state, * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC) * then you can do EXOP_REPL_SECRETS */ + obj_dn = drs_ObjectIdentifier_to_dn(mem_ctx, b_state->sam_ctx_system, ncRoot); + if (!ldb_dn_validate(obj_dn)) goto failed; + if (has_get_all_changes) { goto allowed; } - obj_dn = drs_ObjectIdentifier_to_dn(mem_ctx, b_state->sam_ctx_system, ncRoot); - if (!ldb_dn_validate(obj_dn)) goto failed; - rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "", dom_sid_string(mem_ctx, user_sid)); if (!ldb_dn_validate(rodc_dn)) goto failed; @@ -979,11 +1249,21 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state, if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed; /* if the object SID is equal to the user_sid, allow */ - if (dom_sid_equal(user_sid, - samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid"))) { + object_sid = samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid"); + if (dom_sid_equal(user_sid, object_sid)) { goto allowed; } + additional_sids[0] = object_sid; + + /* + * 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); @@ -1016,8 +1296,14 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state, goto denied; } + /* + * The SID list needs to include itself as well as the tokenGroups. + * + * TODO determine if sIDHistory is required for this check + */ werr = samdb_result_sid_array_ndr(b_state->sam_ctx_system, obj_res->msgs[0], - mem_ctx, "tokenGroups", &token_sids); + mem_ctx, "tokenGroups", &token_sids, + additional_sids, 1); if (!W_ERROR_IS_OK(werr) || token_sids==NULL) { goto denied; } @@ -1037,12 +1323,12 @@ denied: 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", ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC", - ldb_dn_get_linearized(rodc_res->msgs[0]->dn))); + ldb_dn_get_linearized(*machine_dn))); ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; req10->highwatermark.highest_usn = 0; return WERR_OK; @@ -1054,7 +1340,6 @@ failed: return WERR_DS_DRA_BAD_DN; } - /* handle a DRSUAPI_EXOP_REPL_OBJ call */ @@ -1179,11 +1464,13 @@ static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state, */ static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state *b_state, struct drsuapi_DsGetNCChangesRequest10 *req10, + struct dsdb_schema_prefixmap *pfm_remote, bool *is_secret_request) { enum drsuapi_DsExtendedOperation exop; uint32_t i; struct dsdb_schema *schema; + struct dsdb_syntax_ctx syntax_ctx; *is_secret_request = true; @@ -1217,14 +1504,24 @@ static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state } schema = dsdb_get_schema(b_state->sam_ctx, NULL); + dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema); + syntax_ctx.pfm_remote = pfm_remote; /* check the attributes they asked for */ for (i=0; ipartial_attribute_set->num_attids; i++) { const struct dsdb_attribute *sa; - sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set->attids[i]); - if (sa == NULL) { - return WERR_DS_DRA_SCHEMA_MISMATCH; + WERROR werr = getncchanges_attid_remote_to_local(schema, + &syntax_ctx, + req10->partial_attribute_set->attids[i], + NULL, + &sa); + + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0,(__location__": attid 0x%08X not found: %s\n", + req10->partial_attribute_set->attids[i], win_errstr(werr))); + return werr; } + if (!dsdb_attr_in_rodc_fas(sa)) { *is_secret_request = true; return WERR_OK; @@ -1235,10 +1532,18 @@ static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state /* check the extended attributes they asked for */ for (i=0; ipartial_attribute_set_ex->num_attids; i++) { const struct dsdb_attribute *sa; - sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set_ex->attids[i]); - if (sa == NULL) { - return WERR_DS_DRA_SCHEMA_MISMATCH; + WERROR werr = getncchanges_attid_remote_to_local(schema, + &syntax_ctx, + req10->partial_attribute_set_ex->attids[i], + NULL, + &sa); + + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0,(__location__": attid 0x%08X not found: %s\n", + req10->partial_attribute_set_ex->attids[i], win_errstr(werr))); + return werr; } + if (!dsdb_attr_in_rodc_fas(sa)) { *is_secret_request = true; return WERR_OK; @@ -1256,11 +1561,13 @@ static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state */ static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state, struct drsuapi_DsGetNCChangesRequest10 *req10, + struct dsdb_schema_prefixmap *pfm_remote, bool *is_gc_pas_request) { enum drsuapi_DsExtendedOperation exop; uint32_t i; struct dsdb_schema *schema; + struct dsdb_syntax_ctx syntax_ctx; exop = req10->extended_op; @@ -1285,14 +1592,24 @@ static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_stat } schema = dsdb_get_schema(b_state->sam_ctx, NULL); + dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema); + syntax_ctx.pfm_remote = pfm_remote; /* check the attributes they asked for */ for (i=0; ipartial_attribute_set->num_attids; i++) { const struct dsdb_attribute *sa; - sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set->attids[i]); - if (sa == NULL) { - return WERR_DS_DRA_SCHEMA_MISMATCH; + WERROR werr = getncchanges_attid_remote_to_local(schema, + &syntax_ctx, + req10->partial_attribute_set->attids[i], + NULL, + &sa); + + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0,(__location__": attid 0x%08X not found: %s\n", + req10->partial_attribute_set->attids[i], win_errstr(werr))); + return werr; } + if (!sa->isMemberOfPartialAttributeSet) { *is_gc_pas_request = false; return WERR_OK; @@ -1303,10 +1620,18 @@ static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_stat /* check the extended attributes they asked for */ for (i=0; ipartial_attribute_set_ex->num_attids; i++) { const struct dsdb_attribute *sa; - sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set_ex->attids[i]); - if (sa == NULL) { - return WERR_DS_DRA_SCHEMA_MISMATCH; + WERROR werr = getncchanges_attid_remote_to_local(schema, + &syntax_ctx, + req10->partial_attribute_set_ex->attids[i], + NULL, + &sa); + + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0,(__location__": attid 0x%08X not found: %s\n", + req10->partial_attribute_set_ex->attids[i], win_errstr(werr))); + return werr; } + if (!sa->isMemberOfPartialAttributeSet) { *is_gc_pas_request = false; return WERR_OK; @@ -1366,18 +1691,19 @@ 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; + + 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"); - - //getnc_state->min_usn = req10->highwatermark.highest_usn; - /* Construct response. */ search_filter = talloc_asprintf(mem_ctx, "(uSNChanged>=%llu)", @@ -1387,7 +1713,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); @@ -1561,7 +1887,7 @@ static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_sta (*search_res)->count); if ((*search_res)->msgs == NULL) { TALLOC_FREE(frame); - return WERR_NOMEM; + return WERR_NOT_ENOUGH_MEMORY; } @@ -1582,6 +1908,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 @@ -1592,7 +2007,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ { struct drsuapi_DsReplicaObjectIdentifier *ncRoot; int ret; - uint32_t i; + uint32_t i, k; struct dsdb_schema *schema; struct drsuapi_DsReplicaOIDMapping_Ctr *ctr; struct drsuapi_DsReplicaObjectListItemEx **currentObject; @@ -1600,7 +2015,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ DATA_BLOB session_key; WERROR werr; struct dcesrv_handle *h; - struct drsuapi_bind_state *b_state; + struct drsuapi_bind_state *b_state; struct drsuapi_getncchanges_state *getnc_state; struct drsuapi_DsGetNCChangesRequest10 *req10; uint32_t options; @@ -1610,7 +2025,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ uint32_t link_total = 0; uint32_t link_given = 0; struct ldb_dn *search_dn = NULL; - bool am_rodc, null_scope=false; + bool am_rodc; enum security_user_level security_level; struct ldb_context *sam_ctx; struct dom_sid *user_sid; @@ -1623,10 +2038,15 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ bool has_get_all_changes = false; struct GUID invocation_id; static const struct drsuapi_DsReplicaLinkedAttribute no_linked_attr; + 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; + /* sam_ctx_system is not present for non-administrator users */ sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx; invocation_id = *(samdb_ntds_invocation_id(sam_ctx)); @@ -1657,7 +2077,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ case 8: req10 = getncchanges_map_req8(mem_ctx, &r->in.req->req8); if (req10 == NULL) { - return WERR_NOMEM; + return WERR_NOT_ENOUGH_MEMORY; } break; case 10: @@ -1682,7 +2102,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) { return WERR_DS_DRA_INTERNAL_ERROR; } - + if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) && !(req10->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) { return WERR_DS_DRA_SOURCE_DISABLED; @@ -1691,7 +2111,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; /* all clients must have GUID_DRS_GET_CHANGES */ - werr = drs_security_access_check_nc_root(b_state->sam_ctx, + werr = drs_security_access_check_nc_root(sam_ctx, mem_ctx, dce_call->conn->auth_state.session_info->security_token, req10->naming_context, @@ -1700,14 +2120,40 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ return werr; } + if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) { + full = req10->partial_attribute_set == NULL && + req10->partial_attribute_set_ex == NULL; + } else { + full = (options & DRSUAPI_DRS_WRIT_REP) != 0; + } + + werr = dsdb_schema_pfm_from_drsuapi_pfm(&req10->mapping_ctr, true, + mem_ctx, &pfm_remote, NULL); + + /* We were supplied a partial attribute set, without the prefix map! */ + if (!full && !W_ERROR_IS_OK(werr)) { + if (req10->mapping_ctr.num_mappings == 0) { + /* + * Despite the fact MS-DRSR specifies that this shouldn't + * happen, Windows RODCs will in fact not provide a prefixMap. + */ + DEBUG(5,(__location__ ": Failed to provide a remote prefixMap," + " falling back to local prefixMap\n")); + } else { + DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n", + win_errstr(werr))); + return werr; + } + } + /* allowed if the GC PAS and client has GUID_DRS_GET_FILTERED_ATTRIBUTES */ - werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, &is_gc_pas_request); + werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, pfm_remote, &is_gc_pas_request); if (!W_ERROR_IS_OK(werr)) { return werr; } if (is_gc_pas_request) { - werr = drs_security_access_check_nc_root(b_state->sam_ctx, + werr = drs_security_access_check_nc_root(sam_ctx, mem_ctx, dce_call->conn->auth_state.session_info->security_token, req10->naming_context, @@ -1717,18 +2163,23 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ } } - werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10, &is_secret_request); + werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10, + pfm_remote, + &is_secret_request); if (!W_ERROR_IS_OK(werr)) { return werr; } - if (is_secret_request && req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) { - werr = drs_security_access_check_nc_root(b_state->sam_ctx, + if (is_secret_request) { + werr = drs_security_access_check_nc_root(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; } @@ -1773,8 +2224,8 @@ allowed: ldb_dn_get_linearized(new_dn), ldb_dn_get_linearized(getnc_state->ncRoot_dn), ldb_dn_get_linearized(getnc_state->last_dn))); - talloc_free(getnc_state); - getnc_state = NULL; + TALLOC_FREE(getnc_state); + b_state->getncchanges_state = NULL; } } @@ -1787,23 +2238,38 @@ allowed: ldb_dn_get_linearized(getnc_state->ncRoot_dn), (ret > 0) ? "older" : "newer", ldb_dn_get_linearized(getnc_state->last_dn))); - talloc_free(getnc_state); - getnc_state = NULL; + TALLOC_FREE(getnc_state); + b_state->getncchanges_state = NULL; } } if (getnc_state == NULL) { - getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state); - if (getnc_state == NULL) { - return WERR_NOMEM; + struct ldb_result *res = NULL; + const char *attrs[] = { + "instanceType", + "objectGuID", + NULL + }; + uint32_t nc_instanceType; + struct ldb_dn *ncRoot_dn; + + ncRoot_dn = drs_ObjectIdentifier_to_dn(mem_ctx, sam_ctx, ncRoot); + if (ncRoot_dn == NULL) { + return WERR_NOT_ENOUGH_MEMORY; } - b_state->getncchanges_state = getnc_state; - getnc_state->ncRoot_dn = drs_ObjectIdentifier_to_dn(getnc_state, sam_ctx, ncRoot); - /* find out if we are to replicate Schema NC */ - ret = ldb_dn_compare(getnc_state->ncRoot_dn, - ldb_get_schema_basedn(b_state->sam_ctx)); - getnc_state->is_schema_nc = (0 == ret); + ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, + ncRoot_dn, attrs, + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_SHOW_RECYCLED); + if (ret != LDB_SUCCESS) { + DBG_WARNING("Failed to find ncRoot_dn %s\n", + ldb_dn_get_linearized(ncRoot_dn)); + return WERR_DS_DRA_BAD_DN; + } + nc_instanceType = ldb_msg_find_attr_as_int(res->msgs[0], + "instanceType", + 0); if (req10->extended_op != DRSUAPI_EXOP_NONE) { r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; @@ -1817,6 +2283,16 @@ allowed: */ switch (req10->extended_op) { case DRSUAPI_EXOP_NONE: + if ((nc_instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) { + const char *dn_str + = ldb_dn_get_linearized(ncRoot_dn); + + DBG_NOTICE("Rejecting full replication on " + "not NC %s", dn_str); + + return WERR_DS_CANT_FIND_EXPECTED_NC; + } + break; case DRSUAPI_EXOP_FSMO_RID_ALLOC: werr = getncchanges_rid_alloc(b_state, mem_ctx, req10, &r->out.ctr->ctr6, &search_dn); @@ -1829,7 +2305,8 @@ allowed: 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; @@ -1866,6 +2343,27 @@ allowed: (unsigned)req10->extended_op)); return WERR_DS_DRA_NOT_SUPPORTED; } + + /* Initialize the state we'll store over the replication cycle */ + getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state); + if (getnc_state == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + b_state->getncchanges_state = getnc_state; + + getnc_state->ncRoot_dn = ncRoot_dn; + talloc_steal(getnc_state, ncRoot_dn); + + getnc_state->ncRoot_guid = samdb_result_guid(res->msgs[0], + "objectGUID"); + ncRoot->guid = getnc_state->ncRoot_guid; + + /* find out if we are to replicate Schema NC */ + ret = ldb_dn_compare_base(ldb_get_schema_basedn(sam_ctx), + ncRoot_dn); + getnc_state->is_schema_nc = (0 == ret); + + TALLOC_FREE(res); } if (!ldb_dn_validate(getnc_state->ncRoot_dn) || @@ -1875,6 +2373,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)) { @@ -1889,16 +2389,46 @@ 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"); - getnc_state->min_usn = req10->highwatermark.highest_usn; + if (req10->extended_op == DRSUAPI_EXOP_NONE) { + 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; + } + } else { + /* We do not want REPL_SECRETS or REPL_SINGLE to return empty-handed */ + udv = &empty_udv; + getnc_state->min_usn = 0; + } + getnc_state->max_usn = getnc_state->min_usn; getnc_state->final_udv = talloc_zero(getnc_state, struct drsuapi_DsReplicaCursor2CtrEx); if (getnc_state->final_udv == NULL) { - return WERR_NOMEM; + return WERR_NOT_ENOUGH_MEMORY; } werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn, getnc_state->final_udv); @@ -1941,11 +2471,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, @@ -1968,6 +2493,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) { @@ -1985,14 +2519,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); @@ -2033,13 +2563,36 @@ allowed: * 10 seconds by default. */ max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max work time", 10); + + if (req10->partial_attribute_set != NULL) { + struct dsdb_syntax_ctx syntax_ctx; + uint32_t j = 0; + + dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema); + syntax_ctx.pfm_remote = pfm_remote; + + local_pas = talloc_array(b_state, uint32_t, req10->partial_attribute_set->num_attids); + + for (j = 0; j < req10->partial_attribute_set->num_attids; j++) { + getncchanges_attid_remote_to_local(schema, + &syntax_ctx, + req10->partial_attribute_set->attids[j], + (enum drsuapi_DsAttributeId *)&local_pas[j], + NULL); + } + + LDB_TYPESAFE_QSORT(local_pas, + req10->partial_attribute_set->num_attids, + NULL, + uint32_t_ptr_cmp); + } + for (i=getnc_state->num_processed; inum_records && - !null_scope && (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[] = { @@ -2051,6 +2604,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); @@ -2059,8 +2613,13 @@ allowed: W_ERROR_HAVE_NO_MEMORY(msg_dn); - /* by re-searching here we avoid having a lot of full - * records in memory between calls to getncchanges + /* + * by re-searching here we avoid having a lot of full + * records in memory between calls to getncchanges. + * + * We expect that we may get some objects that vanish + * (tombstone expunge) between the first and second + * check. */ ret = drsuapi_search_with_extended_dn(sam_ctx, obj, &msg_res, msg_dn, @@ -2074,8 +2633,36 @@ allowed: continue; } + if (msg_res->count == 0) { + DEBUG(1,("getncchanges: got LDB_SUCCESS but failed" + "to get any results in fetch of DN " + "%s (race with tombstone expunge?)\n", + ldb_dn_get_extended_linearized(obj, + msg_dn, 1))); + talloc_free(obj); + continue; + } + 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, @@ -2086,13 +2673,16 @@ allowed: req10->partial_attribute_set, req10->uptodateness_vector, req10->extended_op, - max_wait_reached); + max_wait_reached, + local_pas, machine_dn, + &getnc_state->guids[i]); 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, msg, @@ -2103,23 +2693,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", @@ -2129,10 +2705,149 @@ 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; + + 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, + machine_dn, + next_anc_guid); + 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 = obj; - currentObject = &obj->next_object; + *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))); @@ -2145,8 +2860,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)) && @@ -2155,10 +2868,10 @@ allowed: DEBUG(3,("UpdateRefs on getncchanges for %s\n", GUID_string(mem_ctx, &req10->destination_dsa_guid))); ureq.naming_context = ncRoot; - ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(b_state->sam_ctx, mem_ctx, + ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(sam_ctx, mem_ctx, &req10->destination_dsa_guid); if (!ureq.dest_dsa_dns_name) { - return WERR_NOMEM; + return WERR_NOT_ENOUGH_MEMORY; } ureq.dest_dsa_guid = req10->destination_dsa_guid; ureq.options = DRSUAPI_DRS_ADD_REF | @@ -2170,7 +2883,9 @@ allowed: to send notifies using the GC SPN */ ureq.options |= (req10->replica_flags & DRSUAPI_DRS_REF_GCSPN); - werr = drsuapi_UpdateRefs(b_state, mem_ctx, &ureq); + werr = drsuapi_UpdateRefs(dce_call->msg_ctx, + dce_call->event_ctx, b_state, + mem_ctx, &ureq); if (!W_ERROR_IS_OK(werr)) { DEBUG(0,(__location__ ": Failed UpdateRefs on %s for %s in DsGetNCChanges - %s\n", drs_ObjectIdentifier_to_string(mem_ctx, ncRoot), ureq.dest_dsa_dns_name, @@ -2196,59 +2911,76 @@ allowed: r->out.ctr->ctr6.more_data = true; } else { /* sort the whole array the first time */ - if (!getnc_state->la_sorted) { + if (getnc_state->la_sorted == NULL) { int j; - struct la_for_sorting guid_array[getnc_state->la_count]; - struct drsuapi_DsReplicaLinkedAttribute tmp_array[getnc_state->la_count]; - TALLOC_CTX *tmp_ctx = talloc_new(sam_ctx); + struct la_for_sorting *guid_array = talloc_array(getnc_state, struct la_for_sorting, getnc_state->la_count); + if (guid_array == NULL) { + DEBUG(0, ("Out of memory allocating %u linked attributes for sorting", getnc_state->la_count)); + return WERR_NOT_ENOUGH_MEMORY; + } for (j = 0; j < getnc_state->la_count; j++) { /* we need to get the target GUIDs to compare */ struct dsdb_dn *dn; const struct drsuapi_DsReplicaLinkedAttribute *la = &getnc_state->la_list[j]; - struct GUID guid; const struct dsdb_attribute *schema_attrib; + const struct ldb_val *target_guid; + DATA_BLOB source_guid; + TALLOC_CTX *frame = talloc_stackframe(); schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid); - werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la->value.blob, &dn); + werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn); if (!W_ERROR_IS_OK(werr)) { DEBUG(0,(__location__ ": Bad la blob in sort\n")); - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return werr; } - status = dsdb_get_extended_dn_guid(dn->dn, &guid, "GUID"); - if (!NT_STATUS_IS_OK(status)) { + /* Extract the target GUID in NDR form */ + target_guid = ldb_dn_get_extended_component(dn->dn, "GUID"); + if (target_guid == NULL + || target_guid->length != sizeof(guid_array[0].target_guid)) { + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + } else { + /* Repack the source GUID as NDR for sorting */ + status = GUID_to_ndr_blob(&la->identifier->guid, + frame, + &source_guid); + } + + if (!NT_STATUS_IS_OK(status) + || source_guid.length != sizeof(guid_array[0].source_guid)) { DEBUG(0,(__location__ ": Bad la guid in sort\n")); - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return ntstatus_to_werror(status); } - guid_array[j] = (struct la_for_sorting) - { - .target_guid = guid, - .link = &getnc_state->la_list[j] - }; + guid_array[j].link = &getnc_state->la_list[j]; + memcpy(guid_array[j].target_guid, target_guid->data, + sizeof(guid_array[j].target_guid)); + memcpy(guid_array[j].source_guid, source_guid.data, + sizeof(guid_array[j].source_guid)); + TALLOC_FREE(frame); } - TALLOC_FREE(tmp_ctx); LDB_TYPESAFE_QSORT(guid_array, getnc_state->la_count, NULL, linked_attribute_compare); - - /* apply the sort to the original list */ - for (j = 0; j < getnc_state->la_count; j++) { - tmp_array[j] = *guid_array[j].link; - } - memcpy(getnc_state->la_list, tmp_array, - getnc_state->la_count * sizeof(struct drsuapi_DsReplicaLinkedAttribute)); - - getnc_state->la_sorted = true; + getnc_state->la_sorted = guid_array; } link_count = getnc_state->la_count - getnc_state->la_idx; link_count = MIN(max_links, link_count); r->out.ctr->ctr6.linked_attributes_count = link_count; - r->out.ctr->ctr6.linked_attributes = getnc_state->la_list + getnc_state->la_idx; + r->out.ctr->ctr6.linked_attributes = talloc_array(r->out.ctr, struct drsuapi_DsReplicaLinkedAttribute, link_count); + if (r->out.ctr->ctr6.linked_attributes == NULL) { + DEBUG(0, ("Out of memory allocating %u linked attributes for output", link_count)); + return WERR_NOT_ENOUGH_MEMORY; + } + + for (k = 0; k < link_count; k++) { + r->out.ctr->ctr6.linked_attributes[k] + = *getnc_state->la_sorted[getnc_state->la_idx + k].link; + } getnc_state->la_idx += link_count; link_given = getnc_state->la_idx; @@ -2258,6 +2990,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);