diff options
Diffstat (limited to 'source4/dsdb/kcc/kcc_drs_replica_info.c')
-rw-r--r-- | source4/dsdb/kcc/kcc_drs_replica_info.c | 911 |
1 files changed, 911 insertions, 0 deletions
diff --git a/source4/dsdb/kcc/kcc_drs_replica_info.c b/source4/dsdb/kcc/kcc_drs_replica_info.c new file mode 100644 index 0000000..edd0710 --- /dev/null +++ b/source4/dsdb/kcc/kcc_drs_replica_info.c @@ -0,0 +1,911 @@ +/* + Unix SMB/CIFS implementation. + + DRS Replica Information + + Copyright (C) Erick Nogueira do Nascimento 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "includes.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/common/proto.h" +#include "auth/auth.h" +#include "samba/service.h" +#include "lib/events/events.h" +#include "lib/messaging/irpc.h" +#include "dsdb/kcc/kcc_service.h" +#include <ldb_errors.h> +#include "../lib/util/dlinklist.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "param/param.h" +#include "dsdb/common/util.h" + + +/* + get the stamp values for the linked attribute 'linked_attr_name' of the object 'dn' +*/ +static WERROR get_linked_attribute_value_stamp(TALLOC_CTX *mem_ctx, struct ldb_context *samdb, + struct ldb_dn *dn, const char *linked_attr_name, + uint32_t *attr_version, NTTIME *attr_change_time, uint32_t *attr_orig_usn) +{ + struct ldb_result *res; + int ret; + const char *attrs[2]; + struct ldb_dn *attr_ext_dn; + NTSTATUS ntstatus; + + attrs[0] = linked_attr_name; + attrs[1] = NULL; + + ret = dsdb_search_dn(samdb, mem_ctx, &res, dn, attrs, + DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_REVEAL_INTERNALS); + if (ret != LDB_SUCCESS) { + DEBUG(0, (__location__ ": Failed search for attribute %s on %s", + linked_attr_name, ldb_dn_get_linearized(dn))); + return WERR_INTERNAL_ERROR; + } + + attr_ext_dn = ldb_msg_find_attr_as_dn(samdb, mem_ctx, res->msgs[0], linked_attr_name); + if (!attr_ext_dn) { + DEBUG(0, (__location__ ": Failed search for attribute %s on %s", + linked_attr_name, ldb_dn_get_linearized(dn))); + return WERR_INTERNAL_ERROR; + } + + DEBUG(0, ("linked_attr_name = %s, attr_ext_dn = %s", linked_attr_name, + ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); + + ntstatus = dsdb_get_extended_dn_uint32(attr_ext_dn, attr_version, "RMD_VERSION"); + if (!NT_STATUS_IS_OK(ntstatus)) { + DEBUG(0, (__location__ ": Could not extract component %s from dn \"%s\"", + "RMD_VERSION", ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); + return WERR_INTERNAL_ERROR; + } + + ntstatus = dsdb_get_extended_dn_nttime(attr_ext_dn, attr_change_time, "RMD_CHANGETIME"); + if (!NT_STATUS_IS_OK(ntstatus)) { + DEBUG(0, (__location__ ": Could not extract component %s from dn \"%s\"", + "RMD_CHANGETIME", ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); + return WERR_INTERNAL_ERROR; + } + + ntstatus = dsdb_get_extended_dn_uint32(attr_ext_dn, attr_version, "RMD_ORIGINATING_USN"); + if (!NT_STATUS_IS_OK(ntstatus)) { + DEBUG(0, (__location__ ": Could not extract component %s from dn \"%s\"", + "RMD_ORIGINATING_USN", ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); + return WERR_INTERNAL_ERROR; + } + + return WERR_OK; +} + +static WERROR get_repl_prop_metadata_ctr(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct ldb_dn *dn, + struct replPropertyMetaDataBlob *obj_metadata_ctr) +{ + int ret; + struct ldb_result *res; + const char *attrs[] = { "replPropertyMetaData", NULL }; + const struct ldb_val *omd_value; + enum ndr_err_code ndr_err; + + ret = ldb_search(samdb, mem_ctx, &res, dn, LDB_SCOPE_BASE, attrs, NULL); + if (ret != LDB_SUCCESS || res->count != 1) { + DEBUG(0, (__location__ ": Failed search for replPropertyMetaData attribute on %s", + ldb_dn_get_linearized(dn))); + return WERR_INTERNAL_ERROR; + } + + omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData"); + if (!omd_value) { + DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n", + ldb_dn_get_linearized(dn))); + talloc_free(res); + return WERR_INTERNAL_ERROR; + } + + ndr_err = ndr_pull_struct_blob(omd_value, mem_ctx, + obj_metadata_ctr, + (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n", + ldb_dn_get_linearized(dn))); + talloc_free(res); + return WERR_INTERNAL_ERROR; + } + + talloc_free(res); + return WERR_OK; +} + +/* + get the DN of the nTDSDSA object from the configuration partition + whose invocationId is 'invocation_id' + put the value on 'dn_str' +*/ +static WERROR get_dn_from_invocation_id(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct GUID *invocation_id, + const char **dn_str) +{ + char *invocation_id_str; + const char *attrs_invocation[] = { NULL }; + struct ldb_message *msg; + int ret; + + invocation_id_str = GUID_string(mem_ctx, invocation_id); + W_ERROR_HAVE_NO_MEMORY(invocation_id_str); + + ret = dsdb_search_one(samdb, invocation_id_str, &msg, ldb_get_config_basedn(samdb), LDB_SCOPE_SUBTREE, + attrs_invocation, 0, "(&(objectClass=nTDSDSA)(invocationId=%s))", invocation_id_str); + if (ret != LDB_SUCCESS) { + DEBUG(0, (__location__ ": Failed search for the object DN under %s whose invocationId is %s", + invocation_id_str, ldb_dn_get_linearized(ldb_get_config_basedn(samdb)))); + talloc_free(invocation_id_str); + return WERR_INTERNAL_ERROR; + } + + *dn_str = ldb_dn_alloc_linearized(mem_ctx, msg->dn); + talloc_free(invocation_id_str); + return WERR_OK; +} + +/* + get metadata version 2 info for a specified object DN +*/ +static WERROR kccdrs_replica_get_info_obj_metadata2(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct drsuapi_DsReplicaGetInfo *r, + union drsuapi_DsReplicaInfo *reply, + struct ldb_dn *dn, + uint32_t base_index) +{ + WERROR status; + struct replPropertyMetaDataBlob omd_ctr; + struct replPropertyMetaData1 *attr; + struct drsuapi_DsReplicaObjMetaData2Ctr *metadata2; + const struct dsdb_schema *schema; + + uint32_t i, j; + + DEBUG(0, ("kccdrs_replica_get_info_obj_metadata2() called\n")); + + if (!dn) { + return WERR_INVALID_PARAMETER; + } + + if (!ldb_dn_validate(dn)) { + return WERR_DS_DRA_BAD_DN; + } + + status = get_repl_prop_metadata_ctr(mem_ctx, samdb, dn, &omd_ctr); + W_ERROR_NOT_OK_RETURN(status); + + schema = dsdb_get_schema(samdb, reply); + if (!schema) { + DEBUG(0,(__location__": Failed to get the schema\n")); + return WERR_INTERNAL_ERROR; + } + + reply->objmetadata2 = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjMetaData2Ctr); + W_ERROR_HAVE_NO_MEMORY(reply->objmetadata2); + metadata2 = reply->objmetadata2; + metadata2->enumeration_context = 0; + + /* For each replicated attribute of the object */ + for (i = 0, j = 0; i < omd_ctr.ctr.ctr1.count; i++) { + const struct dsdb_attribute *schema_attr; + uint32_t attr_version; + NTTIME attr_change_time; + uint32_t attr_originating_usn = 0; + + /* + attr := attrsSeq[i] + s := AttrStamp(object, attr) + */ + /* get a reference to the attribute on 'omd_ctr' */ + attr = &omd_ctr.ctr.ctr1.array[j]; + + schema_attr = dsdb_attribute_by_attributeID_id(schema, attr->attid); + + DEBUG(0, ("attribute_id = %d, attribute_name: %s\n", attr->attid, schema_attr->lDAPDisplayName)); + + /* + if (attr in Link Attributes of object and + dwInVersion = 2 and DS_REPL_INFO_FLAG_IMPROVE_LINKED_ATTRS in msgIn.ulFlags) + */ + if (schema_attr && + schema_attr->linkID != 0 && /* Checks if attribute is a linked attribute */ + (schema_attr->linkID % 2) == 0 && /* is it a forward link? only forward links have the LinkValueStamp */ + r->in.level == 2 && + (r->in.req->req2.flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) /* on MS-DRSR it is DS_REPL_INFO_FLAG_IMPROVE_LINKED_ATTRS */ + { + /* + ls := LinkValueStamp of the most recent + value change in object!attr + */ + status = get_linked_attribute_value_stamp(mem_ctx, samdb, dn, schema_attr->lDAPDisplayName, + &attr_version, &attr_change_time, &attr_originating_usn); + W_ERROR_NOT_OK_RETURN(status); + + /* + Aligning to MS-DRSR 4.1.13.3: + 's' on the doc is 'attr->originating_change_time' here + 'ls' on the doc is 'attr_change_time' here + */ + + /* if (ls is more recent than s (based on order in which the change was applied on server)) then */ + if (attr_change_time > attr->originating_change_time) { + /* + Improve the stamp with the link value stamp. + s.dwVersion := ls.dwVersion + s.timeChanged := ls.timeChanged + s.uuidOriginating := NULLGUID + s.usnOriginating := ls.usnOriginating + */ + attr->version = attr_version; + attr->originating_change_time = attr_change_time; + attr->originating_invocation_id = GUID_zero(); + attr->originating_usn = attr_originating_usn; + } + } + + if (i < base_index) { + continue; + } + + metadata2->array = talloc_realloc(mem_ctx, metadata2->array, + struct drsuapi_DsReplicaObjMetaData2, j + 1); + W_ERROR_HAVE_NO_MEMORY(metadata2->array); + metadata2->array[j].attribute_name = schema_attr->lDAPDisplayName; + metadata2->array[j].local_usn = attr->local_usn; + metadata2->array[j].originating_change_time = attr->originating_change_time; + metadata2->array[j].originating_invocation_id = attr->originating_invocation_id; + metadata2->array[j].originating_usn = attr->originating_usn; + metadata2->array[j].version = attr->version; + + /* + originating_dsa_dn := GetDNFromInvocationID(originating_invocation_id) + GetDNFromInvocationID() should return the DN of the nTDSDSAobject that has the specified invocation ID + See MS-DRSR 4.1.13.3 and 4.1.13.2.1 + */ + status = get_dn_from_invocation_id(mem_ctx, samdb, + &attr->originating_invocation_id, + &metadata2->array[j].originating_dsa_dn); + W_ERROR_NOT_OK_RETURN(status); + j++; + metadata2->count = j; + + } + + return WERR_OK; +} + +/* + get cursors info for a specified DN +*/ +static WERROR kccdrs_replica_get_info_cursors(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct drsuapi_DsReplicaGetInfo *r, + union drsuapi_DsReplicaInfo *reply, + struct ldb_dn *dn) +{ + int ret; + + if (!ldb_dn_validate(dn)) { + return WERR_INVALID_PARAMETER; + } + reply->cursors = talloc(mem_ctx, struct drsuapi_DsReplicaCursorCtr); + W_ERROR_HAVE_NO_MEMORY(reply->cursors); + + reply->cursors->reserved = 0; + + ret = dsdb_load_udv_v1(samdb, dn, reply->cursors, &reply->cursors->array, &reply->cursors->count); + if (ret != LDB_SUCCESS) { + return WERR_DS_DRA_BAD_NC; + } + return WERR_OK; +} + +/* + get cursors2 info for a specified DN +*/ +static WERROR kccdrs_replica_get_info_cursors2(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct drsuapi_DsReplicaGetInfo *r, + union drsuapi_DsReplicaInfo *reply, + struct ldb_dn *dn) +{ + int ret; + + if (!ldb_dn_validate(dn)) { + return WERR_INVALID_PARAMETER; + } + reply->cursors2 = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2Ctr); + W_ERROR_HAVE_NO_MEMORY(reply->cursors2); + + ret = dsdb_load_udv_v2(samdb, dn, reply->cursors2, &reply->cursors2->array, &reply->cursors2->count); + if (ret != LDB_SUCCESS) { + return WERR_DS_DRA_BAD_NC; + } + + reply->cursors2->enumeration_context = reply->cursors2->count; + return WERR_OK; +} + +/* + get pending ops info for a specified DN +*/ +static WERROR kccdrs_replica_get_info_pending_ops(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct drsuapi_DsReplicaGetInfo *r, + union drsuapi_DsReplicaInfo *reply, + struct ldb_dn *dn) +{ + struct timeval now = timeval_current(); + + if (!ldb_dn_validate(dn)) { + return WERR_INVALID_PARAMETER; + } + reply->pendingops = talloc(mem_ctx, struct drsuapi_DsReplicaOpCtr); + W_ERROR_HAVE_NO_MEMORY(reply->pendingops); + + /* claim no pending ops for now */ + reply->pendingops->time = timeval_to_nttime(&now); + reply->pendingops->count = 0; + reply->pendingops->array = NULL; + + return WERR_OK; +} + +struct ncList { + struct ldb_dn *dn; + struct ncList *prev, *next; +}; + +/* + Fill 'master_nc_list' with the master ncs hosted by this server +*/ +static WERROR get_master_ncs(TALLOC_CTX *mem_ctx, struct ldb_context *samdb, + const char *ntds_guid_str, struct ncList **master_nc_list) +{ + const char *post_2003_attrs[] = { "msDS-hasMasterNCs", "hasPartialReplicaNCs", NULL }; + const char *pre_2003_attrs[] = { "hasMasterNCs", "hasPartialReplicaNCs", NULL }; + const char **attrs = post_2003_attrs; + struct ldb_result *res; + struct ncList *nc_list = NULL; + struct ncList *nc_list_elem; + int ret; + unsigned int i; + char *nc_str; + + /* In W2003 and greater, msDS-hasMasterNCs attribute lists the writable NC replicas */ + ret = ldb_search(samdb, mem_ctx, &res, ldb_get_config_basedn(samdb), + LDB_SCOPE_DEFAULT, post_2003_attrs, "(objectguid=%s)", ntds_guid_str); + + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed objectguid search - %s\n", ldb_errstring(samdb))); + + attrs = post_2003_attrs; + ret = ldb_search(samdb, mem_ctx, &res, ldb_get_config_basedn(samdb), + LDB_SCOPE_DEFAULT, pre_2003_attrs, "(objectguid=%s)", ntds_guid_str); + } + + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed objectguid search - %s\n", ldb_errstring(samdb))); + return WERR_INTERNAL_ERROR; + } + + if (res->count == 0) { + DEBUG(0,(__location__ ": Failed: objectguid=%s not found\n", ntds_guid_str)); + return WERR_INTERNAL_ERROR; + } + + for (i = 0; i < res->count; i++) { + struct ldb_message_element *msg_elem; + unsigned int k, a; + + for (a=0; attrs[a]; a++) { + msg_elem = ldb_msg_find_element(res->msgs[i], attrs[a]); + if (!msg_elem || msg_elem->num_values == 0) { + continue; + } + + for (k = 0; k < msg_elem->num_values; k++) { + /* copy the string on msg_elem->values[k]->data to nc_str */ + nc_str = talloc_strndup(mem_ctx, (char *)msg_elem->values[k].data, msg_elem->values[k].length); + W_ERROR_HAVE_NO_MEMORY(nc_str); + + nc_list_elem = talloc_zero(mem_ctx, struct ncList); + W_ERROR_HAVE_NO_MEMORY(nc_list_elem); + nc_list_elem->dn = ldb_dn_new(mem_ctx, samdb, nc_str); + W_ERROR_HAVE_NO_MEMORY(nc_list_elem); + DLIST_ADD(nc_list, nc_list_elem); + } + } + } + + *master_nc_list = nc_list; + return WERR_OK; +} + +/* + Fill 'nc_list' with the ncs list. (MS-DRSR 4.1.13.3) + if the object dn is specified, fill 'nc_list' only with this dn + otherwise, fill 'nc_list' with all master ncs hosted by this server +*/ +static WERROR get_ncs_list(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct kccsrv_service *service, + const char *object_dn_str, + struct ncList **nc_list) +{ + WERROR status; + struct ncList *nc_list_elem; + struct ldb_dn *nc_dn; + + if (object_dn_str != NULL) { + /* ncs := { object_dn } */ + *nc_list = NULL; + nc_dn = ldb_dn_new(mem_ctx, samdb, object_dn_str); + nc_list_elem = talloc_zero(mem_ctx, struct ncList); + W_ERROR_HAVE_NO_MEMORY(nc_list_elem); + nc_list_elem->dn = nc_dn; + DLIST_ADD_END(*nc_list, nc_list_elem); + } else { + /* ncs := getNCs() from ldb database. + * getNCs() must return an array containing + * the DSNames of all NCs hosted by this + * server. + */ + char *ntds_guid_str = GUID_string(mem_ctx, &service->ntds_guid); + W_ERROR_HAVE_NO_MEMORY(ntds_guid_str); + status = get_master_ncs(mem_ctx, samdb, ntds_guid_str, nc_list); + W_ERROR_NOT_OK_RETURN(status); + } + + return WERR_OK; +} + +/* + Copy the fields from 'reps1' to 'reps2', leaving zeroed the fields on + 'reps2' that aren't available on 'reps1'. +*/ +static WERROR copy_repsfrom_1_to_2(TALLOC_CTX *mem_ctx, + struct repsFromTo2 **reps2, + struct repsFromTo1 *reps1) +{ + struct repsFromTo2* reps; + + reps = talloc_zero(mem_ctx, struct repsFromTo2); + W_ERROR_HAVE_NO_MEMORY(reps); + + reps->blobsize = reps1->blobsize; + reps->consecutive_sync_failures = reps1->consecutive_sync_failures; + reps->last_attempt = reps1->last_attempt; + reps->last_success = reps1->last_success; + reps->result_last_attempt = reps1->result_last_attempt; + reps->other_info = talloc_zero(mem_ctx, struct repsFromTo2OtherInfo); + W_ERROR_HAVE_NO_MEMORY(reps->other_info); + reps->other_info->dns_name1 = reps1->other_info->dns_name; + reps->replica_flags = reps1->replica_flags; + memcpy(reps->schedule, reps1->schedule, sizeof(reps1->schedule)); + reps->reserved = reps1->reserved; + reps->highwatermark = reps1->highwatermark; + reps->source_dsa_obj_guid = reps1->source_dsa_obj_guid; + reps->source_dsa_invocation_id = reps1->source_dsa_invocation_id; + reps->transport_guid = reps1->transport_guid; + + *reps2 = reps; + return WERR_OK; +} + +static WERROR fill_neighbor_from_repsFrom(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct ldb_dn *nc_dn, + struct drsuapi_DsReplicaNeighbour *neigh, + struct repsFromTo2 *reps_from) +{ + struct ldb_dn *source_dsa_dn; + int ret; + struct ldb_dn *transport_obj_dn = NULL; + + neigh->source_dsa_address = reps_from->other_info->dns_name1; + neigh->replica_flags = reps_from->replica_flags; + neigh->last_attempt = reps_from->last_attempt; + neigh->source_dsa_obj_guid = reps_from->source_dsa_obj_guid; + + ret = dsdb_find_dn_by_guid(samdb, mem_ctx, &reps_from->source_dsa_obj_guid, + DSDB_SEARCH_SHOW_RECYCLED, + &source_dsa_dn); + + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to find DN for neighbor GUID %s\n", + GUID_string(mem_ctx, &reps_from->source_dsa_obj_guid))); + return WERR_DS_DRA_INTERNAL_ERROR; + } + + neigh->source_dsa_obj_dn = ldb_dn_get_linearized(source_dsa_dn); + neigh->naming_context_dn = ldb_dn_get_linearized(nc_dn); + + if (dsdb_find_guid_by_dn(samdb, nc_dn, + &neigh->naming_context_obj_guid) + != LDB_SUCCESS) { + return WERR_DS_DRA_INTERNAL_ERROR; + } + + if (!GUID_all_zero(&reps_from->transport_guid)) { + ret = dsdb_find_dn_by_guid(samdb, mem_ctx, &reps_from->transport_guid, + DSDB_SEARCH_SHOW_RECYCLED, + &transport_obj_dn); + if (ret != LDB_SUCCESS) { + return WERR_DS_DRA_INTERNAL_ERROR; + } + } + + neigh->transport_obj_dn = ldb_dn_get_linearized(transport_obj_dn); + neigh->source_dsa_invocation_id = reps_from->source_dsa_invocation_id; + neigh->transport_obj_guid = reps_from->transport_guid; + neigh->highest_usn = reps_from->highwatermark.highest_usn; + neigh->tmp_highest_usn = reps_from->highwatermark.tmp_highest_usn; + neigh->last_success = reps_from->last_success; + neigh->result_last_attempt = reps_from->result_last_attempt; + neigh->consecutive_sync_failures = reps_from->consecutive_sync_failures; + neigh->reserved = 0; /* Unused. MUST be 0. */ + + return WERR_OK; +} + +/* + Get the inbound neighbours of this DC + See details on MS-DRSR 4.1.13.3, for infoType DS_REPL_INFO_NEIGHBORS +*/ +static WERROR kccdrs_replica_get_info_neighbours(TALLOC_CTX *mem_ctx, + struct kccsrv_service *service, + struct ldb_context *samdb, + struct drsuapi_DsReplicaGetInfo *r, + union drsuapi_DsReplicaInfo *reply, + uint32_t base_index, + struct GUID req_src_dsa_guid, + const char *object_dn_str) +{ + WERROR status; + uint32_t i, j; + struct ldb_dn *nc_dn = NULL; + struct ncList *p_nc_list = NULL; + struct repsFromToBlob *reps_from_blob = NULL; + struct repsFromTo2 *reps_from = NULL; + uint32_t c_reps_from; + uint32_t i_rep; + struct ncList *nc_list = NULL; + + status = get_ncs_list(mem_ctx, samdb, service, object_dn_str, &nc_list); + W_ERROR_NOT_OK_RETURN(status); + + i = j = 0; + + reply->neighbours = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbourCtr); + W_ERROR_HAVE_NO_MEMORY(reply->neighbours); + reply->neighbours->reserved = 0; + reply->neighbours->count = 0; + + /* foreach nc in ncs */ + for (p_nc_list = nc_list; p_nc_list != NULL; p_nc_list = p_nc_list->next) { + + nc_dn = p_nc_list->dn; + + /* load the nc's repsFromTo blob */ + status = dsdb_loadreps(samdb, mem_ctx, nc_dn, "repsFrom", + &reps_from_blob, &c_reps_from); + W_ERROR_NOT_OK_RETURN(status); + + /* foreach r in nc!repsFrom */ + for (i_rep = 0; i_rep < c_reps_from; i_rep++) { + + /* put all info on reps_from */ + if (reps_from_blob[i_rep].version == 1) { + status = copy_repsfrom_1_to_2(mem_ctx, &reps_from, + &reps_from_blob[i_rep].ctr.ctr1); + W_ERROR_NOT_OK_RETURN(status); + } else { /* reps_from->version == 2 */ + reps_from = &reps_from_blob[i_rep].ctr.ctr2; + } + + if (GUID_all_zero(&req_src_dsa_guid) || + GUID_equal(&req_src_dsa_guid, + &reps_from->source_dsa_obj_guid)) { + + if (i >= base_index) { + struct drsuapi_DsReplicaNeighbour neigh; + ZERO_STRUCT(neigh); + status = fill_neighbor_from_repsFrom(mem_ctx, samdb, + nc_dn, &neigh, + reps_from); + W_ERROR_NOT_OK_RETURN(status); + + /* append the neighbour to the neighbours array */ + reply->neighbours->array = talloc_realloc(mem_ctx, + reply->neighbours->array, + struct drsuapi_DsReplicaNeighbour, + reply->neighbours->count + 1); + reply->neighbours->array[reply->neighbours->count] = neigh; + reply->neighbours->count++; + j++; + } + + i++; + } + } + } + + return WERR_OK; +} + +static WERROR fill_neighbor_from_repsTo(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, struct ldb_dn *nc_dn, + struct drsuapi_DsReplicaNeighbour *neigh, + struct repsFromTo2 *reps_to) +{ + int ret; + struct ldb_dn *source_dsa_dn; + + neigh->source_dsa_address = reps_to->other_info->dns_name1; + neigh->replica_flags = reps_to->replica_flags; + neigh->last_attempt = reps_to->last_attempt; + neigh->source_dsa_obj_guid = reps_to->source_dsa_obj_guid; + + ret = dsdb_find_dn_by_guid(samdb, mem_ctx, + &reps_to->source_dsa_obj_guid, + DSDB_SEARCH_SHOW_RECYCLED, + &source_dsa_dn); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to find DN for neighbor GUID %s\n", + GUID_string(mem_ctx, &reps_to->source_dsa_obj_guid))); + return WERR_DS_DRA_INTERNAL_ERROR; + } + + neigh->source_dsa_obj_dn = ldb_dn_get_linearized(source_dsa_dn); + neigh->naming_context_dn = ldb_dn_get_linearized(nc_dn); + + ret = dsdb_find_guid_by_dn(samdb, nc_dn, + &neigh->naming_context_obj_guid); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to find GUID for DN %s\n", + ldb_dn_get_linearized(nc_dn))); + return WERR_DS_DRA_INTERNAL_ERROR; + } + + neigh->last_success = reps_to->last_success; + neigh->result_last_attempt = reps_to->result_last_attempt; + neigh->consecutive_sync_failures = reps_to->consecutive_sync_failures; + return WERR_OK; +} + +/* + Get the outbound neighbours of this DC + See details on MS-DRSR 4.1.13.3, for infoType DS_REPL_INFO_REPSTO +*/ +static WERROR kccdrs_replica_get_info_repsto(TALLOC_CTX *mem_ctx, + struct kccsrv_service *service, + struct ldb_context *samdb, + struct drsuapi_DsReplicaGetInfo *r, + union drsuapi_DsReplicaInfo *reply, + uint32_t base_index, + struct GUID req_src_dsa_guid, + const char *object_dn_str) +{ + WERROR status; + uint32_t i, j; + struct ncList *p_nc_list = NULL; + struct ldb_dn *nc_dn = NULL; + struct repsFromToBlob *reps_to_blob; + struct repsFromTo2 *reps_to; + uint32_t c_reps_to; + uint32_t i_rep; + struct ncList *nc_list = NULL; + + status = get_ncs_list(mem_ctx, samdb, service, object_dn_str, &nc_list); + W_ERROR_NOT_OK_RETURN(status); + + i = j = 0; + + reply->repsto = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbourCtr); + W_ERROR_HAVE_NO_MEMORY(reply->repsto); + reply->repsto->reserved = 0; + reply->repsto->count = 0; + + /* foreach nc in ncs */ + for (p_nc_list = nc_list; p_nc_list != NULL; p_nc_list = p_nc_list->next) { + + nc_dn = p_nc_list->dn; + + status = dsdb_loadreps(samdb, mem_ctx, nc_dn, "repsTo", + &reps_to_blob, &c_reps_to); + W_ERROR_NOT_OK_RETURN(status); + + /* foreach r in nc!repsTo */ + for (i_rep = 0; i_rep < c_reps_to; i_rep++) { + struct drsuapi_DsReplicaNeighbour neigh; + ZERO_STRUCT(neigh); + + /* put all info on reps_to */ + if (reps_to_blob[i_rep].version == 1) { + status = copy_repsfrom_1_to_2(mem_ctx, + &reps_to, + &reps_to_blob[i_rep].ctr.ctr1); + W_ERROR_NOT_OK_RETURN(status); + } else { /* reps_to->version == 2 */ + reps_to = &reps_to_blob[i_rep].ctr.ctr2; + } + + if (i >= base_index) { + status = fill_neighbor_from_repsTo(mem_ctx, + samdb, nc_dn, + &neigh, reps_to); + W_ERROR_NOT_OK_RETURN(status); + + /* append the neighbour to the neighbours array */ + reply->repsto->array = talloc_realloc(mem_ctx, + reply->repsto->array, + struct drsuapi_DsReplicaNeighbour, + reply->repsto->count + 1); + reply->repsto->array[reply->repsto->count++] = neigh; + j++; + } + i++; + } + } + + return WERR_OK; +} + +NTSTATUS kccdrs_replica_get_info(struct irpc_message *msg, + struct drsuapi_DsReplicaGetInfo *req) +{ + WERROR status; + struct drsuapi_DsReplicaGetInfoRequest1 *req1; + struct drsuapi_DsReplicaGetInfoRequest2 *req2; + uint32_t base_index; + union drsuapi_DsReplicaInfo *reply; + struct GUID req_src_dsa_guid; + const char *object_dn_str = NULL; + struct kccsrv_service *service; + struct ldb_context *samdb; + TALLOC_CTX *mem_ctx; + enum drsuapi_DsReplicaInfoType info_type; + + service = talloc_get_type(msg->private_data, struct kccsrv_service); + samdb = service->samdb; + mem_ctx = talloc_new(msg); + NT_STATUS_HAVE_NO_MEMORY(mem_ctx); + +#if 0 + NDR_PRINT_IN_DEBUG(drsuapi_DsReplicaGetInfo, req); +#endif + + /* check request version */ + if (req->in.level != DRSUAPI_DS_REPLICA_GET_INFO && + req->in.level != DRSUAPI_DS_REPLICA_GET_INFO2) + { + DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo level %u\n", + req->in.level)); + status = WERR_REVISION_MISMATCH; + goto done; + } + + if (req->in.level == DRSUAPI_DS_REPLICA_GET_INFO) { + req1 = &req->in.req->req1; + base_index = 0; + info_type = req1->info_type; + object_dn_str = req1->object_dn; + req_src_dsa_guid = req1->source_dsa_guid; + } else { /* r->in.level == DRSUAPI_DS_REPLICA_GET_INFO2 */ + req2 = &req->in.req->req2; + if (req2->enumeration_context == 0xffffffff) { + /* no more data is available */ + status = WERR_NO_MORE_ITEMS; /* on MS-DRSR it is ERROR_NO_MORE_ITEMS */ + goto done; + } + + base_index = req2->enumeration_context; + info_type = req2->info_type; + object_dn_str = req2->object_dn; + req_src_dsa_guid = req2->source_dsa_guid; + } + + reply = req->out.info; + *req->out.info_type = info_type; + + /* Based on the infoType requested, retrieve the corresponding + * information and construct the response message */ + switch (info_type) { + + case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS: + status = kccdrs_replica_get_info_neighbours(mem_ctx, service, samdb, req, + reply, base_index, req_src_dsa_guid, + object_dn_str); + break; + case DRSUAPI_DS_REPLICA_INFO_REPSTO: + status = kccdrs_replica_get_info_repsto(mem_ctx, service, samdb, req, + reply, base_index, req_src_dsa_guid, + object_dn_str); + break; + case DRSUAPI_DS_REPLICA_INFO_CURSORS: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_FOR_NC */ + status = kccdrs_replica_get_info_cursors(mem_ctx, samdb, req, reply, + ldb_dn_new(mem_ctx, samdb, object_dn_str)); + break; + case DRSUAPI_DS_REPLICA_INFO_CURSORS2: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_2_FOR_NC */ + status = kccdrs_replica_get_info_cursors2(mem_ctx, samdb, req, reply, + ldb_dn_new(mem_ctx, samdb, object_dn_str)); + break; + case DRSUAPI_DS_REPLICA_INFO_PENDING_OPS: + status = kccdrs_replica_get_info_pending_ops(mem_ctx, samdb, req, reply, + ldb_dn_new(mem_ctx, samdb, object_dn_str)); + break; + case DRSUAPI_DS_REPLICA_INFO_CURSORS3: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_3_FOR_NC */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1: /* On MS-DRSR it is DS_REPL_INFO_UPTODATE_VECTOR_V1 */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */ + /* + * It should be too complicated to filter the metadata2 to remove the additional data + * as metadata2 is a superset of metadata + */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */ + status = kccdrs_replica_get_info_obj_metadata2(mem_ctx, samdb, req, reply, + ldb_dn_new(mem_ctx, samdb, object_dn_str), base_index); + break; + case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_ATTR_VALUE */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_LINK_FAILURES */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS: /* On MS-DRSR it is DS_REPL_INFO_CLIENT_CONTEXTS */ + status = WERR_NOT_SUPPORTED; + break; + case DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS: /* On MS-DRSR it is DS_REPL_INFO_SERVER_OUTGOING_CALLS */ + status = WERR_NOT_SUPPORTED; + break; + default: + DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo info_type %u\n", + info_type)); + status = WERR_INVALID_LEVEL; + break; + } + +done: + /* put the status on the result field of the reply */ + req->out.result = status; +#if 0 + NDR_PRINT_OUT_DEBUG(drsuapi_DsReplicaGetInfo, req); +#endif + return NT_STATUS_OK; +} |