drepl_server: Allow refresh of partitions on UpdateRef
[Samba.git] / source4 / rpc_server / drsuapi / getncchanges.c
blob1f02f2c0936ae7b298a6f9176d492015e348f840
1 /*
2 Unix SMB/CIFS implementation.
4 implement the DSGetNCChanges call
6 Copyright (C) Anatoliy Atanasov 2009
7 Copyright (C) Andrew Tridgell 2009-2010
8 Copyright (C) Andrew Bartlett 2010-2016
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "rpc_server/dcerpc_server.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "param/param.h"
28 #include "librpc/gen_ndr/ndr_drsblobs.h"
29 #include "librpc/gen_ndr/ndr_drsuapi.h"
30 #include "librpc/gen_ndr/ndr_security.h"
31 #include "libcli/security/security.h"
32 #include "libcli/security/session.h"
33 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
34 #include "rpc_server/dcerpc_server_proto.h"
35 #include "../libcli/drsuapi/drsuapi.h"
36 #include "lib/util/binsearch.h"
37 #include "lib/util/tsort.h"
38 #include "auth/session.h"
39 #include "dsdb/common/util.h"
40 #include "lib/dbwrap/dbwrap.h"
41 #include "lib/dbwrap/dbwrap_rbt.h"
42 #include "librpc/gen_ndr/ndr_misc.h"
44 /* state of a partially completed getncchanges call */
45 struct drsuapi_getncchanges_state {
46 struct db_context *anc_cache;
47 struct GUID *guids;
48 uint32_t num_records;
49 uint32_t num_processed;
50 struct ldb_dn *ncRoot_dn;
51 struct GUID ncRoot_guid;
52 bool is_schema_nc;
53 uint64_t min_usn;
54 uint64_t max_usn;
55 struct drsuapi_DsReplicaHighWaterMark last_hwm;
56 struct ldb_dn *last_dn;
57 struct drsuapi_DsReplicaHighWaterMark final_hwm;
58 struct drsuapi_DsReplicaCursor2CtrEx *final_udv;
59 struct drsuapi_DsReplicaLinkedAttribute *la_list;
60 uint32_t la_count;
61 struct la_for_sorting *la_sorted;
62 uint32_t la_idx;
65 /* We must keep the GUIDs in NDR form for sorting */
66 struct la_for_sorting {
67 struct drsuapi_DsReplicaLinkedAttribute *link;
68 uint8_t target_guid[16];
69 uint8_t source_guid[16];
72 static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1,
73 const struct drsuapi_DsReplicaHighWaterMark *h2)
75 if (h1->highest_usn < h2->highest_usn) {
76 return -1;
77 } else if (h1->highest_usn > h2->highest_usn) {
78 return 1;
79 } else if (h1->tmp_highest_usn < h2->tmp_highest_usn) {
80 return -1;
81 } else if (h1->tmp_highest_usn > h2->tmp_highest_usn) {
82 return 1;
83 } else if (h1->reserved_usn < h2->reserved_usn) {
84 return -1;
85 } else if (h1->reserved_usn > h2->reserved_usn) {
86 return 1;
89 return 0;
93 build a DsReplicaObjectIdentifier from a ldb msg
95 static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
96 const struct ldb_message *msg)
98 struct drsuapi_DsReplicaObjectIdentifier *identifier;
99 struct dom_sid *sid;
101 identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
102 if (identifier == NULL) {
103 return NULL;
106 identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
107 identifier->guid = samdb_result_guid(msg, "objectGUID");
109 sid = samdb_result_dom_sid(identifier, msg, "objectSid");
110 if (sid) {
111 identifier->sid = *sid;
112 } else {
113 ZERO_STRUCT(identifier->sid);
115 return identifier;
118 static int udv_compare(const struct GUID *guid1, struct GUID guid2)
120 return GUID_compare(guid1, &guid2);
124 see if we can filter an attribute using the uptodateness_vector
126 static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
127 const struct GUID *originating_invocation_id,
128 uint64_t originating_usn)
130 const struct drsuapi_DsReplicaCursor *c;
131 if (udv == NULL) return false;
132 BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id,
133 originating_invocation_id, udv_compare, c);
134 if (c && originating_usn <= c->highest_usn) {
135 return true;
137 return false;
140 static int uint32_t_cmp(uint32_t a1, uint32_t a2)
142 if (a1 == a2) return 0;
143 return a1 > a2 ? 1 : -1;
146 static int uint32_t_ptr_cmp(uint32_t *a1, uint32_t *a2, void *unused)
148 if (*a1 == *a2) return 0;
149 return *a1 > *a2 ? 1 : -1;
152 static WERROR getncchanges_attid_remote_to_local(const struct dsdb_schema *schema,
153 const struct dsdb_syntax_ctx *ctx,
154 enum drsuapi_DsAttributeId remote_attid_as_enum,
155 enum drsuapi_DsAttributeId *local_attid_as_enum,
156 const struct dsdb_attribute **_sa)
158 WERROR werr;
159 const struct dsdb_attribute *sa = NULL;
161 if (ctx->pfm_remote == NULL) {
162 DEBUG(7, ("No prefixMap supplied, falling back to local prefixMap.\n"));
163 goto fail;
166 werr = dsdb_attribute_drsuapi_remote_to_local(ctx,
167 remote_attid_as_enum,
168 local_attid_as_enum,
169 _sa);
170 if (!W_ERROR_IS_OK(werr)) {
171 DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n"));
172 goto fail;
175 return werr;
176 fail:
178 sa = dsdb_attribute_by_attributeID_id(schema, remote_attid_as_enum);
179 if (sa == NULL) {
180 return WERR_DS_DRA_SCHEMA_MISMATCH;
181 } else {
182 if (local_attid_as_enum != NULL) {
183 *local_attid_as_enum = sa->attributeID_id;
185 if (_sa != NULL) {
186 *_sa = sa;
188 return WERR_OK;
193 * Similar to function in repl_meta_data without the extra
194 * dependencies.
196 static WERROR get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el,
197 struct parsed_dn **pdn)
199 /* Here we get a list of 'struct parsed_dns' without the parsing */
200 int i;
201 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
202 el->num_values);
203 if (!*pdn) {
204 return WERR_DS_DRA_INTERNAL_ERROR;
207 for (i = 0; i < el->num_values; i++) {
208 (*pdn)[i].v = &el->values[i];
211 return WERR_OK;
214 static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
215 TALLOC_CTX *mem_ctx,
216 struct ldb_message **msg,
217 struct ldb_dn *object_dn,
218 const struct GUID *object_guid,
219 const struct dsdb_attribute *sa,
220 struct replPropertyMetaData1 *meta_data,
221 struct ldb_message *revealed_users)
223 enum ndr_err_code ndr_err;
224 int ldb_err;
225 char *attr_str = NULL;
226 char *attr_hex = NULL;
227 DATA_BLOB attr_blob;
228 struct ldb_message_element *existing = NULL, *el_add = NULL, *el_del = NULL;
229 const char * const * secret_attributes = ldb_get_opaque(sam_ctx, "LDB_SECRET_ATTRIBUTE_LIST");
231 if (!ldb_attr_in_list(secret_attributes,
232 sa->lDAPDisplayName)) {
233 return WERR_OK;
237 ndr_err = ndr_push_struct_blob(&attr_blob, mem_ctx, meta_data, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaData1);
238 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
239 return WERR_DS_DRA_INTERNAL_ERROR;
242 attr_hex = hex_encode_talloc(mem_ctx, attr_blob.data, attr_blob.length);
243 if (attr_hex == NULL) {
244 return WERR_NOT_ENOUGH_MEMORY;
247 attr_str = talloc_asprintf(mem_ctx, "B:%zd:%s:%s", attr_blob.length*2, attr_hex, ldb_dn_get_linearized(object_dn));
248 if (attr_str == NULL) {
249 return WERR_NOT_ENOUGH_MEMORY;
252 existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
253 if (existing != NULL) {
254 /* Replace the old value (if one exists) with the current one */
255 struct parsed_dn *link_dns;
256 struct parsed_dn *exact = NULL, *unused = NULL;
257 WERROR werr;
258 uint8_t attid[4];
259 DATA_BLOB partial_meta;
261 werr = get_parsed_dns_trusted(mem_ctx, existing, &link_dns);
262 if (!W_ERROR_IS_OK(werr)) {
263 return werr;
266 /* Construct a partial metadata blob to match on in the DB */
267 SIVAL(attid, 0, sa->attributeID_id);
268 partial_meta.length = 4;
269 partial_meta.data = attid;
271 /* Binary search using GUID and attribute id for uniqueness */
272 ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values,
273 object_guid, object_dn,
274 partial_meta, 4,
275 &exact, &unused,
276 DSDB_SYNTAX_BINARY_DN, true);
278 if (ldb_err != LDB_SUCCESS) {
279 DEBUG(0,(__location__ ": Failed parsed DN find - %s\n",
280 ldb_errstring(sam_ctx)));
281 return WERR_DS_DRA_INTERNAL_ERROR;
284 if (exact != NULL) {
285 /* Perform some verification of the blob */
286 struct replPropertyMetaData1 existing_meta_data;
287 ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_dn->extra_part,
288 &existing_meta_data,
289 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
290 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
291 return WERR_DS_DRA_INTERNAL_ERROR;
294 if (existing_meta_data.attid == sa->attributeID_id) {
295 ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
296 if (ldb_err != LDB_SUCCESS) {
297 return WERR_DS_DRA_INTERNAL_ERROR;
300 el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
301 if (el_del->values == NULL) {
302 return WERR_NOT_ENOUGH_MEMORY;
304 el_del->values[0] = *exact->v;
305 el_del->num_values = 1;
306 } else {
307 return WERR_DS_DRA_INTERNAL_ERROR;
312 ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_ADD, &el_add);
313 if (ldb_err != LDB_SUCCESS) {
314 return WERR_DS_DRA_INTERNAL_ERROR;
317 el_add->values = talloc_array((*msg)->elements, struct ldb_val, 1);
318 if (el_add->values == NULL) {
319 return WERR_NOT_ENOUGH_MEMORY;
323 el_add->values[0] = data_blob_string_const(attr_str);
324 el_add->num_values = 1;
326 return WERR_OK;
330 * This function filter attributes for build_object based on the
331 * uptodatenessvector and partial attribute set.
333 * Any secret attributes are forced here for REPL_SECRET, and audited at this
334 * point with msDS-RevealedUsers.
336 static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItemEx *obj,
337 struct replPropertyMetaDataBlob md,
338 struct ldb_context *sam_ctx,
339 const struct ldb_message *msg,
340 const struct GUID *guid,
341 uint32_t *count,
342 uint64_t highest_usn,
343 const struct dsdb_attribute *rdn_sa,
344 struct dsdb_schema *schema,
345 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
346 struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
347 uint32_t *local_pas,
348 uint32_t *attids,
349 bool exop_secret,
350 struct ldb_message **revealed_list_msg,
351 struct ldb_message *existing_revealed_list_msg)
353 uint32_t i, n;
354 WERROR werr;
355 for (n=i=0; i<md.ctr.ctr1.count; i++) {
356 const struct dsdb_attribute *sa;
357 bool force_attribute = false;
359 /* if the attribute has not changed, and it is not the
360 instanceType then don't include it */
361 if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
362 !exop_secret &&
363 md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) continue;
365 /* don't include the rDN */
366 if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
368 sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
369 if (!sa) {
370 DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n",
371 (unsigned int)md.ctr.ctr1.array[i].attid,
372 ldb_dn_get_linearized(msg->dn)));
373 return WERR_DS_DRA_INTERNAL_ERROR;
376 if (sa->linkID) {
377 struct ldb_message_element *el;
378 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
379 if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
380 /* don't send upgraded links inline */
381 continue;
385 if (exop_secret &&
386 !dsdb_attr_in_rodc_fas(sa)) {
387 force_attribute = true;
388 DEBUG(4,("Forcing attribute %s in %s\n",
389 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
390 werr = getncchanges_update_revealed_list(sam_ctx, obj,
391 revealed_list_msg,
392 msg->dn, guid, sa,
393 &md.ctr.ctr1.array[i],
394 existing_revealed_list_msg);
395 if (!W_ERROR_IS_OK(werr)) {
396 return werr;
400 /* filter by uptodateness_vector */
401 if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType &&
402 !force_attribute &&
403 udv_filter(uptodateness_vector,
404 &md.ctr.ctr1.array[i].originating_invocation_id,
405 md.ctr.ctr1.array[i].originating_usn)) {
406 continue;
409 /* filter by partial_attribute_set */
410 if (partial_attribute_set && !force_attribute) {
411 uint32_t *result = NULL;
412 BINARY_ARRAY_SEARCH_V(local_pas, partial_attribute_set->num_attids, sa->attributeID_id,
413 uint32_t_cmp, result);
414 if (result == NULL) {
415 continue;
419 obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
420 obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
421 obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
422 obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
423 attids[n] = md.ctr.ctr1.array[i].attid;
425 n++;
428 *count = n;
430 return WERR_OK;
434 drsuapi_DsGetNCChanges for one object
436 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
437 const struct ldb_message *msg,
438 struct ldb_context *sam_ctx,
439 struct ldb_dn *ncRoot_dn,
440 bool is_schema_nc,
441 struct dsdb_schema *schema,
442 DATA_BLOB *session_key,
443 uint64_t highest_usn,
444 uint32_t replica_flags,
445 struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
446 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
447 enum drsuapi_DsExtendedOperation extended_op,
448 bool force_object_return,
449 uint32_t *local_pas,
450 struct ldb_dn *machine_dn,
451 const struct GUID *guid)
453 const struct ldb_val *md_value;
454 uint32_t i, n;
455 struct replPropertyMetaDataBlob md;
456 uint32_t rid = 0;
457 int ldb_err;
458 enum ndr_err_code ndr_err;
459 uint32_t *attids;
460 const char *rdn;
461 const struct dsdb_attribute *rdn_sa;
462 uint64_t uSNChanged;
463 unsigned int instanceType;
464 struct dsdb_syntax_ctx syntax_ctx;
465 struct ldb_result *res = NULL;
466 WERROR werr;
467 int ret;
469 /* make dsdb sytanx context for conversions */
470 dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
471 syntax_ctx.is_schema_nc = is_schema_nc;
473 uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
474 instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
475 if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
476 obj->is_nc_prefix = true;
477 obj->parent_object_guid = NULL;
478 } else {
479 obj->is_nc_prefix = false;
480 obj->parent_object_guid = talloc(obj, struct GUID);
481 if (obj->parent_object_guid == NULL) {
482 return WERR_DS_DRA_INTERNAL_ERROR;
484 *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
485 if (GUID_all_zero(obj->parent_object_guid)) {
486 DEBUG(0,(__location__ ": missing parentGUID for %s\n",
487 ldb_dn_get_linearized(msg->dn)));
488 return WERR_DS_DRA_INTERNAL_ERROR;
491 obj->next_object = NULL;
493 md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
494 if (!md_value) {
495 /* nothing to send */
496 return WERR_OK;
499 if (instanceType & INSTANCE_TYPE_UNINSTANT) {
500 /* don't send uninstantiated objects */
501 return WERR_OK;
504 if (uSNChanged <= highest_usn) {
505 /* nothing to send */
506 return WERR_OK;
509 ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
510 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
511 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
512 return WERR_DS_DRA_INTERNAL_ERROR;
515 if (md.version != 1) {
516 return WERR_DS_DRA_INTERNAL_ERROR;
519 rdn = ldb_dn_get_rdn_name(msg->dn);
520 if (rdn == NULL) {
521 DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
522 return WERR_DS_DRA_INTERNAL_ERROR;
525 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
526 if (rdn_sa == NULL) {
527 DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n",
528 rdn, ldb_dn_get_linearized(msg->dn)));
529 return WERR_DS_DRA_INTERNAL_ERROR;
532 obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
533 attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
535 obj->object.identifier = get_object_identifier(obj, msg);
536 if (obj->object.identifier == NULL) {
537 return WERR_NOT_ENOUGH_MEMORY;
539 dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
541 obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
543 if (extended_op == DRSUAPI_EXOP_REPL_SECRET) {
544 /* Get the existing revealed users for the destination */
545 struct ldb_message *revealed_list_msg = NULL;
546 struct ldb_message *existing_revealed_list_msg = NULL;
547 const char *machine_attrs[] = {
548 "msDS-RevealedUsers",
549 NULL
552 revealed_list_msg = ldb_msg_new(sam_ctx);
553 if (revealed_list_msg == NULL) {
554 return WERR_NOT_ENOUGH_MEMORY;
556 revealed_list_msg->dn = machine_dn;
558 ret = ldb_transaction_start(sam_ctx);
559 if (ret != LDB_SUCCESS) {
560 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
561 ldb_errstring(sam_ctx)));
562 return WERR_DS_DRA_INTERNAL_ERROR;
565 ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
566 if (ldb_err != LDB_SUCCESS || res->count != 1) {
567 ldb_transaction_cancel(sam_ctx);
568 return WERR_DS_DRA_INTERNAL_ERROR;
571 existing_revealed_list_msg = res->msgs[0];
573 werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg,
574 guid, &n, highest_usn,
575 rdn_sa, schema,
576 uptodateness_vector,
577 partial_attribute_set, local_pas,
578 attids,
579 true,
580 &revealed_list_msg,
581 existing_revealed_list_msg);
582 if (!W_ERROR_IS_OK(werr)) {
583 ldb_transaction_cancel(sam_ctx);
584 return werr;
587 if (revealed_list_msg != NULL) {
588 ret = ldb_modify(sam_ctx, revealed_list_msg);
589 if (ret != LDB_SUCCESS) {
590 DEBUG(0,(__location__ ": Failed to alter revealed links - %s\n",
591 ldb_errstring(sam_ctx)));
592 ldb_transaction_cancel(sam_ctx);
593 return WERR_DS_DRA_INTERNAL_ERROR;
597 ret = ldb_transaction_commit(sam_ctx);
598 if (ret != LDB_SUCCESS) {
599 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
600 ldb_errstring(sam_ctx)));
601 return WERR_DS_DRA_INTERNAL_ERROR;
603 } else {
604 werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid,
605 &n, highest_usn, rdn_sa,
606 schema, uptodateness_vector,
607 partial_attribute_set, local_pas,
608 attids,
609 false,
610 NULL,
611 NULL);
612 if (!W_ERROR_IS_OK(werr)) {
613 return werr;
617 /* ignore it if its an empty change. Note that renames always
618 * change the 'name' attribute, so they won't be ignored by
619 * this
621 * the force_object_return check is used to force an empty
622 * object return when we timeout in the getncchanges loop.
623 * This allows us to return an empty object, which keeps the
624 * client happy while preventing timeouts
626 if (n == 0 ||
627 (n == 1 &&
628 attids[0] == DRSUAPI_ATTID_instanceType &&
629 !force_object_return)) {
630 talloc_free(obj->meta_data_ctr);
631 obj->meta_data_ctr = NULL;
632 return WERR_OK;
635 obj->meta_data_ctr->count = n;
637 obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
638 obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
639 obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
640 obj->object.attribute_ctr.num_attributes);
641 if (obj->object.attribute_ctr.attributes == NULL) {
642 return WERR_NOT_ENOUGH_MEMORY;
646 * Note that the meta_data array and the attributes array must
647 * be the same size and in the same order
649 for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
650 struct ldb_message_element *el;
651 const struct dsdb_attribute *sa;
653 sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
654 if (!sa) {
655 DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
656 return WERR_DS_DRA_INTERNAL_ERROR;
659 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
660 if (el == NULL) {
661 /* this happens for attributes that have been removed */
662 DEBUG(5,("No element '%s' for attributeID %u in message\n",
663 sa->lDAPDisplayName, attids[i]));
664 ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
665 obj->object.attribute_ctr.attributes[i].attid =
666 dsdb_attribute_get_attid(sa, syntax_ctx.is_schema_nc);
667 } else {
668 werr = sa->syntax->ldb_to_drsuapi(&syntax_ctx, sa, el, obj,
669 &obj->object.attribute_ctr.attributes[i]);
670 if (!W_ERROR_IS_OK(werr)) {
671 DEBUG(0,("Unable to convert %s on %s to DRS object - %s\n",
672 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
673 win_errstr(werr)));
674 return werr;
676 /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
677 * check if attribute is secret and send a null value
679 if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
680 drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
681 &obj->meta_data_ctr->meta_data[i]);
683 /* some attributes needs to be encrypted
684 before being sent */
685 werr = drsuapi_encrypt_attribute(obj, session_key, rid,
686 &obj->object.attribute_ctr.attributes[i]);
687 if (!W_ERROR_IS_OK(werr)) {
688 DEBUG(0,("Unable to encrypt %s on %s in DRS object - %s\n",
689 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
690 win_errstr(werr)));
691 return werr;
694 if (attids[i] != obj->object.attribute_ctr.attributes[i].attid) {
695 DEBUG(0, ("Unable to replicate attribute %s on %s via DRS, incorrect attributeID: "
696 "0x%08x vs 0x%08x "
697 "Run dbcheck!\n",
698 sa->lDAPDisplayName,
699 ldb_dn_get_linearized(msg->dn),
700 attids[i],
701 obj->object.attribute_ctr.attributes[i].attid));
702 return WERR_DS_DATABASE_ERROR;
706 return WERR_OK;
710 add one linked attribute from an object to the list of linked
711 attributes in a getncchanges request
713 static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
714 struct ldb_context *sam_ctx,
715 const struct dsdb_schema *schema,
716 const struct dsdb_attribute *sa,
717 const struct ldb_message *msg,
718 struct dsdb_dn *dsdb_dn,
719 struct drsuapi_DsReplicaLinkedAttribute **la_list,
720 uint32_t *la_count,
721 bool is_schema_nc)
723 struct drsuapi_DsReplicaLinkedAttribute *la;
724 bool active;
725 NTSTATUS status;
726 WERROR werr;
728 (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
729 W_ERROR_HAVE_NO_MEMORY(*la_list);
731 la = &(*la_list)[*la_count];
733 la->identifier = get_object_identifier(*la_list, msg);
734 W_ERROR_HAVE_NO_MEMORY(la->identifier);
736 active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
738 if (!active) {
739 /* We have to check that the inactive link still point to an existing object */
740 struct GUID guid;
741 struct ldb_dn *tdn;
742 int ret;
743 const char *v;
745 v = ldb_msg_find_attr_as_string(msg, "isDeleted", "FALSE");
746 if (strncmp(v, "TRUE", 4) == 0) {
748 * Note: we skip the transmition of the deleted link even if the other part used to
749 * know about it because when we transmit the deletion of the object, the link will
750 * be deleted too due to deletion of object where link points and Windows do so.
752 if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008_R2) {
753 v = ldb_msg_find_attr_as_string(msg, "isRecycled", "FALSE");
755 * On Windows 2008R2 isRecycled is always present even if FL or DL are < FL 2K8R2
756 * if it join an existing domain with deleted objets, it firsts impose to have a
757 * schema with the is-Recycled object and for all deleted objects it adds the isRecycled
758 * either during initial replication or after the getNCChanges.
759 * Behavior of samba has been changed to always have this attribute if it's present in the schema.
761 * So if FL <2K8R2 isRecycled might be here or not but we don't care, it's meaning less.
762 * If FL >=2K8R2 we are sure that this attribute will be here.
763 * For this kind of forest level we do not return the link if the object is recycled
764 * (isRecycled = true).
766 if (strncmp(v, "TRUE", 4) == 0) {
767 DEBUG(2, (" object %s is recycled, not returning linked attribute !\n",
768 ldb_dn_get_linearized(msg->dn)));
769 return WERR_OK;
771 } else {
772 return WERR_OK;
775 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
776 if (!NT_STATUS_IS_OK(status)) {
777 DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
778 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
779 return ntstatus_to_werror(status);
781 ret = dsdb_find_dn_by_guid(sam_ctx, mem_ctx, &guid, 0, &tdn);
782 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
783 DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
784 GUID_string(mem_ctx, &guid)));
785 return WERR_OK;
786 } else if (ret != LDB_SUCCESS) {
787 DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
788 GUID_string(mem_ctx, &guid),
789 ret));
790 return WERR_OK;
793 la->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
794 la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
796 status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
797 if (!NT_STATUS_IS_OK(status)) {
798 DEBUG(0,(__location__ " No RMD_VERSION in linked attribute '%s' in '%s'\n",
799 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
800 return ntstatus_to_werror(status);
802 status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
803 if (!NT_STATUS_IS_OK(status)) {
804 DEBUG(0,(__location__ " No RMD_CHANGETIME in linked attribute '%s' in '%s'\n",
805 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
806 return ntstatus_to_werror(status);
808 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
809 if (!NT_STATUS_IS_OK(status)) {
810 DEBUG(0,(__location__ " No RMD_INVOCID in linked attribute '%s' in '%s'\n",
811 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
812 return ntstatus_to_werror(status);
814 status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
815 if (!NT_STATUS_IS_OK(status)) {
816 DEBUG(0,(__location__ " No RMD_ORIGINATING_USN in linked attribute '%s' in '%s'\n",
817 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
818 return ntstatus_to_werror(status);
821 status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
822 if (!NT_STATUS_IS_OK(status)) {
823 /* this is possible for upgraded links */
824 la->originating_add_time = la->meta_data.originating_change_time;
827 werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
828 W_ERROR_NOT_OK_RETURN(werr);
830 (*la_count)++;
831 return WERR_OK;
836 add linked attributes from an object to the list of linked
837 attributes in a getncchanges request
839 static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
840 TALLOC_CTX *mem_ctx,
841 struct ldb_dn *ncRoot_dn,
842 bool is_schema_nc,
843 struct dsdb_schema *schema,
844 uint64_t highest_usn,
845 uint32_t replica_flags,
846 const struct ldb_message *msg,
847 struct drsuapi_DsReplicaLinkedAttribute **la_list,
848 uint32_t *la_count,
849 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
851 unsigned int i;
852 TALLOC_CTX *tmp_ctx = NULL;
853 uint64_t uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
854 bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
856 if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
857 if (!is_critical) {
858 return WERR_OK;
862 if (uSNChanged <= highest_usn) {
863 return WERR_OK;
866 tmp_ctx = talloc_new(mem_ctx);
867 if (tmp_ctx == NULL) {
868 return WERR_NOT_ENOUGH_MEMORY;
871 for (i=0; i<msg->num_elements; i++) {
872 struct ldb_message_element *el = &msg->elements[i];
873 const struct dsdb_attribute *sa;
874 unsigned int j;
876 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
878 if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
879 /* we only want forward links */
880 continue;
883 if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
884 /* its an old style link, it will have been
885 * sent in the main replication data */
886 continue;
889 for (j=0; j<el->num_values; j++) {
890 struct dsdb_dn *dsdb_dn;
891 uint64_t local_usn;
892 uint64_t originating_usn;
893 NTSTATUS status, status2;
894 WERROR werr;
895 struct GUID originating_invocation_id;
897 dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
898 if (dsdb_dn == NULL) {
899 DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
900 el->name, ldb_dn_get_linearized(msg->dn)));
901 talloc_free(tmp_ctx);
902 return WERR_DS_DRA_INTERNAL_ERROR;
905 status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
906 if (!NT_STATUS_IS_OK(status)) {
907 /* this can happen for attributes
908 given to us with old style meta
909 data */
910 continue;
913 if (local_usn > uSNChanged) {
914 DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
915 el->name, ldb_dn_get_linearized(msg->dn)));
916 talloc_free(tmp_ctx);
917 return WERR_DS_DRA_INTERNAL_ERROR;
920 if (local_usn <= highest_usn) {
921 continue;
924 status = dsdb_get_extended_dn_guid(dsdb_dn->dn,
925 &originating_invocation_id,
926 "RMD_INVOCID");
927 status2 = dsdb_get_extended_dn_uint64(dsdb_dn->dn,
928 &originating_usn,
929 "RMD_ORIGINATING_USN");
931 if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2)) {
932 if (udv_filter(uptodateness_vector,
933 &originating_invocation_id,
934 originating_usn)) {
935 continue;
939 werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema,
940 sa, msg, dsdb_dn, la_list,
941 la_count, is_schema_nc);
942 if (!W_ERROR_IS_OK(werr)) {
943 talloc_free(tmp_ctx);
944 return werr;
949 talloc_free(tmp_ctx);
950 return WERR_OK;
954 fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
956 static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
957 struct ldb_dn *ncRoot_dn,
958 struct drsuapi_DsReplicaCursor2CtrEx *udv)
960 int ret;
962 udv->version = 2;
963 udv->reserved1 = 0;
964 udv->reserved2 = 0;
966 ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
967 if (ret != LDB_SUCCESS) {
968 DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
969 ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
970 return WERR_DS_DRA_INTERNAL_ERROR;
973 return WERR_OK;
977 /* comparison function for linked attributes - see CompareLinks() in
978 * MS-DRSR section 4.1.10.5.17 */
979 static int linked_attribute_compare(const struct la_for_sorting *la1,
980 const struct la_for_sorting *la2,
981 void *opaque)
983 int c;
984 c = memcmp(la1->source_guid,
985 la2->source_guid, sizeof(la2->source_guid));
986 if (c != 0) {
987 return c;
990 if (la1->link->attid != la2->link->attid) {
991 return la1->link->attid < la2->link->attid? -1:1;
994 if ((la1->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
995 (la2->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
996 return (la1->link->flags &
997 DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
1000 return memcmp(la1->target_guid,
1001 la2->target_guid, sizeof(la2->target_guid));
1004 struct drsuapi_changed_objects {
1005 struct ldb_dn *dn;
1006 struct GUID guid;
1007 uint64_t usn;
1011 sort the objects we send first by uSNChanged
1013 static int site_res_cmp_usn_order(struct drsuapi_changed_objects *m1,
1014 struct drsuapi_changed_objects *m2,
1015 struct drsuapi_getncchanges_state *getnc_state)
1017 int ret;
1019 ret = ldb_dn_compare(getnc_state->ncRoot_dn, m1->dn);
1020 if (ret == 0) {
1021 return -1;
1024 ret = ldb_dn_compare(getnc_state->ncRoot_dn, m2->dn);
1025 if (ret == 0) {
1026 return 1;
1029 if (m1->usn == m2->usn) {
1030 return ldb_dn_compare(m2->dn, m1->dn);
1033 if (m1->usn < m2->usn) {
1034 return -1;
1037 return 1;
1042 handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
1044 static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
1045 TALLOC_CTX *mem_ctx,
1046 struct drsuapi_DsGetNCChangesRequest10 *req10,
1047 struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1048 struct ldb_dn **rid_manager_dn)
1050 struct ldb_dn *req_dn, *ntds_dn = NULL;
1051 int ret;
1052 struct ldb_context *ldb = b_state->sam_ctx;
1053 struct ldb_result *ext_res;
1054 struct dsdb_fsmo_extended_op *exop;
1055 bool is_us;
1058 steps:
1059 - verify that the DN being asked for is the RID Manager DN
1060 - verify that we are the RID Manager
1063 /* work out who is the RID Manager, also return to caller */
1064 ret = samdb_rid_manager_dn(ldb, mem_ctx, rid_manager_dn);
1065 if (ret != LDB_SUCCESS) {
1066 DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
1067 return WERR_DS_DRA_INTERNAL_ERROR;
1070 req_dn = drs_ObjectIdentifier_to_dn(mem_ctx, ldb, req10->naming_context);
1071 if (!ldb_dn_validate(req_dn) ||
1072 ldb_dn_compare(req_dn, *rid_manager_dn) != 0) {
1073 /* that isn't the RID Manager DN */
1074 DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n",
1075 drs_ObjectIdentifier_to_string(mem_ctx, req10->naming_context)));
1076 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1077 return WERR_OK;
1080 /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1081 ret = dsdb_find_dn_by_guid(ldb, mem_ctx, &req10->destination_dsa_guid, 0, &ntds_dn);
1082 if (ret != LDB_SUCCESS) {
1083 DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1084 GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1085 ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1086 return WERR_OK;
1089 /* find the DN of the RID Manager */
1090 ret = samdb_reference_dn_is_our_ntdsa(ldb, *rid_manager_dn, "fSMORoleOwner", &is_us);
1091 if (ret != LDB_SUCCESS) {
1092 DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1093 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1094 return WERR_DS_DRA_INTERNAL_ERROR;
1097 if (!is_us) {
1098 /* we're not the RID Manager - go away */
1099 DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
1100 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1101 return WERR_OK;
1104 exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
1105 W_ERROR_HAVE_NO_MEMORY(exop);
1107 exop->fsmo_info = req10->fsmo_info;
1108 exop->destination_dsa_guid = req10->destination_dsa_guid;
1110 ret = ldb_transaction_start(ldb);
1111 if (ret != LDB_SUCCESS) {
1112 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1113 ldb_errstring(ldb)));
1114 return WERR_DS_DRA_INTERNAL_ERROR;
1117 ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
1118 if (ret != LDB_SUCCESS) {
1119 DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
1120 ldb_errstring(ldb)));
1121 ldb_transaction_cancel(ldb);
1122 return WERR_DS_DRA_INTERNAL_ERROR;
1125 ret = ldb_transaction_commit(ldb);
1126 if (ret != LDB_SUCCESS) {
1127 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1128 ldb_errstring(ldb)));
1129 return WERR_DS_DRA_INTERNAL_ERROR;
1132 talloc_free(ext_res);
1134 DEBUG(2,("Allocated RID pool for server %s\n",
1135 GUID_string(mem_ctx, &req10->destination_dsa_guid)));
1137 ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1139 return WERR_OK;
1143 return an array of SIDs from a ldb_message given an attribute name
1144 assumes the SIDs are in extended DN format
1146 static WERROR samdb_result_sid_array_dn(struct ldb_context *sam_ctx,
1147 struct ldb_message *msg,
1148 TALLOC_CTX *mem_ctx,
1149 const char *attr,
1150 const struct dom_sid ***sids)
1152 struct ldb_message_element *el;
1153 unsigned int i;
1155 el = ldb_msg_find_element(msg, attr);
1156 if (!el) {
1157 *sids = NULL;
1158 return WERR_OK;
1161 (*sids) = talloc_array(mem_ctx, const struct dom_sid *, el->num_values + 1);
1162 W_ERROR_HAVE_NO_MEMORY(*sids);
1164 for (i=0; i<el->num_values; i++) {
1165 struct ldb_dn *dn = ldb_dn_from_ldb_val(mem_ctx, sam_ctx, &el->values[i]);
1166 NTSTATUS status;
1167 struct dom_sid *sid;
1169 sid = talloc(*sids, struct dom_sid);
1170 W_ERROR_HAVE_NO_MEMORY(sid);
1171 status = dsdb_get_extended_dn_sid(dn, sid, "SID");
1172 if (!NT_STATUS_IS_OK(status)) {
1173 return WERR_INTERNAL_DB_CORRUPTION;
1175 (*sids)[i] = sid;
1177 (*sids)[i] = NULL;
1179 return WERR_OK;
1184 * Return an array of SIDs from a ldb_message given an attribute name assumes
1185 * the SIDs are in NDR form (with additional sids applied on the end).
1187 static WERROR samdb_result_sid_array_ndr(struct ldb_context *sam_ctx,
1188 struct ldb_message *msg,
1189 TALLOC_CTX *mem_ctx,
1190 const char *attr,
1191 const struct dom_sid ***sids,
1192 const struct dom_sid **additional_sids,
1193 unsigned int num_additional)
1195 struct ldb_message_element *el;
1196 unsigned int i, j;
1198 el = ldb_msg_find_element(msg, attr);
1199 if (!el) {
1200 *sids = NULL;
1201 return WERR_OK;
1204 /* Make array long enough for NULL and additional SID */
1205 (*sids) = talloc_array(mem_ctx, const struct dom_sid *,
1206 el->num_values + num_additional + 1);
1207 W_ERROR_HAVE_NO_MEMORY(*sids);
1209 for (i=0; i<el->num_values; i++) {
1210 enum ndr_err_code ndr_err;
1211 struct dom_sid *sid;
1213 sid = talloc(*sids, struct dom_sid);
1214 W_ERROR_HAVE_NO_MEMORY(sid);
1216 ndr_err = ndr_pull_struct_blob(&el->values[i], sid, sid,
1217 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
1218 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1219 return WERR_INTERNAL_DB_CORRUPTION;
1221 (*sids)[i] = sid;
1224 for (j = 0; j < num_additional; j++) {
1225 (*sids)[i++] = additional_sids[j];
1228 (*sids)[i] = NULL;
1230 return WERR_OK;
1234 see if any SIDs in list1 are in list2
1236 static bool sid_list_match(const struct dom_sid **list1, const struct dom_sid **list2)
1238 unsigned int i, j;
1239 /* do we ever have enough SIDs here to worry about O(n^2) ? */
1240 for (i=0; list1[i]; i++) {
1241 for (j=0; list2[j]; j++) {
1242 if (dom_sid_equal(list1[i], list2[j])) {
1243 return true;
1247 return false;
1251 handle a DRSUAPI_EXOP_REPL_SECRET call
1253 static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
1254 TALLOC_CTX *mem_ctx,
1255 struct drsuapi_DsGetNCChangesRequest10 *req10,
1256 struct dom_sid *user_sid,
1257 struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1258 bool has_get_all_changes,
1259 struct ldb_dn **machine_dn)
1261 struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1262 struct ldb_dn *obj_dn = NULL;
1263 struct ldb_dn *ntds_dn = NULL, *server_dn = NULL;
1264 struct ldb_dn *rodc_dn, *krbtgt_link_dn;
1265 int ret;
1266 const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", "objectGUID", NULL };
1267 const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
1268 struct ldb_result *rodc_res = NULL, *obj_res = NULL;
1269 const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids;
1270 const struct dom_sid *object_sid = NULL;
1271 WERROR werr;
1272 const struct dom_sid *additional_sids[] = { NULL, NULL };
1274 DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n",
1275 drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
1278 * we need to work out if we will allow this DC to
1279 * replicate the secrets for this object
1281 * see 4.1.10.5.14 GetRevealSecretsPolicyForUser for details
1282 * of this function
1285 if (b_state->sam_ctx_system == NULL) {
1286 /* this operation needs system level access */
1287 ctr6->extended_ret = DRSUAPI_EXOP_ERR_ACCESS_DENIED;
1288 return WERR_DS_DRA_SOURCE_DISABLED;
1292 * Before we accept or deny, fetch the machine DN for the destination
1293 * DSA GUID.
1295 * If we are the RODC, we will check that this matches the SID.
1297 ret = dsdb_find_dn_by_guid(b_state->sam_ctx_system, mem_ctx,
1298 &req10->destination_dsa_guid, 0,
1299 &ntds_dn);
1300 if (ret != LDB_SUCCESS) {
1301 goto failed;
1304 server_dn = ldb_dn_get_parent(mem_ctx, ntds_dn);
1305 if (server_dn == NULL) {
1306 goto failed;
1309 ret = samdb_reference_dn(b_state->sam_ctx_system, mem_ctx, server_dn,
1310 "serverReference", machine_dn);
1312 if (ret != LDB_SUCCESS) {
1313 goto failed;
1317 * In MS-DRSR.pdf 5.99 IsGetNCChangesPermissionGranted
1319 * The pseudo code indicate
1320 * revealsecrets = true
1321 * if IsRevealSecretRequest(msgIn) then
1322 * if AccessCheckCAR(ncRoot, Ds-Replication-Get-Changes-All) = false
1323 * then
1324 * if (msgIn.ulExtendedOp = EXOP_REPL_SECRETS) then
1325 * <... check if this account is ok to be replicated on this DC ...>
1326 * <... and if not reveal secrets = no ...>
1327 * else
1328 * reveal secrets = false
1329 * endif
1330 * endif
1331 * endif
1333 * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC)
1334 * then you can do EXOP_REPL_SECRETS
1336 obj_dn = drs_ObjectIdentifier_to_dn(mem_ctx, b_state->sam_ctx_system, ncRoot);
1337 if (!ldb_dn_validate(obj_dn)) goto failed;
1339 if (has_get_all_changes) {
1340 goto allowed;
1343 rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "<SID=%s>",
1344 dom_sid_string(mem_ctx, user_sid));
1345 if (!ldb_dn_validate(rodc_dn)) goto failed;
1347 /* do the two searches we need */
1348 ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
1349 DSDB_SEARCH_SHOW_EXTENDED_DN);
1350 if (ret != LDB_SUCCESS || rodc_res->count != 1) goto failed;
1352 ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
1353 if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed;
1355 /* if the object SID is equal to the user_sid, allow */
1356 object_sid = samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid");
1357 if (dom_sid_equal(user_sid, object_sid)) {
1358 goto allowed;
1361 additional_sids[0] = object_sid;
1364 * Must be an RODC account at this point, verify machine DN matches the
1365 * SID account
1367 if (ldb_dn_compare(rodc_res->msgs[0]->dn, *machine_dn) != 0) {
1368 goto denied;
1371 /* an RODC is allowed to get its own krbtgt account secrets */
1372 krbtgt_link_dn = samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
1373 rodc_res->msgs[0], "msDS-KrbTgtLink", NULL);
1374 if (krbtgt_link_dn != NULL &&
1375 ldb_dn_compare(obj_dn, krbtgt_link_dn) == 0) {
1376 goto allowed;
1379 /* but it isn't allowed to get anyone elses krbtgt secrets */
1380 if (samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
1381 obj_res->msgs[0], "msDS-KrbTgtLinkBL", NULL)) {
1382 goto denied;
1385 if (ldb_msg_find_attr_as_uint(obj_res->msgs[0],
1386 "userAccountControl", 0) &
1387 UF_INTERDOMAIN_TRUST_ACCOUNT) {
1388 goto denied;
1391 werr = samdb_result_sid_array_dn(b_state->sam_ctx_system, rodc_res->msgs[0],
1392 mem_ctx, "msDS-NeverRevealGroup", &never_reveal_sids);
1393 if (!W_ERROR_IS_OK(werr)) {
1394 goto denied;
1397 werr = samdb_result_sid_array_dn(b_state->sam_ctx_system, rodc_res->msgs[0],
1398 mem_ctx, "msDS-RevealOnDemandGroup", &reveal_sids);
1399 if (!W_ERROR_IS_OK(werr)) {
1400 goto denied;
1404 * The SID list needs to include itself as well as the tokenGroups.
1406 * TODO determine if sIDHistory is required for this check
1408 werr = samdb_result_sid_array_ndr(b_state->sam_ctx_system, obj_res->msgs[0],
1409 mem_ctx, "tokenGroups", &token_sids,
1410 additional_sids, 1);
1411 if (!W_ERROR_IS_OK(werr) || token_sids==NULL) {
1412 goto denied;
1415 if (never_reveal_sids &&
1416 sid_list_match(token_sids, never_reveal_sids)) {
1417 goto denied;
1420 if (reveal_sids &&
1421 sid_list_match(token_sids, reveal_sids)) {
1422 goto allowed;
1425 /* default deny */
1426 denied:
1427 DEBUG(2,(__location__ ": Denied single object with secret replication for %s by RODC %s\n",
1428 ldb_dn_get_linearized(obj_dn), ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
1429 ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1430 return WERR_DS_DRA_SECRETS_DENIED;
1432 allowed:
1433 DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
1434 ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC",
1435 ldb_dn_get_linearized(*machine_dn)));
1436 ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1437 req10->highwatermark.highest_usn = 0;
1438 return WERR_OK;
1440 failed:
1441 DEBUG(2,(__location__ ": Failed single secret replication for %s by RODC %s\n",
1442 ldb_dn_get_linearized(obj_dn), dom_sid_string(mem_ctx, user_sid)));
1443 ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1444 return WERR_DS_DRA_BAD_DN;
1448 handle a DRSUAPI_EXOP_REPL_OBJ call
1450 static WERROR getncchanges_repl_obj(struct drsuapi_bind_state *b_state,
1451 TALLOC_CTX *mem_ctx,
1452 struct drsuapi_DsGetNCChangesRequest10 *req10,
1453 struct dom_sid *user_sid,
1454 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1456 struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1458 DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_OBJ extended op on %s\n",
1459 drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
1461 ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1462 return WERR_OK;
1467 handle DRSUAPI_EXOP_FSMO_REQ_ROLE,
1468 DRSUAPI_EXOP_FSMO_RID_REQ_ROLE,
1469 and DRSUAPI_EXOP_FSMO_REQ_PDC calls
1471 static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state,
1472 TALLOC_CTX *mem_ctx,
1473 struct drsuapi_DsGetNCChangesRequest10 *req10,
1474 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1476 struct ldb_dn *req_dn, *ntds_dn;
1477 int ret;
1478 unsigned int i;
1479 struct ldb_context *ldb = b_state->sam_ctx;
1480 struct ldb_message *msg;
1481 bool is_us;
1484 steps:
1485 - verify that the client dn exists
1486 - verify that we are the current master
1489 req_dn = drs_ObjectIdentifier_to_dn(mem_ctx, ldb, req10->naming_context);
1490 if (!ldb_dn_validate(req_dn)) {
1491 /* that is not a valid dn */
1492 DEBUG(0,(__location__ ": FSMO role transfer request for invalid DN %s\n",
1493 drs_ObjectIdentifier_to_string(mem_ctx, req10->naming_context)));
1494 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1495 return WERR_OK;
1498 /* find the DN of the current role owner */
1499 ret = samdb_reference_dn_is_our_ntdsa(ldb, req_dn, "fSMORoleOwner", &is_us);
1500 if (ret != LDB_SUCCESS) {
1501 DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1502 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1503 return WERR_DS_DRA_INTERNAL_ERROR;
1506 if (!is_us) {
1507 /* we're not the RID Manager or role owner - go away */
1508 DEBUG(0,(__location__ ": FSMO role or RID manager transfer owner request when not role owner\n"));
1509 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1510 return WERR_OK;
1513 /* change the current master */
1514 msg = ldb_msg_new(ldb);
1515 W_ERROR_HAVE_NO_MEMORY(msg);
1516 msg->dn = drs_ObjectIdentifier_to_dn(msg, ldb, req10->naming_context);
1517 W_ERROR_HAVE_NO_MEMORY(msg->dn);
1519 /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1520 ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, 0, &ntds_dn);
1521 if (ret != LDB_SUCCESS) {
1522 DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1523 GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1524 talloc_free(msg);
1525 ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1526 return WERR_OK;
1529 ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn));
1530 if (ret != 0) {
1531 talloc_free(msg);
1532 return WERR_DS_DRA_INTERNAL_ERROR;
1535 for (i=0;i<msg->num_elements;i++) {
1536 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1539 ret = ldb_transaction_start(ldb);
1540 if (ret != LDB_SUCCESS) {
1541 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1542 ldb_errstring(ldb)));
1543 return WERR_DS_DRA_INTERNAL_ERROR;
1546 ret = ldb_modify(ldb, msg);
1547 if (ret != LDB_SUCCESS) {
1548 DEBUG(0,(__location__ ": Failed to change current owner - %s\n",
1549 ldb_errstring(ldb)));
1550 ldb_transaction_cancel(ldb);
1551 return WERR_DS_DRA_INTERNAL_ERROR;
1554 ret = ldb_transaction_commit(ldb);
1555 if (ret != LDB_SUCCESS) {
1556 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1557 ldb_errstring(ldb)));
1558 return WERR_DS_DRA_INTERNAL_ERROR;
1561 ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1563 return WERR_OK;
1567 see if this getncchanges request includes a request to reveal secret information
1569 static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state *b_state,
1570 struct drsuapi_DsGetNCChangesRequest10 *req10,
1571 struct dsdb_schema_prefixmap *pfm_remote,
1572 bool *is_secret_request)
1574 enum drsuapi_DsExtendedOperation exop;
1575 uint32_t i;
1576 struct dsdb_schema *schema;
1577 struct dsdb_syntax_ctx syntax_ctx;
1579 *is_secret_request = true;
1581 exop = req10->extended_op;
1583 switch (exop) {
1584 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1585 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1586 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1587 case DRSUAPI_EXOP_FSMO_REQ_PDC:
1588 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1589 /* FSMO exops can reveal secrets */
1590 *is_secret_request = true;
1591 return WERR_OK;
1592 case DRSUAPI_EXOP_REPL_SECRET:
1593 case DRSUAPI_EXOP_REPL_OBJ:
1594 case DRSUAPI_EXOP_NONE:
1595 break;
1598 if (req10->replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
1599 *is_secret_request = false;
1600 return WERR_OK;
1603 if (exop == DRSUAPI_EXOP_REPL_SECRET ||
1604 req10->partial_attribute_set == NULL) {
1605 /* they want secrets */
1606 *is_secret_request = true;
1607 return WERR_OK;
1610 schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1611 dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1612 syntax_ctx.pfm_remote = pfm_remote;
1614 /* check the attributes they asked for */
1615 for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1616 const struct dsdb_attribute *sa;
1617 WERROR werr = getncchanges_attid_remote_to_local(schema,
1618 &syntax_ctx,
1619 req10->partial_attribute_set->attids[i],
1620 NULL,
1621 &sa);
1623 if (!W_ERROR_IS_OK(werr)) {
1624 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1625 req10->partial_attribute_set->attids[i], win_errstr(werr)));
1626 return werr;
1629 if (!dsdb_attr_in_rodc_fas(sa)) {
1630 *is_secret_request = true;
1631 return WERR_OK;
1635 if (req10->partial_attribute_set_ex) {
1636 /* check the extended attributes they asked for */
1637 for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1638 const struct dsdb_attribute *sa;
1639 WERROR werr = getncchanges_attid_remote_to_local(schema,
1640 &syntax_ctx,
1641 req10->partial_attribute_set_ex->attids[i],
1642 NULL,
1643 &sa);
1645 if (!W_ERROR_IS_OK(werr)) {
1646 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1647 req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1648 return werr;
1651 if (!dsdb_attr_in_rodc_fas(sa)) {
1652 *is_secret_request = true;
1653 return WERR_OK;
1658 *is_secret_request = false;
1659 return WERR_OK;
1663 see if this getncchanges request is only for attributes in the GC
1664 partial attribute set
1666 static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state,
1667 struct drsuapi_DsGetNCChangesRequest10 *req10,
1668 struct dsdb_schema_prefixmap *pfm_remote,
1669 bool *is_gc_pas_request)
1671 enum drsuapi_DsExtendedOperation exop;
1672 uint32_t i;
1673 struct dsdb_schema *schema;
1674 struct dsdb_syntax_ctx syntax_ctx;
1676 exop = req10->extended_op;
1678 switch (exop) {
1679 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1680 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1681 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1682 case DRSUAPI_EXOP_FSMO_REQ_PDC:
1683 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1684 case DRSUAPI_EXOP_REPL_SECRET:
1685 *is_gc_pas_request = false;
1686 return WERR_OK;
1687 case DRSUAPI_EXOP_REPL_OBJ:
1688 case DRSUAPI_EXOP_NONE:
1689 break;
1692 if (req10->partial_attribute_set == NULL) {
1693 /* they want it all */
1694 *is_gc_pas_request = false;
1695 return WERR_OK;
1698 schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1699 dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1700 syntax_ctx.pfm_remote = pfm_remote;
1702 /* check the attributes they asked for */
1703 for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1704 const struct dsdb_attribute *sa;
1705 WERROR werr = getncchanges_attid_remote_to_local(schema,
1706 &syntax_ctx,
1707 req10->partial_attribute_set->attids[i],
1708 NULL,
1709 &sa);
1711 if (!W_ERROR_IS_OK(werr)) {
1712 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1713 req10->partial_attribute_set->attids[i], win_errstr(werr)));
1714 return werr;
1717 if (!sa->isMemberOfPartialAttributeSet) {
1718 *is_gc_pas_request = false;
1719 return WERR_OK;
1723 if (req10->partial_attribute_set_ex) {
1724 /* check the extended attributes they asked for */
1725 for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1726 const struct dsdb_attribute *sa;
1727 WERROR werr = getncchanges_attid_remote_to_local(schema,
1728 &syntax_ctx,
1729 req10->partial_attribute_set_ex->attids[i],
1730 NULL,
1731 &sa);
1733 if (!W_ERROR_IS_OK(werr)) {
1734 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1735 req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1736 return werr;
1739 if (!sa->isMemberOfPartialAttributeSet) {
1740 *is_gc_pas_request = false;
1741 return WERR_OK;
1746 *is_gc_pas_request = true;
1747 return WERR_OK;
1752 map from req8 to req10
1754 static struct drsuapi_DsGetNCChangesRequest10 *
1755 getncchanges_map_req8(TALLOC_CTX *mem_ctx,
1756 struct drsuapi_DsGetNCChangesRequest8 *req8)
1758 struct drsuapi_DsGetNCChangesRequest10 *req10 = talloc_zero(mem_ctx,
1759 struct drsuapi_DsGetNCChangesRequest10);
1760 if (req10 == NULL) {
1761 return NULL;
1764 req10->destination_dsa_guid = req8->destination_dsa_guid;
1765 req10->source_dsa_invocation_id = req8->source_dsa_invocation_id;
1766 req10->naming_context = req8->naming_context;
1767 req10->highwatermark = req8->highwatermark;
1768 req10->uptodateness_vector = req8->uptodateness_vector;
1769 req10->replica_flags = req8->replica_flags;
1770 req10->max_object_count = req8->max_object_count;
1771 req10->max_ndr_size = req8->max_ndr_size;
1772 req10->extended_op = req8->extended_op;
1773 req10->fsmo_info = req8->fsmo_info;
1774 req10->partial_attribute_set = req8->partial_attribute_set;
1775 req10->partial_attribute_set_ex = req8->partial_attribute_set_ex;
1776 req10->mapping_ctr = req8->mapping_ctr;
1778 return req10;
1781 static const char *collect_objects_attrs[] = { "uSNChanged",
1782 "objectGUID" ,
1783 NULL };
1786 * Collects object for normal replication cycle.
1788 static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
1789 TALLOC_CTX *mem_ctx,
1790 struct drsuapi_DsGetNCChangesRequest10 *req10,
1791 struct ldb_dn *search_dn,
1792 const char *extra_filter,
1793 struct ldb_result **search_res)
1795 int ret;
1796 char* search_filter;
1797 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
1798 struct drsuapi_getncchanges_state *getnc_state = b_state->getncchanges_state;
1799 bool critical_only = false;
1801 if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
1802 critical_only = true;
1805 if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ ||
1806 req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
1807 scope = LDB_SCOPE_BASE;
1808 critical_only = false;
1811 /* Construct response. */
1812 search_filter = talloc_asprintf(mem_ctx,
1813 "(uSNChanged>=%llu)",
1814 (unsigned long long)(getnc_state->min_usn+1));
1816 if (extra_filter) {
1817 search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
1820 if (critical_only) {
1821 search_filter = talloc_asprintf(mem_ctx,
1822 "(&%s(isCriticalSystemObject=TRUE))",
1823 search_filter);
1826 if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
1827 scope = LDB_SCOPE_BASE;
1830 if (!search_dn) {
1831 search_dn = getnc_state->ncRoot_dn;
1834 DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n",
1835 ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
1836 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, search_res,
1837 search_dn, scope,
1838 collect_objects_attrs,
1839 search_filter);
1840 if (ret != LDB_SUCCESS) {
1841 return WERR_DS_DRA_INTERNAL_ERROR;
1844 return WERR_OK;
1848 * Collects object for normal replication cycle.
1850 static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state,
1851 TALLOC_CTX *mem_ctx,
1852 struct drsuapi_DsGetNCChangesRequest10 *req10,
1853 struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1854 struct ldb_dn *search_dn,
1855 const char *extra_filter,
1856 struct ldb_result **search_res)
1858 /* we have nothing to do in case of ex-op failure */
1859 if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
1860 return WERR_OK;
1863 switch (req10->extended_op) {
1864 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1866 int ret;
1867 struct ldb_dn *ntds_dn = NULL;
1868 struct ldb_dn *server_dn = NULL;
1869 struct ldb_dn *machine_dn = NULL;
1870 struct ldb_dn *rid_set_dn = NULL;
1871 struct ldb_result *search_res2 = NULL;
1872 struct ldb_result *search_res3 = NULL;
1873 TALLOC_CTX *frame = talloc_stackframe();
1874 /* get RID manager, RID set and server DN (in that order) */
1876 /* This first search will get the RID Manager */
1877 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1878 search_res,
1879 search_dn, LDB_SCOPE_BASE,
1880 collect_objects_attrs,
1881 NULL);
1882 if (ret != LDB_SUCCESS) {
1883 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %s",
1884 ldb_dn_get_linearized(search_dn),
1885 ldb_errstring(b_state->sam_ctx)));
1886 TALLOC_FREE(frame);
1887 return WERR_DS_DRA_INTERNAL_ERROR;
1890 if ((*search_res)->count != 1) {
1891 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %u objects returned",
1892 ldb_dn_get_linearized(search_dn),
1893 (*search_res)->count));
1894 TALLOC_FREE(frame);
1895 return WERR_DS_DRA_INTERNAL_ERROR;
1898 /* Now extend it to the RID set */
1900 /* Find the computer account DN for the destination
1901 * dsa GUID specified */
1903 ret = dsdb_find_dn_by_guid(b_state->sam_ctx, frame,
1904 &req10->destination_dsa_guid, 0,
1905 &ntds_dn);
1906 if (ret != LDB_SUCCESS) {
1907 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Unable to find NTDS object for guid %s - %s\n",
1908 GUID_string(frame,
1909 &req10->destination_dsa_guid),
1910 ldb_errstring(b_state->sam_ctx)));
1911 TALLOC_FREE(frame);
1912 return WERR_DS_DRA_INTERNAL_ERROR;
1915 server_dn = ldb_dn_get_parent(frame, ntds_dn);
1916 if (!server_dn) {
1917 TALLOC_FREE(frame);
1918 return WERR_DS_DRA_INTERNAL_ERROR;
1921 ret = samdb_reference_dn(b_state->sam_ctx, frame, server_dn,
1922 "serverReference", &machine_dn);
1923 if (ret != LDB_SUCCESS) {
1924 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find serverReference in %s - %s",
1925 ldb_dn_get_linearized(server_dn),
1926 ldb_errstring(b_state->sam_ctx)));
1927 TALLOC_FREE(frame);
1928 return WERR_DS_DRA_INTERNAL_ERROR;
1931 ret = samdb_reference_dn(b_state->sam_ctx, frame, machine_dn,
1932 "rIDSetReferences", &rid_set_dn);
1933 if (ret != LDB_SUCCESS) {
1934 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find rIDSetReferences in %s - %s",
1935 ldb_dn_get_linearized(server_dn),
1936 ldb_errstring(b_state->sam_ctx)));
1937 TALLOC_FREE(frame);
1938 return WERR_DS_DRA_INTERNAL_ERROR;
1942 /* This first search will get the RID Manager, now get the RID set */
1943 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1944 &search_res2,
1945 rid_set_dn, LDB_SCOPE_BASE,
1946 collect_objects_attrs,
1947 NULL);
1948 if (ret != LDB_SUCCESS) {
1949 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %s",
1950 ldb_dn_get_linearized(rid_set_dn),
1951 ldb_errstring(b_state->sam_ctx)));
1952 TALLOC_FREE(frame);
1953 return WERR_DS_DRA_INTERNAL_ERROR;
1956 if (search_res2->count != 1) {
1957 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %u objects returned",
1958 ldb_dn_get_linearized(rid_set_dn),
1959 search_res2->count));
1960 TALLOC_FREE(frame);
1961 return WERR_DS_DRA_INTERNAL_ERROR;
1964 /* Finally get the server DN */
1965 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1966 &search_res3,
1967 machine_dn, LDB_SCOPE_BASE,
1968 collect_objects_attrs,
1969 NULL);
1970 if (ret != LDB_SUCCESS) {
1971 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %s",
1972 ldb_dn_get_linearized(server_dn),
1973 ldb_errstring(b_state->sam_ctx)));
1974 TALLOC_FREE(frame);
1975 return WERR_DS_DRA_INTERNAL_ERROR;
1978 if (search_res3->count != 1) {
1979 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %u objects returned",
1980 ldb_dn_get_linearized(server_dn),
1981 search_res3->count));
1982 TALLOC_FREE(frame);
1983 return WERR_DS_DRA_INTERNAL_ERROR;
1986 /* Now extend the original search_res with these answers */
1987 (*search_res)->count = 3;
1989 (*search_res)->msgs = talloc_realloc(frame, (*search_res)->msgs,
1990 struct ldb_message *,
1991 (*search_res)->count);
1992 if ((*search_res)->msgs == NULL) {
1993 TALLOC_FREE(frame);
1994 return WERR_NOT_ENOUGH_MEMORY;
1998 talloc_steal(mem_ctx, *search_res);
1999 (*search_res)->msgs[1] =
2000 talloc_steal((*search_res)->msgs, search_res2->msgs[0]);
2001 (*search_res)->msgs[2] =
2002 talloc_steal((*search_res)->msgs, search_res3->msgs[0]);
2004 TALLOC_FREE(frame);
2005 return WERR_OK;
2007 default:
2008 /* TODO: implement extended op specific collection
2009 * of objects. Right now we just normal procedure
2010 * for collecting objects */
2011 return getncchanges_collect_objects(b_state, mem_ctx, req10, search_dn, extra_filter, search_res);
2015 static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
2016 uint64_t max_usn,
2017 struct drsuapi_DsReplicaHighWaterMark *hwm)
2019 uint64_t uSN = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
2021 if (uSN > max_usn) {
2023 * Only report the max_usn we had at the start
2024 * of the replication cycle.
2026 * If this object has changed lately we better
2027 * let the destination dsa refetch the change.
2028 * This is better than the risk of loosing some
2029 * objects or linked attributes.
2031 return;
2034 if (uSN <= hwm->tmp_highest_usn) {
2035 return;
2038 hwm->tmp_highest_usn = uSN;
2039 hwm->reserved_usn = 0;
2042 static WERROR dcesrv_drsuapi_anc_cache_add(struct db_context *anc_cache,
2043 const struct GUID *guid)
2045 enum ndr_err_code ndr_err;
2046 uint8_t guid_buf[16] = { 0, };
2047 DATA_BLOB b = {
2048 .data = guid_buf,
2049 .length = sizeof(guid_buf),
2051 TDB_DATA key = {
2052 .dptr = b.data,
2053 .dsize = b.length,
2055 TDB_DATA val = {
2056 .dptr = NULL,
2057 .dsize = 0,
2059 NTSTATUS status;
2061 ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
2062 (ndr_push_flags_fn_t)ndr_push_GUID);
2063 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2064 return WERR_DS_DRA_INTERNAL_ERROR;
2067 status = dbwrap_store(anc_cache, key, val, TDB_REPLACE);
2068 if (!NT_STATUS_IS_OK(status)) {
2069 return WERR_DS_DRA_INTERNAL_ERROR;
2072 return WERR_OK;
2075 static WERROR dcesrv_drsuapi_anc_cache_exists(struct db_context *anc_cache,
2076 const struct GUID *guid)
2078 enum ndr_err_code ndr_err;
2079 uint8_t guid_buf[16] = { 0, };
2080 DATA_BLOB b = {
2081 .data = guid_buf,
2082 .length = sizeof(guid_buf),
2084 TDB_DATA key = {
2085 .dptr = b.data,
2086 .dsize = b.length,
2088 bool exists;
2090 ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
2091 (ndr_push_flags_fn_t)ndr_push_GUID);
2092 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2093 return WERR_DS_DRA_INTERNAL_ERROR;
2096 exists = dbwrap_exists(anc_cache, key);
2097 if (!exists) {
2098 return WERR_OBJECT_NOT_FOUND;
2101 return WERR_OBJECT_NAME_EXISTS;
2105 drsuapi_DsGetNCChanges
2107 see MS-DRSR 4.1.10.5.2 for basic logic of this function
2109 WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2110 struct drsuapi_DsGetNCChanges *r)
2112 struct drsuapi_DsReplicaObjectIdentifier *ncRoot;
2113 int ret;
2114 uint32_t i, k;
2115 struct dsdb_schema *schema;
2116 struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
2117 struct drsuapi_DsReplicaObjectListItemEx **currentObject;
2118 NTSTATUS status;
2119 DATA_BLOB session_key;
2120 WERROR werr;
2121 struct dcesrv_handle *h;
2122 struct drsuapi_bind_state *b_state;
2123 struct drsuapi_getncchanges_state *getnc_state;
2124 struct drsuapi_DsGetNCChangesRequest10 *req10;
2125 uint32_t options;
2126 uint32_t max_objects;
2127 uint32_t max_links;
2128 uint32_t link_count = 0;
2129 uint32_t link_total = 0;
2130 uint32_t link_given = 0;
2131 struct ldb_dn *search_dn = NULL;
2132 bool am_rodc, null_scope=false;
2133 enum security_user_level security_level;
2134 struct ldb_context *sam_ctx;
2135 struct dom_sid *user_sid;
2136 bool is_secret_request;
2137 bool is_gc_pas_request;
2138 struct drsuapi_changed_objects *changes;
2139 time_t max_wait;
2140 time_t start = time(NULL);
2141 bool max_wait_reached = false;
2142 bool has_get_all_changes = false;
2143 struct GUID invocation_id;
2144 static const struct drsuapi_DsReplicaLinkedAttribute no_linked_attr;
2145 struct dsdb_schema_prefixmap *pfm_remote = NULL;
2146 bool full = true;
2147 uint32_t *local_pas = NULL;
2148 struct ldb_dn *machine_dn = NULL; /* Only used for REPL SECRET EXOP */
2150 DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
2151 b_state = h->data;
2153 sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
2155 invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2157 *r->out.level_out = 6;
2158 /* TODO: linked attributes*/
2159 r->out.ctr->ctr6.linked_attributes_count = 0;
2160 r->out.ctr->ctr6.linked_attributes = discard_const_p(struct drsuapi_DsReplicaLinkedAttribute, &no_linked_attr);
2162 r->out.ctr->ctr6.object_count = 0;
2163 r->out.ctr->ctr6.nc_object_count = 0;
2164 r->out.ctr->ctr6.more_data = false;
2165 r->out.ctr->ctr6.uptodateness_vector = NULL;
2166 r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
2167 r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2168 r->out.ctr->ctr6.first_object = NULL;
2170 /* a RODC doesn't allow for any replication */
2171 ret = samdb_rodc(sam_ctx, &am_rodc);
2172 if (ret == LDB_SUCCESS && am_rodc) {
2173 DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
2174 return WERR_DS_DRA_SOURCE_DISABLED;
2177 /* Check request revision.
2179 switch (r->in.level) {
2180 case 8:
2181 req10 = getncchanges_map_req8(mem_ctx, &r->in.req->req8);
2182 if (req10 == NULL) {
2183 return WERR_NOT_ENOUGH_MEMORY;
2185 break;
2186 case 10:
2187 req10 = &r->in.req->req10;
2188 break;
2189 default:
2190 DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
2191 r->in.level));
2192 return WERR_REVISION_MISMATCH;
2196 /* Perform access checks. */
2197 /* TODO: we need to support a sync on a specific non-root
2198 * DN. We'll need to find the real partition root here */
2199 ncRoot = req10->naming_context;
2200 if (ncRoot == NULL) {
2201 DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
2202 return WERR_DS_DRA_INVALID_PARAMETER;
2205 if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) {
2206 return WERR_DS_DRA_INTERNAL_ERROR;
2209 if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
2210 !(req10->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
2211 return WERR_DS_DRA_SOURCE_DISABLED;
2214 user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
2216 /* all clients must have GUID_DRS_GET_CHANGES */
2217 werr = drs_security_access_check_nc_root(b_state->sam_ctx,
2218 mem_ctx,
2219 dce_call->conn->auth_state.session_info->security_token,
2220 req10->naming_context,
2221 GUID_DRS_GET_CHANGES);
2222 if (!W_ERROR_IS_OK(werr)) {
2223 return werr;
2226 if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) {
2227 full = req10->partial_attribute_set == NULL &&
2228 req10->partial_attribute_set_ex == NULL;
2229 } else {
2230 full = (options & DRSUAPI_DRS_WRIT_REP) != 0;
2233 werr = dsdb_schema_pfm_from_drsuapi_pfm(&req10->mapping_ctr, true,
2234 mem_ctx, &pfm_remote, NULL);
2236 /* We were supplied a partial attribute set, without the prefix map! */
2237 if (!full && !W_ERROR_IS_OK(werr)) {
2238 if (req10->mapping_ctr.num_mappings == 0) {
2240 * Despite the fact MS-DRSR specifies that this shouldn't
2241 * happen, Windows RODCs will in fact not provide a prefixMap.
2243 DEBUG(5,(__location__ ": Failed to provide a remote prefixMap,"
2244 " falling back to local prefixMap\n"));
2245 } else {
2246 DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
2247 win_errstr(werr)));
2248 return werr;
2252 /* allowed if the GC PAS and client has
2253 GUID_DRS_GET_FILTERED_ATTRIBUTES */
2254 werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, pfm_remote, &is_gc_pas_request);
2255 if (!W_ERROR_IS_OK(werr)) {
2256 return werr;
2258 if (is_gc_pas_request) {
2259 werr = drs_security_access_check_nc_root(b_state->sam_ctx,
2260 mem_ctx,
2261 dce_call->conn->auth_state.session_info->security_token,
2262 req10->naming_context,
2263 GUID_DRS_GET_FILTERED_ATTRIBUTES);
2264 if (W_ERROR_IS_OK(werr)) {
2265 goto allowed;
2269 werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10,
2270 pfm_remote,
2271 &is_secret_request);
2272 if (!W_ERROR_IS_OK(werr)) {
2273 return werr;
2275 if (is_secret_request) {
2276 werr = drs_security_access_check_nc_root(b_state->sam_ctx,
2277 mem_ctx,
2278 dce_call->conn->auth_state.session_info->security_token,
2279 req10->naming_context,
2280 GUID_DRS_GET_ALL_CHANGES);
2281 if (!W_ERROR_IS_OK(werr)) {
2282 /* Only bail if this is not a EXOP_REPL_SECRET */
2283 if (req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
2284 return werr;
2286 } else {
2287 has_get_all_changes = true;
2291 allowed:
2292 /* for non-administrator replications, check that they have
2293 given the correct source_dsa_invocation_id */
2294 security_level = security_session_user_level(dce_call->conn->auth_state.session_info,
2295 samdb_domain_sid(sam_ctx));
2296 if (security_level == SECURITY_RO_DOMAIN_CONTROLLER) {
2297 if (req10->replica_flags & DRSUAPI_DRS_WRIT_REP) {
2298 /* we rely on this flag being unset for RODC requests */
2299 req10->replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
2303 if (req10->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
2304 /* Ignore the _in_ uptpdateness vector*/
2305 req10->uptodateness_vector = NULL;
2308 if (GUID_all_zero(&req10->source_dsa_invocation_id)) {
2309 req10->source_dsa_invocation_id = invocation_id;
2312 if (!GUID_equal(&req10->source_dsa_invocation_id, &invocation_id)) {
2314 * The given highwatermark is only valid relative to the
2315 * specified source_dsa_invocation_id.
2317 ZERO_STRUCT(req10->highwatermark);
2320 getnc_state = b_state->getncchanges_state;
2322 /* see if a previous replication has been abandoned */
2323 if (getnc_state) {
2324 struct ldb_dn *new_dn = drs_ObjectIdentifier_to_dn(getnc_state, sam_ctx, ncRoot);
2325 if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
2326 DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
2327 ldb_dn_get_linearized(new_dn),
2328 ldb_dn_get_linearized(getnc_state->ncRoot_dn),
2329 ldb_dn_get_linearized(getnc_state->last_dn)));
2330 talloc_free(getnc_state);
2331 getnc_state = NULL;
2335 if (getnc_state) {
2336 ret = drsuapi_DsReplicaHighWaterMark_cmp(&getnc_state->last_hwm,
2337 &req10->highwatermark);
2338 if (ret != 0) {
2339 DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication "
2340 "on DN %s %s highwatermark (last_dn %s)\n",
2341 ldb_dn_get_linearized(getnc_state->ncRoot_dn),
2342 (ret > 0) ? "older" : "newer",
2343 ldb_dn_get_linearized(getnc_state->last_dn)));
2344 talloc_free(getnc_state);
2345 getnc_state = NULL;
2349 if (getnc_state == NULL) {
2350 getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state);
2351 if (getnc_state == NULL) {
2352 return WERR_NOT_ENOUGH_MEMORY;
2354 b_state->getncchanges_state = getnc_state;
2355 getnc_state->ncRoot_dn = drs_ObjectIdentifier_to_dn(getnc_state, sam_ctx, ncRoot);
2356 if (getnc_state->ncRoot_dn == NULL) {
2357 return WERR_NOT_ENOUGH_MEMORY;
2360 ret = dsdb_find_guid_by_dn(b_state->sam_ctx_system,
2361 getnc_state->ncRoot_dn,
2362 &getnc_state->ncRoot_guid);
2363 if (ret != LDB_SUCCESS) {
2364 DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
2365 ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
2366 return WERR_DS_DRA_INTERNAL_ERROR;
2368 ncRoot->guid = getnc_state->ncRoot_guid;
2370 /* find out if we are to replicate Schema NC */
2371 ret = ldb_dn_compare_base(ldb_get_schema_basedn(b_state->sam_ctx),
2372 getnc_state->ncRoot_dn);
2374 getnc_state->is_schema_nc = (0 == ret);
2376 if (req10->extended_op != DRSUAPI_EXOP_NONE) {
2377 r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
2381 * This is the first replication cycle and it is
2382 * a good place to handle extended operations
2384 * FIXME: we don't fully support extended operations yet
2386 switch (req10->extended_op) {
2387 case DRSUAPI_EXOP_NONE:
2388 break;
2389 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
2390 werr = getncchanges_rid_alloc(b_state, mem_ctx, req10, &r->out.ctr->ctr6, &search_dn);
2391 W_ERROR_NOT_OK_RETURN(werr);
2392 if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2393 return WERR_OK;
2395 break;
2396 case DRSUAPI_EXOP_REPL_SECRET:
2397 werr = getncchanges_repl_secret(b_state, mem_ctx, req10,
2398 user_sid,
2399 &r->out.ctr->ctr6,
2400 has_get_all_changes,
2401 &machine_dn);
2402 r->out.result = werr;
2403 W_ERROR_NOT_OK_RETURN(werr);
2404 break;
2405 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
2406 werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
2407 W_ERROR_NOT_OK_RETURN(werr);
2408 if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2409 return WERR_OK;
2411 break;
2412 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
2413 werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
2414 W_ERROR_NOT_OK_RETURN(werr);
2415 if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2416 return WERR_OK;
2418 break;
2419 case DRSUAPI_EXOP_FSMO_REQ_PDC:
2420 werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
2421 W_ERROR_NOT_OK_RETURN(werr);
2422 if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2423 return WERR_OK;
2425 break;
2426 case DRSUAPI_EXOP_REPL_OBJ:
2427 werr = getncchanges_repl_obj(b_state, mem_ctx, req10, user_sid, &r->out.ctr->ctr6);
2428 r->out.result = werr;
2429 W_ERROR_NOT_OK_RETURN(werr);
2430 break;
2432 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
2434 DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
2435 (unsigned)req10->extended_op));
2436 return WERR_DS_DRA_NOT_SUPPORTED;
2440 if (!ldb_dn_validate(getnc_state->ncRoot_dn) ||
2441 ldb_dn_is_null(getnc_state->ncRoot_dn)) {
2442 DEBUG(0,(__location__ ": Bad DN '%s'\n",
2443 drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
2444 return WERR_DS_DRA_INVALID_PARAMETER;
2447 ncRoot->guid = getnc_state->ncRoot_guid;
2449 /* we need the session key for encrypting password attributes */
2450 status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
2451 if (!NT_STATUS_IS_OK(status)) {
2452 DEBUG(0,(__location__ ": Failed to get session key\n"));
2453 return WERR_DS_DRA_INTERNAL_ERROR;
2457 TODO: MS-DRSR section 4.1.10.1.1
2458 Work out if this is the start of a new cycle */
2460 if (getnc_state->guids == NULL) {
2461 const char *extra_filter;
2462 struct ldb_result *search_res = NULL;
2463 static const struct drsuapi_DsReplicaCursorCtrEx empty_udv;
2464 const struct drsuapi_DsReplicaCursorCtrEx *udv = NULL;
2466 extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
2468 if (req10->uptodateness_vector != NULL) {
2469 udv = req10->uptodateness_vector;
2470 } else {
2471 udv = &empty_udv;
2474 getnc_state->min_usn = req10->highwatermark.highest_usn;
2475 for (i = 0; i < udv->count; i++) {
2476 bool match;
2477 const struct drsuapi_DsReplicaCursor *cur =
2478 &udv->cursors[i];
2480 match = GUID_equal(&invocation_id,
2481 &cur->source_dsa_invocation_id);
2482 if (!match) {
2483 continue;
2485 if (cur->highest_usn > getnc_state->min_usn) {
2486 getnc_state->min_usn = cur->highest_usn;
2488 break;
2490 getnc_state->max_usn = getnc_state->min_usn;
2492 getnc_state->final_udv = talloc_zero(getnc_state,
2493 struct drsuapi_DsReplicaCursor2CtrEx);
2494 if (getnc_state->final_udv == NULL) {
2495 return WERR_NOT_ENOUGH_MEMORY;
2497 werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn,
2498 getnc_state->final_udv);
2499 if (!W_ERROR_IS_OK(werr)) {
2500 return werr;
2503 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2504 werr = getncchanges_collect_objects(b_state, mem_ctx, req10,
2505 search_dn, extra_filter,
2506 &search_res);
2507 } else {
2508 werr = getncchanges_collect_objects_exop(b_state, mem_ctx, req10,
2509 &r->out.ctr->ctr6,
2510 search_dn, extra_filter,
2511 &search_res);
2513 W_ERROR_NOT_OK_RETURN(werr);
2515 /* extract out the GUIDs list */
2516 getnc_state->num_records = search_res ? search_res->count : 0;
2517 getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records);
2518 W_ERROR_HAVE_NO_MEMORY(getnc_state->guids);
2520 changes = talloc_array(getnc_state,
2521 struct drsuapi_changed_objects,
2522 getnc_state->num_records);
2523 W_ERROR_HAVE_NO_MEMORY(changes);
2525 for (i=0; i<getnc_state->num_records; i++) {
2526 changes[i].dn = search_res->msgs[i]->dn;
2527 changes[i].guid = samdb_result_guid(search_res->msgs[i], "objectGUID");
2528 changes[i].usn = ldb_msg_find_attr_as_uint64(search_res->msgs[i], "uSNChanged", 0);
2530 if (changes[i].usn > getnc_state->max_usn) {
2531 getnc_state->max_usn = changes[i].usn;
2535 /* RID_ALLOC returns 3 objects in a fixed order */
2536 if (req10->extended_op == DRSUAPI_EXOP_FSMO_RID_ALLOC) {
2537 /* Do nothing */
2538 } else {
2539 LDB_TYPESAFE_QSORT(changes,
2540 getnc_state->num_records,
2541 getnc_state,
2542 site_res_cmp_usn_order);
2545 for (i=0; i < getnc_state->num_records; i++) {
2546 getnc_state->guids[i] = changes[i].guid;
2547 if (GUID_all_zero(&getnc_state->guids[i])) {
2548 DEBUG(2,("getncchanges: bad objectGUID from %s\n",
2549 ldb_dn_get_linearized(search_res->msgs[i]->dn)));
2550 return WERR_DS_DRA_INTERNAL_ERROR;
2554 getnc_state->final_hwm.tmp_highest_usn = getnc_state->max_usn;
2555 getnc_state->final_hwm.reserved_usn = 0;
2556 getnc_state->final_hwm.highest_usn = getnc_state->max_usn;
2558 talloc_free(search_res);
2559 talloc_free(changes);
2561 if (req10->extended_op != DRSUAPI_EXOP_NONE) {
2562 /* Do nothing */
2563 } else if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) {
2564 getnc_state->anc_cache = db_open_rbt(getnc_state);
2565 if (getnc_state->anc_cache == NULL) {
2566 return WERR_NOT_ENOUGH_MEMORY;
2571 if (req10->uptodateness_vector) {
2572 /* make sure its sorted */
2573 TYPESAFE_QSORT(req10->uptodateness_vector->cursors,
2574 req10->uptodateness_vector->count,
2575 drsuapi_DsReplicaCursor_compare);
2578 /* Prefix mapping */
2579 schema = dsdb_get_schema(sam_ctx, mem_ctx);
2580 if (!schema) {
2581 DEBUG(0,("No schema in sam_ctx\n"));
2582 return WERR_DS_DRA_INTERNAL_ERROR;
2585 r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
2586 if (r->out.ctr->ctr6.naming_context == NULL) {
2587 return WERR_NOT_ENOUGH_MEMORY;
2589 *r->out.ctr->ctr6.naming_context = *ncRoot;
2591 /* find the SID if there is one */
2592 dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
2594 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
2595 r->out.ctr->ctr6.mapping_ctr = *ctr;
2597 r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
2598 r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2600 r->out.ctr->ctr6.old_highwatermark = req10->highwatermark;
2601 r->out.ctr->ctr6.new_highwatermark = req10->highwatermark;
2603 currentObject = &r->out.ctr->ctr6.first_object;
2605 max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
2607 * The client control here only applies in normal replication, not extended
2608 * operations, which return a fixed set, even if the caller
2609 * sets max_object_count == 0
2611 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2612 /* use this to force single objects at a time, which is useful
2613 * for working out what object is giving problems
2615 if (req10->max_object_count < max_objects) {
2616 max_objects = req10->max_object_count;
2620 * TODO: work out how the maximum should be calculated
2622 max_links = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max link sync", 1500);
2625 * Maximum time that we can spend in a getncchanges
2626 * in order to avoid timeout of the other part.
2627 * 10 seconds by default.
2629 max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max work time", 10);
2631 if (req10->partial_attribute_set != NULL) {
2632 struct dsdb_syntax_ctx syntax_ctx;
2633 uint32_t j = 0;
2635 dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
2636 syntax_ctx.pfm_remote = pfm_remote;
2638 local_pas = talloc_array(b_state, uint32_t, req10->partial_attribute_set->num_attids);
2640 for (j = 0; j < req10->partial_attribute_set->num_attids; j++) {
2641 getncchanges_attid_remote_to_local(schema,
2642 &syntax_ctx,
2643 req10->partial_attribute_set->attids[j],
2644 (enum drsuapi_DsAttributeId *)&local_pas[j],
2645 NULL);
2648 LDB_TYPESAFE_QSORT(local_pas,
2649 req10->partial_attribute_set->num_attids,
2650 NULL,
2651 uint32_t_ptr_cmp);
2654 for (i=getnc_state->num_processed;
2655 i<getnc_state->num_records &&
2656 !null_scope &&
2657 (r->out.ctr->ctr6.object_count < max_objects)
2658 && !max_wait_reached;
2659 i++) {
2660 struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
2661 struct drsuapi_DsReplicaObjectListItemEx *obj;
2662 struct ldb_message *msg;
2663 static const char * const msg_attrs[] = {
2664 "*",
2665 "nTSecurityDescriptor",
2666 "parentGUID",
2667 "replPropertyMetaData",
2668 DSDB_SECRET_ATTRIBUTES,
2669 NULL };
2670 struct ldb_result *msg_res;
2671 struct ldb_dn *msg_dn;
2672 const struct GUID *next_anc_guid = NULL;
2674 obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
2675 W_ERROR_HAVE_NO_MEMORY(obj);
2677 msg_dn = ldb_dn_new_fmt(obj, sam_ctx, "<GUID=%s>", GUID_string(obj, &getnc_state->guids[i]));
2678 W_ERROR_HAVE_NO_MEMORY(msg_dn);
2681 /* by re-searching here we avoid having a lot of full
2682 * records in memory between calls to getncchanges
2684 ret = drsuapi_search_with_extended_dn(sam_ctx, obj, &msg_res,
2685 msg_dn,
2686 LDB_SCOPE_BASE, msg_attrs, NULL);
2687 if (ret != LDB_SUCCESS) {
2688 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
2689 DEBUG(1,("getncchanges: failed to fetch DN %s - %s\n",
2690 ldb_dn_get_extended_linearized(obj, msg_dn, 1), ldb_errstring(sam_ctx)));
2692 talloc_free(obj);
2693 continue;
2696 msg = msg_res->msgs[0];
2699 * If it has already been added as an ancestor of
2700 * an object, we don't need to do anything more,
2701 * as we've already added the links.
2703 if (getnc_state->anc_cache != NULL) {
2704 werr = dcesrv_drsuapi_anc_cache_exists(getnc_state->anc_cache,
2705 &getnc_state->guids[i]);
2706 if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2707 dcesrv_drsuapi_update_highwatermark(msg,
2708 getnc_state->max_usn,
2709 &r->out.ctr->ctr6.new_highwatermark);
2710 /* no attributes to send */
2711 talloc_free(obj);
2712 continue;
2716 max_wait_reached = (time(NULL) - start > max_wait);
2718 werr = get_nc_changes_build_object(obj, msg,
2719 sam_ctx, getnc_state->ncRoot_dn,
2720 getnc_state->is_schema_nc,
2721 schema, &session_key, getnc_state->min_usn,
2722 req10->replica_flags,
2723 req10->partial_attribute_set,
2724 req10->uptodateness_vector,
2725 req10->extended_op,
2726 max_wait_reached,
2727 local_pas, machine_dn,
2728 &getnc_state->guids[i]);
2729 if (!W_ERROR_IS_OK(werr)) {
2730 return werr;
2733 werr = get_nc_changes_add_links(sam_ctx, getnc_state,
2734 getnc_state->ncRoot_dn,
2735 getnc_state->is_schema_nc,
2736 schema, getnc_state->min_usn,
2737 req10->replica_flags,
2738 msg,
2739 &getnc_state->la_list,
2740 &getnc_state->la_count,
2741 req10->uptodateness_vector);
2742 if (!W_ERROR_IS_OK(werr)) {
2743 return werr;
2746 dcesrv_drsuapi_update_highwatermark(msg,
2747 getnc_state->max_usn,
2748 &r->out.ctr->ctr6.new_highwatermark);
2750 if (obj->meta_data_ctr == NULL) {
2751 DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
2752 ldb_dn_get_linearized(msg->dn)));
2753 /* no attributes to send */
2754 talloc_free(obj);
2755 continue;
2758 new_objs = obj;
2760 if (getnc_state->anc_cache != NULL) {
2761 werr = dcesrv_drsuapi_anc_cache_add(getnc_state->anc_cache,
2762 &getnc_state->guids[i]);
2763 if (!W_ERROR_IS_OK(werr)) {
2764 return werr;
2767 next_anc_guid = obj->parent_object_guid;
2770 while (next_anc_guid != NULL) {
2771 struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
2772 struct ldb_message *anc_msg = NULL;
2773 struct ldb_result *anc_res = NULL;
2774 struct ldb_dn *anc_dn = NULL;
2776 werr = dcesrv_drsuapi_anc_cache_exists(getnc_state->anc_cache,
2777 next_anc_guid);
2778 if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2780 * We don't need to send it twice.
2782 break;
2784 if (W_ERROR_IS_OK(werr)) {
2785 return WERR_INTERNAL_ERROR;
2787 if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
2788 return werr;
2790 werr = WERR_OK;
2792 anc_obj = talloc_zero(mem_ctx,
2793 struct drsuapi_DsReplicaObjectListItemEx);
2794 if (anc_obj == NULL) {
2795 return WERR_NOT_ENOUGH_MEMORY;
2798 anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
2799 GUID_string(anc_obj, next_anc_guid));
2800 if (anc_dn == NULL) {
2801 return WERR_NOT_ENOUGH_MEMORY;
2804 ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
2805 &anc_res, anc_dn,
2806 LDB_SCOPE_BASE,
2807 msg_attrs, NULL);
2808 if (ret != LDB_SUCCESS) {
2809 const char *anc_str = NULL;
2810 const char *obj_str = NULL;
2812 anc_str = ldb_dn_get_extended_linearized(anc_obj,
2813 anc_dn,
2815 obj_str = ldb_dn_get_extended_linearized(anc_obj,
2816 msg->dn,
2819 DBG_ERR("getncchanges: failed to fetch ANC "
2820 "DN %s for DN %s - %s\n",
2821 anc_str, obj_str,
2822 ldb_errstring(sam_ctx));
2823 return WERR_DS_DRA_INCONSISTENT_DIT;
2826 anc_msg = anc_res->msgs[0];
2828 werr = get_nc_changes_build_object(anc_obj, anc_msg,
2829 sam_ctx,
2830 getnc_state->ncRoot_dn,
2831 getnc_state->is_schema_nc,
2832 schema, &session_key,
2833 getnc_state->min_usn,
2834 req10->replica_flags,
2835 req10->partial_attribute_set,
2836 req10->uptodateness_vector,
2837 req10->extended_op,
2838 false, /* force_object_return */
2839 local_pas,
2840 machine_dn,
2841 next_anc_guid);
2842 if (!W_ERROR_IS_OK(werr)) {
2843 return werr;
2846 werr = get_nc_changes_add_links(sam_ctx, getnc_state,
2847 getnc_state->ncRoot_dn,
2848 getnc_state->is_schema_nc,
2849 schema, getnc_state->min_usn,
2850 req10->replica_flags,
2851 anc_msg,
2852 &getnc_state->la_list,
2853 &getnc_state->la_count,
2854 req10->uptodateness_vector);
2855 if (!W_ERROR_IS_OK(werr)) {
2856 return werr;
2860 * Regardless of if we actually use it or not,
2861 * we add it to the cache so we don't look at it again
2863 werr = dcesrv_drsuapi_anc_cache_add(getnc_state->anc_cache,
2864 next_anc_guid);
2865 if (!W_ERROR_IS_OK(werr)) {
2866 return werr;
2870 * Any ancestors which are below the highwatermark
2871 * or uptodateness_vector shouldn't be added,
2872 * but we still look further up the
2873 * tree for ones which have been changed recently.
2875 if (anc_obj->meta_data_ctr != NULL) {
2877 * prepend it to the list
2879 anc_obj->next_object = new_objs;
2880 new_objs = anc_obj;
2883 anc_msg = NULL;
2884 TALLOC_FREE(anc_res);
2885 TALLOC_FREE(anc_dn);
2888 * We may need to resolve more...
2890 next_anc_guid = anc_obj->parent_object_guid;
2893 *currentObject = new_objs;
2894 while (new_objs != NULL) {
2895 r->out.ctr->ctr6.object_count += 1;
2896 if (new_objs->next_object == NULL) {
2897 currentObject = &new_objs->next_object;
2899 new_objs = new_objs->next_object;
2902 DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));
2904 talloc_free(getnc_state->last_dn);
2905 getnc_state->last_dn = talloc_move(getnc_state, &msg->dn);
2907 talloc_free(msg_res);
2908 talloc_free(msg_dn);
2911 getnc_state->num_processed = i;
2913 /* the client can us to call UpdateRefs on its behalf to
2914 re-establish monitoring of the NC */
2915 if ((req10->replica_flags & (DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_REF_GCSPN)) &&
2916 !GUID_all_zero(&req10->destination_dsa_guid)) {
2917 struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
2918 DEBUG(3,("UpdateRefs on getncchanges for %s\n",
2919 GUID_string(mem_ctx, &req10->destination_dsa_guid)));
2920 ureq.naming_context = ncRoot;
2921 ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(b_state->sam_ctx, mem_ctx,
2922 &req10->destination_dsa_guid);
2923 if (!ureq.dest_dsa_dns_name) {
2924 return WERR_NOT_ENOUGH_MEMORY;
2926 ureq.dest_dsa_guid = req10->destination_dsa_guid;
2927 ureq.options = DRSUAPI_DRS_ADD_REF |
2928 DRSUAPI_DRS_ASYNC_OP |
2929 DRSUAPI_DRS_GETCHG_CHECK;
2931 /* we also need to pass through the
2932 DRSUAPI_DRS_REF_GCSPN bit so that repsTo gets flagged
2933 to send notifies using the GC SPN */
2934 ureq.options |= (req10->replica_flags & DRSUAPI_DRS_REF_GCSPN);
2936 werr = drsuapi_UpdateRefs(dce_call->msg_ctx,
2937 dce_call->event_ctx, b_state,
2938 mem_ctx, &ureq);
2939 if (!W_ERROR_IS_OK(werr)) {
2940 DEBUG(0,(__location__ ": Failed UpdateRefs on %s for %s in DsGetNCChanges - %s\n",
2941 drs_ObjectIdentifier_to_string(mem_ctx, ncRoot), ureq.dest_dsa_dns_name,
2942 win_errstr(werr)));
2947 * TODO:
2948 * This is just a guess, how to calculate the
2949 * number of linked attributes to send, we need to
2950 * find out how to do this right.
2952 if (r->out.ctr->ctr6.object_count >= max_links) {
2953 max_links = 0;
2954 } else {
2955 max_links -= r->out.ctr->ctr6.object_count;
2958 link_total = getnc_state->la_count;
2960 if (i < getnc_state->num_records) {
2961 r->out.ctr->ctr6.more_data = true;
2962 } else {
2963 /* sort the whole array the first time */
2964 if (getnc_state->la_sorted == NULL) {
2965 int j;
2966 struct la_for_sorting *guid_array = talloc_array(getnc_state, struct la_for_sorting, getnc_state->la_count);
2967 if (guid_array == NULL) {
2968 DEBUG(0, ("Out of memory allocating %u linked attributes for sorting", getnc_state->la_count));
2969 return WERR_NOT_ENOUGH_MEMORY;
2971 for (j = 0; j < getnc_state->la_count; j++) {
2972 /* we need to get the target GUIDs to compare */
2973 struct dsdb_dn *dn;
2974 const struct drsuapi_DsReplicaLinkedAttribute *la = &getnc_state->la_list[j];
2975 const struct dsdb_attribute *schema_attrib;
2976 const struct ldb_val *target_guid;
2977 DATA_BLOB source_guid;
2978 TALLOC_CTX *frame = talloc_stackframe();
2980 schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid);
2982 werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn);
2983 if (!W_ERROR_IS_OK(werr)) {
2984 DEBUG(0,(__location__ ": Bad la blob in sort\n"));
2985 TALLOC_FREE(frame);
2986 return werr;
2989 /* Extract the target GUID in NDR form */
2990 target_guid = ldb_dn_get_extended_component(dn->dn, "GUID");
2991 if (target_guid == NULL
2992 || target_guid->length != sizeof(guid_array[0].target_guid)) {
2993 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2994 } else {
2995 /* Repack the source GUID as NDR for sorting */
2996 status = GUID_to_ndr_blob(&la->identifier->guid,
2997 frame,
2998 &source_guid);
3001 if (!NT_STATUS_IS_OK(status)
3002 || source_guid.length != sizeof(guid_array[0].source_guid)) {
3003 DEBUG(0,(__location__ ": Bad la guid in sort\n"));
3004 TALLOC_FREE(frame);
3005 return ntstatus_to_werror(status);
3008 guid_array[j].link = &getnc_state->la_list[j];
3009 memcpy(guid_array[j].target_guid, target_guid->data,
3010 sizeof(guid_array[j].target_guid));
3011 memcpy(guid_array[j].source_guid, source_guid.data,
3012 sizeof(guid_array[j].source_guid));
3013 TALLOC_FREE(frame);
3016 LDB_TYPESAFE_QSORT(guid_array, getnc_state->la_count, NULL, linked_attribute_compare);
3017 getnc_state->la_sorted = guid_array;
3020 link_count = getnc_state->la_count - getnc_state->la_idx;
3021 link_count = MIN(max_links, link_count);
3023 r->out.ctr->ctr6.linked_attributes_count = link_count;
3024 r->out.ctr->ctr6.linked_attributes = talloc_array(r->out.ctr, struct drsuapi_DsReplicaLinkedAttribute, link_count);
3025 if (r->out.ctr->ctr6.linked_attributes == NULL) {
3026 DEBUG(0, ("Out of memory allocating %u linked attributes for output", link_count));
3027 return WERR_NOT_ENOUGH_MEMORY;
3030 for (k = 0; k < link_count; k++) {
3031 r->out.ctr->ctr6.linked_attributes[k]
3032 = *getnc_state->la_sorted[getnc_state->la_idx + k].link;
3035 getnc_state->la_idx += link_count;
3036 link_given = getnc_state->la_idx;
3038 if (getnc_state->la_idx < getnc_state->la_count) {
3039 r->out.ctr->ctr6.more_data = true;
3043 if (req10->replica_flags & DRSUAPI_DRS_GET_NC_SIZE) {
3045 * TODO: This implementation is wrong
3046 * we should find out the total number of
3047 * objects and links in the whole naming context
3048 * at the start of the cycle and return these
3049 * values in each message.
3051 * For now we keep our current strategy and return
3052 * the number of objects for this cycle and the number
3053 * of links we found so far during the cycle.
3055 r->out.ctr->ctr6.nc_object_count = getnc_state->num_records;
3056 r->out.ctr->ctr6.nc_linked_attributes_count = getnc_state->la_count;
3059 if (!r->out.ctr->ctr6.more_data) {
3060 talloc_steal(mem_ctx, getnc_state->la_list);
3062 r->out.ctr->ctr6.new_highwatermark = getnc_state->final_hwm;
3063 r->out.ctr->ctr6.uptodateness_vector = talloc_move(mem_ctx,
3064 &getnc_state->final_udv);
3066 talloc_free(getnc_state);
3067 b_state->getncchanges_state = NULL;
3068 } else {
3069 ret = drsuapi_DsReplicaHighWaterMark_cmp(&r->out.ctr->ctr6.old_highwatermark,
3070 &r->out.ctr->ctr6.new_highwatermark);
3071 if (ret == 0) {
3073 * We need to make sure that we never return the
3074 * same highwatermark within the same replication
3075 * cycle more than once. Otherwise we cannot detect
3076 * when the client uses an unexptected highwatermark.
3078 * This is a HACK which is needed because our
3079 * object ordering is wrong and set tmp_highest_usn
3080 * to a value that is higher than what we already
3081 * sent to the client (destination dsa).
3083 r->out.ctr->ctr6.new_highwatermark.reserved_usn += 1;
3086 getnc_state->last_hwm = r->out.ctr->ctr6.new_highwatermark;
3089 if (req10->extended_op != DRSUAPI_EXOP_NONE) {
3090 r->out.ctr->ctr6.uptodateness_vector = NULL;
3091 r->out.ctr->ctr6.nc_object_count = 0;
3092 ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
3095 DEBUG(r->out.ctr->ctr6.more_data?4:2,
3096 ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %u/%u) %u links (done %u/%u (as %s))\n",
3097 (unsigned long long)(req10->highwatermark.highest_usn+1),
3098 req10->replica_flags, drs_ObjectIdentifier_to_string(mem_ctx, ncRoot),
3099 r->out.ctr->ctr6.object_count,
3100 i, r->out.ctr->ctr6.more_data?getnc_state->num_records:i,
3101 r->out.ctr->ctr6.linked_attributes_count,
3102 link_given, link_total,
3103 dom_sid_string(mem_ctx, user_sid)));
3105 #if 0
3106 if (!r->out.ctr->ctr6.more_data && req10->extended_op != DRSUAPI_EXOP_NONE) {
3107 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);
3109 #endif
3111 return WERR_OK;