2 Unix SMB/CIFS implementation.
4 DRS Replica Information
6 Copyright (C) Erick Nogueira do Nascimento 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "dsdb/samdb/samdb.h"
25 #include "auth/auth.h"
26 #include "smbd/service.h"
27 #include "lib/events/events.h"
28 #include "lib/messaging/irpc.h"
29 #include "dsdb/kcc/kcc_service.h"
30 #include "lib/ldb/include/ldb_errors.h"
31 #include "../lib/util/dlinklist.h"
32 #include "librpc/gen_ndr/ndr_misc.h"
33 #include "librpc/gen_ndr/ndr_drsuapi.h"
34 #include "librpc/gen_ndr/ndr_drsblobs.h"
35 #include "param/param.h"
39 get cursors info for a specified DN
41 static WERROR kccdrs_replica_get_info_cursors(TALLOC_CTX *mem_ctx,
42 struct ldb_context *samdb,
43 struct drsuapi_DsReplicaGetInfo *r,
44 union drsuapi_DsReplicaInfo *reply,
49 if (!ldb_dn_validate(dn)) {
50 return WERR_INVALID_PARAMETER;
52 reply->cursors = talloc(mem_ctx, struct drsuapi_DsReplicaCursorCtr);
53 W_ERROR_HAVE_NO_MEMORY(reply->cursors);
55 reply->cursors->reserved = 0;
57 ret = dsdb_load_udv_v1(samdb, dn, reply->cursors, &reply->cursors->array, &reply->cursors->count);
58 if (ret != LDB_SUCCESS) {
59 return WERR_DS_DRA_BAD_NC;
65 get cursors2 info for a specified DN
67 static WERROR kccdrs_replica_get_info_cursors2(TALLOC_CTX *mem_ctx,
68 struct ldb_context *samdb,
69 struct drsuapi_DsReplicaGetInfo *r,
70 union drsuapi_DsReplicaInfo *reply,
75 if (!ldb_dn_validate(dn)) {
76 return WERR_INVALID_PARAMETER;
78 reply->cursors2 = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2Ctr);
79 W_ERROR_HAVE_NO_MEMORY(reply->cursors2);
81 ret = dsdb_load_udv_v2(samdb, dn, reply->cursors2, &reply->cursors2->array, &reply->cursors2->count);
82 if (ret != LDB_SUCCESS) {
83 return WERR_DS_DRA_BAD_NC;
86 reply->cursors2->enumeration_context = reply->cursors2->count;
91 get pending ops info for a specified DN
93 static WERROR kccdrs_replica_get_info_pending_ops(TALLOC_CTX *mem_ctx,
94 struct ldb_context *samdb,
95 struct drsuapi_DsReplicaGetInfo *r,
96 union drsuapi_DsReplicaInfo *reply,
99 struct timeval now = timeval_current();
101 if (!ldb_dn_validate(dn)) {
102 return WERR_INVALID_PARAMETER;
104 reply->pendingops = talloc(mem_ctx, struct drsuapi_DsReplicaOpCtr);
105 W_ERROR_HAVE_NO_MEMORY(reply->pendingops);
107 /* claim no pending ops for now */
108 reply->pendingops->time = timeval_to_nttime(&now);
109 reply->pendingops->count = 0;
110 reply->pendingops->array = NULL;
117 struct ncList *prev, *next;
121 Fill 'master_nc_list' with the master ncs hosted by this server
123 static WERROR get_master_ncs(TALLOC_CTX *mem_ctx, struct ldb_context *samdb,
124 const char *ntds_guid_str, struct ncList **master_nc_list)
126 const char *attrs[] = { "hasMasterNCs", NULL };
127 struct ldb_result *res;
128 struct ncList *nc_list = NULL;
129 struct ncList *nc_list_elem;
134 ret = ldb_search(samdb, mem_ctx, &res, ldb_get_config_basedn(samdb),
135 LDB_SCOPE_DEFAULT, attrs, "(objectguid=%s)", ntds_guid_str);
137 if (ret != LDB_SUCCESS) {
138 DEBUG(0,(__location__ ": Failed objectguid search - %s\n", ldb_errstring(samdb)));
139 return WERR_INTERNAL_ERROR;
142 if (res->count == 0) {
143 DEBUG(0,(__location__ ": Failed: objectguid=%s not found\n", ntds_guid_str));
144 return WERR_INTERNAL_ERROR;
147 for (i = 0; i < res->count; i++) {
148 struct ldb_message_element *msg_elem = ldb_msg_find_element(res->msgs[i], "hasMasterNCs");
151 if (!msg_elem || msg_elem->num_values == 0) {
152 DEBUG(0,(__location__ ": Failed: Attribute hasMasterNCs not found - %s\n",
153 ldb_errstring(samdb)));
154 return WERR_INTERNAL_ERROR;
157 for (k = 0; k < msg_elem->num_values; k++) {
158 int len = msg_elem->values[k].length;
160 /* copy the string on msg_elem->values[k]->data to nc_str */
161 nc_str = talloc_array(mem_ctx, char, len);
162 W_ERROR_HAVE_NO_MEMORY(nc_str);
163 memcpy(nc_str, msg_elem->values[k].data, len);
166 nc_list_elem = talloc_zero(mem_ctx, struct ncList);
167 W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
168 nc_list_elem->dn = ldb_dn_new(mem_ctx, samdb, nc_str);
169 W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
170 DLIST_ADD(nc_list, nc_list_elem);
175 *master_nc_list = nc_list;
180 Fill 'nc_list' with the ncs list. (MS-DRSR 4.1.13.3)
181 if the object dn is specified, fill 'nc_list' only with this dn
182 otherwise, fill 'nc_list' with all master ncs hosted by this server
184 static WERROR get_ncs_list(TALLOC_CTX *mem_ctx,
185 struct ldb_context *samdb,
186 struct kccsrv_service *service,
187 const char *object_dn_str,
188 struct ncList **nc_list)
191 struct ncList *nc_list_elem;
192 struct ldb_dn *nc_dn;
194 if (object_dn_str != NULL) {
195 /* ncs := { object_dn } */
197 nc_dn = ldb_dn_new(mem_ctx, samdb, object_dn_str);
198 nc_list_elem = talloc_zero(mem_ctx, struct ncList);
199 W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
200 nc_list_elem->dn = nc_dn;
201 DLIST_ADD_END(*nc_list, nc_list_elem, struct ncList*);
203 /* ncs := getNCs() from ldb database.
204 * getNCs() must return an array containing
205 * the DSNames of all NCs hosted by this
208 char *ntds_guid_str = GUID_string(mem_ctx, &service->ntds_guid);
209 W_ERROR_HAVE_NO_MEMORY(ntds_guid_str);
210 status = get_master_ncs(mem_ctx, samdb, ntds_guid_str, nc_list);
211 W_ERROR_NOT_OK_RETURN(status);
218 Copy the fields from 'reps1' to 'reps2', leaving zeroed the fields on
219 'reps2' that aren't available on 'reps1'.
221 static WERROR copy_repsfrom_1_to_2(TALLOC_CTX *mem_ctx,
222 struct repsFromTo2 **reps2,
223 struct repsFromTo1 *reps1)
225 struct repsFromTo2* reps;
227 reps = talloc_zero(mem_ctx, struct repsFromTo2);
228 W_ERROR_HAVE_NO_MEMORY(reps);
230 reps->blobsize = reps1->blobsize;
231 reps->consecutive_sync_failures = reps1->consecutive_sync_failures;
232 reps->last_attempt = reps1->last_attempt;
233 reps->last_success = reps1->last_success;
234 reps->other_info = talloc_zero(mem_ctx, struct repsFromTo2OtherInfo);
235 W_ERROR_HAVE_NO_MEMORY(reps->other_info);
236 reps->other_info->dns_name1 = reps1->other_info->dns_name;
237 reps->replica_flags = reps1->replica_flags;
238 memcpy(reps->schedule, reps1->schedule, sizeof(reps1->schedule));
239 reps->reserved = reps1->reserved;
240 reps->highwatermark = reps1->highwatermark;
241 reps->source_dsa_obj_guid = reps1->source_dsa_obj_guid;
242 reps->source_dsa_invocation_id = reps1->source_dsa_invocation_id;
243 reps->transport_guid = reps1->transport_guid;
249 static WERROR fill_neighbor_from_repsFrom(TALLOC_CTX *mem_ctx,
250 struct ldb_context *samdb,
251 struct ldb_dn *nc_dn,
252 struct drsuapi_DsReplicaNeighbour *neigh,
253 struct repsFromTo2 *reps_from)
255 struct ldb_dn *source_dsa_dn;
257 struct ldb_dn *transport_obj_dn = NULL;
259 neigh->source_dsa_address = reps_from->other_info->dns_name1;
260 neigh->replica_flags = reps_from->replica_flags;
261 neigh->last_attempt = reps_from->last_attempt;
262 neigh->source_dsa_obj_guid = reps_from->source_dsa_obj_guid;
264 ret = dsdb_find_dn_by_guid(samdb, mem_ctx, &reps_from->source_dsa_obj_guid, &source_dsa_dn);
266 if (ret != LDB_SUCCESS) {
267 DEBUG(0,(__location__ ": Failed to find DN for neighbor GUID %s\n",
268 GUID_string(mem_ctx, &reps_from->source_dsa_obj_guid)));
269 return WERR_DS_DRA_INTERNAL_ERROR;
272 neigh->source_dsa_obj_dn = ldb_dn_get_linearized(source_dsa_dn);
273 neigh->naming_context_dn = ldb_dn_get_linearized(nc_dn);
275 if (dsdb_find_guid_by_dn(samdb, nc_dn, &neigh->naming_context_obj_guid)
277 return WERR_DS_DRA_INTERNAL_ERROR;
280 if (!GUID_all_zero(&reps_from->transport_guid)) {
281 if (dsdb_find_dn_by_guid(samdb, mem_ctx, &reps_from->transport_guid,
282 &transport_obj_dn) != LDB_SUCCESS)
284 return WERR_DS_DRA_INTERNAL_ERROR;
288 neigh->transport_obj_dn = ldb_dn_get_linearized(transport_obj_dn);
289 neigh->source_dsa_invocation_id = reps_from->source_dsa_invocation_id;
290 neigh->transport_obj_guid = reps_from->transport_guid;
291 neigh->highest_usn = reps_from->highwatermark.highest_usn;
292 neigh->tmp_highest_usn = reps_from->highwatermark.tmp_highest_usn;
293 neigh->last_success = reps_from->last_success;
294 neigh->result_last_attempt = reps_from->result_last_attempt;
295 neigh->consecutive_sync_failures = reps_from->consecutive_sync_failures;
296 neigh->reserved = 0; /* Unused. MUST be 0. */
302 Get the inbound neighbours of this DC
303 See details on MS-DRSR 4.1.13.3, for infoType DS_REPL_INFO_NEIGHBORS
305 static WERROR kccdrs_replica_get_info_neighbours(TALLOC_CTX *mem_ctx,
306 struct kccsrv_service *service,
307 struct ldb_context *samdb,
308 struct drsuapi_DsReplicaGetInfo *r,
309 union drsuapi_DsReplicaInfo *reply,
311 struct GUID req_src_dsa_guid,
312 const char *object_dn_str)
316 struct ldb_dn *nc_dn = NULL;
317 struct ncList *p_nc_list = NULL;
318 struct repsFromToBlob *reps_from_blob = NULL;
319 struct repsFromTo2 *reps_from = NULL;
320 uint32_t c_reps_from;
322 struct drsuapi_DsReplicaNeighbour neigh;
323 struct ncList *nc_list = NULL;
325 status = get_ncs_list(mem_ctx, samdb, service, object_dn_str, &nc_list);
326 W_ERROR_NOT_OK_RETURN(status);
330 reply->neighbours = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbourCtr);
331 W_ERROR_HAVE_NO_MEMORY(reply->neighbours);
332 reply->neighbours->reserved = 0;
333 reply->neighbours->count = 0;
335 /* foreach nc in ncs */
336 for (p_nc_list = nc_list; p_nc_list != NULL; p_nc_list = p_nc_list->next) {
338 nc_dn = p_nc_list->dn;
340 /* load the nc's repsFromTo blob */
341 status = dsdb_loadreps(samdb, mem_ctx, nc_dn, "repsFrom",
342 &reps_from_blob, &c_reps_from);
343 W_ERROR_NOT_OK_RETURN(status);
345 /* foreach r in nc!repsFrom */
346 for (i_rep = 0; i_rep < c_reps_from; i_rep++) {
348 /* put all info on reps_from */
349 if (reps_from_blob[i_rep].version == 1) {
350 status = copy_repsfrom_1_to_2(mem_ctx, &reps_from,
351 &reps_from_blob[i_rep].ctr.ctr1);
352 W_ERROR_NOT_OK_RETURN(status);
353 } else { /* reps_from->version == 2 */
354 reps_from = &reps_from_blob[i_rep].ctr.ctr2;
357 if (GUID_all_zero(&req_src_dsa_guid) ||
358 GUID_compare(&req_src_dsa_guid, &reps_from->source_dsa_obj_guid) == 0)
361 if (i >= base_index) {
362 status = fill_neighbor_from_repsFrom(mem_ctx, samdb,
365 W_ERROR_NOT_OK_RETURN(status);
367 /* append the neighbour to the neighbours array */
368 reply->neighbours->array = talloc_realloc(mem_ctx,
369 reply->neighbours->array,
370 struct drsuapi_DsReplicaNeighbour,
371 reply->neighbours->count + 1);
372 reply->neighbours->array[reply->neighbours->count++] = neigh;
384 static WERROR fill_neighbor_from_repsTo(TALLOC_CTX *mem_ctx,
385 struct ldb_context *samdb, struct ldb_dn *nc_dn,
386 struct drsuapi_DsReplicaNeighbour *neigh,
387 struct repsFromTo2 *reps_to)
390 struct ldb_dn *source_dsa_dn;
392 neigh->source_dsa_address = reps_to->other_info->dns_name1;
393 neigh->replica_flags = reps_to->replica_flags;
394 neigh->last_attempt = reps_to->last_attempt;
395 neigh->source_dsa_obj_guid = reps_to->source_dsa_obj_guid;
397 ret = dsdb_find_dn_by_guid(samdb, mem_ctx, &reps_to->source_dsa_obj_guid, &source_dsa_dn);
398 if (ret != LDB_SUCCESS) {
399 DEBUG(0,(__location__ ": Failed to find DN for neighbor GUID %s\n",
400 GUID_string(mem_ctx, &reps_to->source_dsa_obj_guid)));
401 return WERR_DS_DRA_INTERNAL_ERROR;
404 neigh->source_dsa_obj_dn = ldb_dn_get_linearized(source_dsa_dn);
405 neigh->naming_context_dn = ldb_dn_get_linearized(nc_dn);
407 ret = dsdb_find_guid_by_dn(samdb, nc_dn,
408 &neigh->naming_context_obj_guid);
409 if (ret != LDB_SUCCESS) {
410 DEBUG(0,(__location__ ": Failed to find GUID for DN %s\n",
411 ldb_dn_get_linearized(nc_dn)));
412 return WERR_DS_DRA_INTERNAL_ERROR;
419 Get the outbound neighbours of this DC
420 See details on MS-DRSR 4.1.13.3, for infoType DS_REPL_INFO_REPSTO
422 static WERROR kccdrs_replica_get_info_repsto(TALLOC_CTX *mem_ctx,
423 struct kccsrv_service *service,
424 struct ldb_context *samdb,
425 struct drsuapi_DsReplicaGetInfo *r,
426 union drsuapi_DsReplicaInfo *reply,
428 struct GUID req_src_dsa_guid,
429 const char *object_dn_str)
433 struct ncList *p_nc_list = NULL;
434 struct ldb_dn *nc_dn = NULL;
435 struct repsFromToBlob *reps_to_blob;
436 struct repsFromTo2 *reps_to;
439 struct drsuapi_DsReplicaNeighbour neigh;
440 struct ncList *nc_list = NULL;
442 status = get_ncs_list(mem_ctx, samdb, service, object_dn_str, &nc_list);
443 W_ERROR_NOT_OK_RETURN(status);
447 reply->neighbours02 = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbourCtr);
448 W_ERROR_HAVE_NO_MEMORY(reply->neighbours02);
449 reply->neighbours02->reserved = 0;
450 reply->neighbours02->count = 0;
452 /* foreach nc in ncs */
453 for (p_nc_list = nc_list; p_nc_list != NULL; p_nc_list = p_nc_list->next) {
455 nc_dn = p_nc_list->dn;
457 status = dsdb_loadreps(samdb, mem_ctx, nc_dn, "repsTo",
458 &reps_to_blob, &c_reps_to);
459 W_ERROR_NOT_OK_RETURN(status);
461 /* foreach r in nc!repsTo */
462 for (i_rep = 0; i_rep < c_reps_to; i_rep++) {
464 /* put all info on reps_to */
465 if (reps_to_blob[i_rep].version == 1) {
466 status = copy_repsfrom_1_to_2(mem_ctx,
468 &reps_to_blob[i_rep].ctr.ctr1);
469 W_ERROR_NOT_OK_RETURN(status);
470 } else { /* reps_to->version == 2 */
471 reps_to = &reps_to_blob[i_rep].ctr.ctr2;
474 if (i >= base_index) {
475 status = fill_neighbor_from_repsTo(mem_ctx,
478 W_ERROR_NOT_OK_RETURN(status);
480 /* append the neighbour to the neighbours array */
481 reply->neighbours02->array = talloc_realloc(mem_ctx,
482 reply->neighbours02->array,
483 struct drsuapi_DsReplicaNeighbour,
484 reply->neighbours02->count + 1);
485 reply->neighbours02->array[reply->neighbours02->count++] = neigh;
495 NTSTATUS kccdrs_replica_get_info(struct irpc_message *msg,
496 struct drsuapi_DsReplicaGetInfo *req)
499 struct drsuapi_DsReplicaGetInfoRequest1 *req1;
500 struct drsuapi_DsReplicaGetInfoRequest2 *req2;
502 union drsuapi_DsReplicaInfo *reply;
503 struct GUID req_src_dsa_guid;
504 const char *object_dn_str = NULL;
505 struct kccsrv_service *service;
506 struct ldb_context *samdb;
508 enum drsuapi_DsReplicaInfoType info_type;
510 service = talloc_get_type(msg->private_data, struct kccsrv_service);
511 samdb = service->samdb;
512 mem_ctx = talloc_new(msg);
513 NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
515 NDR_PRINT_IN_DEBUG(drsuapi_DsReplicaGetInfo, req);
517 /* check request version */
518 if (req->in.level != DRSUAPI_DS_REPLICA_GET_INFO &&
519 req->in.level != DRSUAPI_DS_REPLICA_GET_INFO2)
521 DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo level %u\n",
523 status = WERR_REVISION_MISMATCH;
527 if (req->in.level == DRSUAPI_DS_REPLICA_GET_INFO) {
528 req1 = &req->in.req->req1;
530 info_type = req1->info_type;
531 object_dn_str = req1->object_dn;
532 req_src_dsa_guid = req1->guid1;
534 } else { /* r->in.level == DRSUAPI_DS_REPLICA_GET_INFO2 */
535 req2 = &req->in.req->req2;
536 if (req2->enumeration_context == 0xffffffff) {
537 /* no more data is available */
538 status = WERR_NO_MORE_ITEMS; /* on MS-DRSR it is ERROR_NO_MORE_ITEMS */
542 base_index = req2->enumeration_context;
543 info_type = req2->info_type;
544 object_dn_str = req2->object_dn;
545 req_src_dsa_guid = req2->guid1;
548 /* allocate the reply and fill in some fields */
549 reply = talloc_zero(mem_ctx, union drsuapi_DsReplicaInfo);
550 NT_STATUS_HAVE_NO_MEMORY(reply);
551 req->out.info = reply;
552 req->out.info_type = talloc(mem_ctx, enum drsuapi_DsReplicaInfoType);
553 NT_STATUS_HAVE_NO_MEMORY(req->out.info_type);
554 *req->out.info_type = info_type;
556 /* Based on the infoType requested, retrieve the corresponding
557 * information and construct the response message */
560 case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS:
561 status = kccdrs_replica_get_info_neighbours(mem_ctx, service, samdb, req,
562 reply, base_index, req_src_dsa_guid,
565 case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02: /* On MS-DRSR it is DS_REPL_INFO_REPSTO */
566 status = kccdrs_replica_get_info_repsto(mem_ctx, service, samdb, req,
567 reply, base_index, req_src_dsa_guid,
570 case DRSUAPI_DS_REPLICA_INFO_CURSORS: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_FOR_NC */
571 status = kccdrs_replica_get_info_cursors(mem_ctx, samdb, req, reply,
572 ldb_dn_new(mem_ctx, samdb, object_dn_str));
574 case DRSUAPI_DS_REPLICA_INFO_CURSORS2: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_2_FOR_NC */
575 status = kccdrs_replica_get_info_cursors2(mem_ctx, samdb, req, reply,
576 ldb_dn_new(mem_ctx, samdb, object_dn_str));
578 case DRSUAPI_DS_REPLICA_INFO_PENDING_OPS: /* On MS-DRSR it is DS_REPL_INFO_PENDING_OPS */
579 status = kccdrs_replica_get_info_pending_ops(mem_ctx, samdb, req, reply,
580 ldb_dn_new(mem_ctx, samdb, object_dn_str));
583 case DRSUAPI_DS_REPLICA_INFO_CURSORS3: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_3_FOR_NC */
584 case DRSUAPI_DS_REPLICA_INFO_CURSORS05: /* On MS-DRSR it is DS_REPL_INFO_UPTODATE_VECTOR_V1 */
585 case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */
586 case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */
587 case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_ATTR_VALUE */
588 case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE */
589 case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES */
590 case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_LINK_FAILURES */
591 case DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04: /* On MS-DRSR it is DS_REPL_INFO_CLIENT_CONTEXTS */
592 case DRSUAPI_DS_REPLICA_INFO_06: /* On MS-DRSR it is DS_REPL_INFO_SERVER_OUTGOING_CALLS */
594 DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo info_type %u\n",
596 status = WERR_INVALID_LEVEL;
601 /* put the status on the result field of the reply */
602 req->out.result = status;
603 NDR_PRINT_OUT_DEBUG(drsuapi_DsReplicaGetInfo, req);