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/>.
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
;
49 uint32_t num_processed
;
50 struct ldb_dn
*ncRoot_dn
;
51 struct GUID ncRoot_guid
;
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
;
61 struct la_for_sorting
*la_sorted
;
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
) {
77 } else if (h1
->highest_usn
> h2
->highest_usn
) {
79 } else if (h1
->tmp_highest_usn
< h2
->tmp_highest_usn
) {
81 } else if (h1
->tmp_highest_usn
> h2
->tmp_highest_usn
) {
83 } else if (h1
->reserved_usn
< h2
->reserved_usn
) {
85 } else if (h1
->reserved_usn
> h2
->reserved_usn
) {
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
;
101 identifier
= talloc(mem_ctx
, struct drsuapi_DsReplicaObjectIdentifier
);
102 if (identifier
== 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");
111 identifier
->sid
= *sid
;
113 ZERO_STRUCT(identifier
->sid
);
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
) {
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
)
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"));
166 werr
= dsdb_attribute_drsuapi_remote_to_local(ctx
,
167 remote_attid_as_enum
,
170 if (!W_ERROR_IS_OK(werr
)) {
171 DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n"));
178 sa
= dsdb_attribute_by_attributeID_id(schema
, remote_attid_as_enum
);
180 return WERR_DS_DRA_SCHEMA_MISMATCH
;
182 if (local_attid_as_enum
!= NULL
) {
183 *local_attid_as_enum
= sa
->attributeID_id
;
193 * Similar to function in repl_meta_data without the extra
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 */
201 *pdn
= talloc_zero_array(mem_ctx
, struct parsed_dn
,
204 return WERR_DS_DRA_INTERNAL_ERROR
;
207 for (i
= 0; i
< el
->num_values
; i
++) {
208 (*pdn
)[i
].v
= &el
->values
[i
];
214 static WERROR
getncchanges_update_revealed_list(struct ldb_context
*sam_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
;
225 char *attr_str
= NULL
;
226 char *attr_hex
= NULL
;
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
)) {
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
;
259 DATA_BLOB partial_meta
;
261 werr
= get_parsed_dns_trusted(mem_ctx
, existing
, &link_dns
);
262 if (!W_ERROR_IS_OK(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
,
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
;
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
,
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;
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;
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
,
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
,
350 struct ldb_message
**revealed_list_msg
,
351 struct ldb_message
*existing_revealed_list_msg
)
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
&&
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
);
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
;
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 */
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
,
393 &md
.ctr
.ctr1
.array
[i
],
394 existing_revealed_list_msg
);
395 if (!W_ERROR_IS_OK(werr
)) {
400 /* filter by uptodateness_vector */
401 if (md
.ctr
.ctr1
.array
[i
].attid
!= DRSUAPI_ATTID_instanceType
&&
403 udv_filter(uptodateness_vector
,
404 &md
.ctr
.ctr1
.array
[i
].originating_invocation_id
,
405 md
.ctr
.ctr1
.array
[i
].originating_usn
)) {
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
) {
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
;
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
,
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
,
450 struct ldb_dn
*machine_dn
,
451 const struct GUID
*guid
)
453 const struct ldb_val
*md_value
;
455 struct replPropertyMetaDataBlob md
;
458 enum ndr_err_code ndr_err
;
461 const struct dsdb_attribute
*rdn_sa
;
463 unsigned int instanceType
;
464 struct dsdb_syntax_ctx syntax_ctx
;
465 struct ldb_result
*res
= NULL
;
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
;
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");
495 /* nothing to send */
499 if (instanceType
& INSTANCE_TYPE_UNINSTANT
) {
500 /* don't send uninstantiated objects */
504 if (uSNChanged
<= highest_usn
) {
505 /* nothing to send */
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
);
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",
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
,
577 partial_attribute_set
, local_pas
,
581 existing_revealed_list_msg
);
582 if (!W_ERROR_IS_OK(werr
)) {
583 ldb_transaction_cancel(sam_ctx
);
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
;
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
,
612 if (!W_ERROR_IS_OK(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
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
628 attids
[0] == DRSUAPI_ATTID_instanceType
&&
629 !force_object_return
)) {
630 talloc_free(obj
->meta_data_ctr
);
631 obj
->meta_data_ctr
= NULL
;
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
]);
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
);
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
);
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
),
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
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
),
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: "
699 ldb_dn_get_linearized(msg
->dn
),
701 obj
->object
.attribute_ctr
.attributes
[i
].attid
));
702 return WERR_DS_DATABASE_ERROR
;
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
,
723 struct drsuapi_DsReplicaLinkedAttribute
*la
;
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;
739 /* We have to check that the inactive link still point to an existing object */
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
)));
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
)));
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
),
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
);
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
,
841 struct ldb_dn
*ncRoot_dn
,
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
,
849 struct drsuapi_DsReplicaCursorCtrEx
*uptodateness_vector
)
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
) {
862 if (uSNChanged
<= highest_usn
) {
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
;
876 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, el
->name
);
878 if (!sa
|| sa
->linkID
== 0 || (sa
->linkID
& 1)) {
879 /* we only want forward links */
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 */
889 for (j
=0; j
<el
->num_values
; j
++) {
890 struct dsdb_dn
*dsdb_dn
;
892 uint64_t originating_usn
;
893 NTSTATUS status
, status2
;
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
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
) {
924 status
= dsdb_get_extended_dn_guid(dsdb_dn
->dn
,
925 &originating_invocation_id
,
927 status2
= dsdb_get_extended_dn_uint64(dsdb_dn
->dn
,
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
,
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
);
949 talloc_free(tmp_ctx
);
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
)
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
;
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
,
984 c
= memcmp(la1
->source_guid
,
985 la2
->source_guid
, sizeof(la2
->source_guid
));
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
{
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
)
1019 ret
= ldb_dn_compare(getnc_state
->ncRoot_dn
, m1
->dn
);
1024 ret
= ldb_dn_compare(getnc_state
->ncRoot_dn
, m2
->dn
);
1029 if (m1
->usn
== m2
->usn
) {
1030 return ldb_dn_compare(m2
->dn
, m1
->dn
);
1033 if (m1
->usn
< m2
->usn
) {
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
;
1052 struct ldb_context
*ldb
= b_state
->sam_ctx
;
1053 struct ldb_result
*ext_res
;
1054 struct dsdb_fsmo_extended_op
*exop
;
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
;
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
;
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
;
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
;
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
;
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
,
1150 const struct dom_sid
***sids
)
1152 struct ldb_message_element
*el
;
1155 el
= ldb_msg_find_element(msg
, attr
);
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
]);
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
;
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
,
1191 const struct dom_sid
***sids
,
1192 const struct dom_sid
**additional_sids
,
1193 unsigned int num_additional
)
1195 struct ldb_message_element
*el
;
1198 el
= ldb_msg_find_element(msg
, attr
);
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
;
1224 for (j
= 0; j
< num_additional
; j
++) {
1225 (*sids
)[i
++] = additional_sids
[j
];
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
)
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
])) {
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
;
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
;
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
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
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,
1300 if (ret
!= LDB_SUCCESS
) {
1304 server_dn
= ldb_dn_get_parent(mem_ctx
, ntds_dn
);
1305 if (server_dn
== NULL
) {
1309 ret
= samdb_reference_dn(b_state
->sam_ctx_system
, mem_ctx
, server_dn
,
1310 "serverReference", machine_dn
);
1312 if (ret
!= LDB_SUCCESS
) {
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
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 ...>
1328 * reveal secrets = false
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
) {
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
)) {
1361 additional_sids
[0] = object_sid
;
1364 * Must be an RODC account at this point, verify machine DN matches the
1367 if (ldb_dn_compare(rodc_res
->msgs
[0]->dn
, *machine_dn
) != 0) {
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) {
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
)) {
1385 if (ldb_msg_find_attr_as_uint(obj_res
->msgs
[0],
1386 "userAccountControl", 0) &
1387 UF_INTERDOMAIN_TRUST_ACCOUNT
) {
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
)) {
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
)) {
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
) {
1415 if (never_reveal_sids
&&
1416 sid_list_match(token_sids
, never_reveal_sids
)) {
1421 sid_list_match(token_sids
, reveal_sids
)) {
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
;
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;
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
;
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
;
1479 struct ldb_context
*ldb
= b_state
->sam_ctx
;
1480 struct ldb_message
*msg
;
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
;
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
;
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
;
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
)));
1525 ctr6
->extended_ret
= DRSUAPI_EXOP_ERR_UNKNOWN_CALLER
;
1529 ret
= ldb_msg_add_string(msg
, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn
));
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
;
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
;
1576 struct dsdb_schema
*schema
;
1577 struct dsdb_syntax_ctx syntax_ctx
;
1579 *is_secret_request
= true;
1581 exop
= req10
->extended_op
;
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;
1592 case DRSUAPI_EXOP_REPL_SECRET
:
1593 case DRSUAPI_EXOP_REPL_OBJ
:
1594 case DRSUAPI_EXOP_NONE
:
1598 if (req10
->replica_flags
& DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
) {
1599 *is_secret_request
= false;
1603 if (exop
== DRSUAPI_EXOP_REPL_SECRET
||
1604 req10
->partial_attribute_set
== NULL
) {
1605 /* they want secrets */
1606 *is_secret_request
= true;
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
,
1619 req10
->partial_attribute_set
->attids
[i
],
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
)));
1629 if (!dsdb_attr_in_rodc_fas(sa
)) {
1630 *is_secret_request
= true;
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
,
1641 req10
->partial_attribute_set_ex
->attids
[i
],
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
)));
1651 if (!dsdb_attr_in_rodc_fas(sa
)) {
1652 *is_secret_request
= true;
1658 *is_secret_request
= false;
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
;
1673 struct dsdb_schema
*schema
;
1674 struct dsdb_syntax_ctx syntax_ctx
;
1676 exop
= req10
->extended_op
;
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;
1687 case DRSUAPI_EXOP_REPL_OBJ
:
1688 case DRSUAPI_EXOP_NONE
:
1692 if (req10
->partial_attribute_set
== NULL
) {
1693 /* they want it all */
1694 *is_gc_pas_request
= false;
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
,
1707 req10
->partial_attribute_set
->attids
[i
],
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
)));
1717 if (!sa
->isMemberOfPartialAttributeSet
) {
1718 *is_gc_pas_request
= false;
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
,
1729 req10
->partial_attribute_set_ex
->attids
[i
],
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
)));
1739 if (!sa
->isMemberOfPartialAttributeSet
) {
1740 *is_gc_pas_request
= false;
1746 *is_gc_pas_request
= true;
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
) {
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
;
1781 static const char *collect_objects_attrs
[] = { "uSNChanged",
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
)
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));
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))",
1826 if (req10
->replica_flags
& DRSUAPI_DRS_ASYNC_REP
) {
1827 scope
= LDB_SCOPE_BASE
;
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
,
1838 collect_objects_attrs
,
1840 if (ret
!= LDB_SUCCESS
) {
1841 return WERR_DS_DRA_INTERNAL_ERROR
;
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
) {
1863 switch (req10
->extended_op
) {
1864 case DRSUAPI_EXOP_FSMO_RID_ALLOC
:
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
,
1879 search_dn
, LDB_SCOPE_BASE
,
1880 collect_objects_attrs
,
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
)));
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
));
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,
1906 if (ret
!= LDB_SUCCESS
) {
1907 DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Unable to find NTDS object for guid %s - %s\n",
1909 &req10
->destination_dsa_guid
),
1910 ldb_errstring(b_state
->sam_ctx
)));
1912 return WERR_DS_DRA_INTERNAL_ERROR
;
1915 server_dn
= ldb_dn_get_parent(frame
, ntds_dn
);
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
)));
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
)));
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
,
1945 rid_set_dn
, LDB_SCOPE_BASE
,
1946 collect_objects_attrs
,
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
)));
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
));
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
,
1967 machine_dn
, LDB_SCOPE_BASE
,
1968 collect_objects_attrs
,
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
)));
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
));
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
) {
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]);
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
,
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.
2034 if (uSN
<= hwm
->tmp_highest_usn
) {
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, };
2049 .length
= sizeof(guid_buf
),
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
;
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, };
2082 .length
= sizeof(guid_buf
),
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
);
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
;
2115 struct dsdb_schema
*schema
;
2116 struct drsuapi_DsReplicaOIDMapping_Ctr
*ctr
;
2117 struct drsuapi_DsReplicaObjectListItemEx
**currentObject
;
2119 DATA_BLOB session_key
;
2121 struct dcesrv_handle
*h
;
2122 struct drsuapi_bind_state
*b_state
;
2123 struct drsuapi_getncchanges_state
*getnc_state
;
2124 struct drsuapi_DsGetNCChangesRequest10
*req10
;
2126 uint32_t max_objects
;
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
;
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
;
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
);
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
) {
2181 req10
= getncchanges_map_req8(mem_ctx
, &r
->in
.req
->req8
);
2182 if (req10
== NULL
) {
2183 return WERR_NOT_ENOUGH_MEMORY
;
2187 req10
= &r
->in
.req
->req10
;
2190 DEBUG(0,(__location__
": Request for DsGetNCChanges with unsupported level %u\n",
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
,
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
)) {
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
;
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"));
2246 DEBUG(0,(__location__
": Failed to decode remote prefixMap: %s\n",
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
)) {
2258 if (is_gc_pas_request
) {
2259 werr
= drs_security_access_check_nc_root(b_state
->sam_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
)) {
2269 werr
= dcesrv_drsuapi_is_reveal_secrets_request(b_state
, req10
,
2271 &is_secret_request
);
2272 if (!W_ERROR_IS_OK(werr
)) {
2275 if (is_secret_request
) {
2276 werr
= drs_security_access_check_nc_root(b_state
->sam_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
) {
2287 has_get_all_changes
= true;
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 */
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
);
2336 ret
= drsuapi_DsReplicaHighWaterMark_cmp(&getnc_state
->last_hwm
,
2337 &req10
->highwatermark
);
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
);
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
:
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
) {
2396 case DRSUAPI_EXOP_REPL_SECRET
:
2397 werr
= getncchanges_repl_secret(b_state
, mem_ctx
, req10
,
2400 has_get_all_changes
,
2402 r
->out
.result
= werr
;
2403 W_ERROR_NOT_OK_RETURN(werr
);
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
) {
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
) {
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
) {
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
);
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
;
2474 getnc_state
->min_usn
= req10
->highwatermark
.highest_usn
;
2475 for (i
= 0; i
< udv
->count
; i
++) {
2477 const struct drsuapi_DsReplicaCursor
*cur
=
2480 match
= GUID_equal(&invocation_id
,
2481 &cur
->source_dsa_invocation_id
);
2485 if (cur
->highest_usn
> getnc_state
->min_usn
) {
2486 getnc_state
->min_usn
= cur
->highest_usn
;
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
)) {
2503 if (req10
->extended_op
== DRSUAPI_EXOP_NONE
) {
2504 werr
= getncchanges_collect_objects(b_state
, mem_ctx
, req10
,
2505 search_dn
, extra_filter
,
2508 werr
= getncchanges_collect_objects_exop(b_state
, mem_ctx
, req10
,
2510 search_dn
, extra_filter
,
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
) {
2539 LDB_TYPESAFE_QSORT(changes
,
2540 getnc_state
->num_records
,
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
) {
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
);
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
;
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
,
2643 req10
->partial_attribute_set
->attids
[j
],
2644 (enum drsuapi_DsAttributeId
*)&local_pas
[j
],
2648 LDB_TYPESAFE_QSORT(local_pas
,
2649 req10
->partial_attribute_set
->num_attids
,
2654 for (i
=getnc_state
->num_processed
;
2655 i
<getnc_state
->num_records
&&
2657 (r
->out
.ctr
->ctr6
.object_count
< max_objects
)
2658 && !max_wait_reached
;
2660 struct drsuapi_DsReplicaObjectListItemEx
*new_objs
= NULL
;
2661 struct drsuapi_DsReplicaObjectListItemEx
*obj
;
2662 struct ldb_message
*msg
;
2663 static const char * const msg_attrs
[] = {
2665 "nTSecurityDescriptor",
2667 "replPropertyMetaData",
2668 DSDB_SECRET_ATTRIBUTES
,
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
,
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
)));
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 */
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
,
2727 local_pas
, machine_dn
,
2728 &getnc_state
->guids
[i
]);
2729 if (!W_ERROR_IS_OK(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
,
2739 &getnc_state
->la_list
,
2740 &getnc_state
->la_count
,
2741 req10
->uptodateness_vector
);
2742 if (!W_ERROR_IS_OK(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 */
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
)) {
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
,
2778 if (W_ERROR_EQUAL(werr
, WERR_OBJECT_NAME_EXISTS
)) {
2780 * We don't need to send it twice.
2784 if (W_ERROR_IS_OK(werr
)) {
2785 return WERR_INTERNAL_ERROR
;
2787 if (!W_ERROR_EQUAL(werr
, WERR_OBJECT_NOT_FOUND
)) {
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
,
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
,
2815 obj_str
= ldb_dn_get_extended_linearized(anc_obj
,
2819 DBG_ERR("getncchanges: failed to fetch ANC "
2820 "DN %s for DN %s - %s\n",
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
,
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
,
2838 false, /* force_object_return */
2842 if (!W_ERROR_IS_OK(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
,
2852 &getnc_state
->la_list
,
2853 &getnc_state
->la_count
,
2854 req10
->uptodateness_vector
);
2855 if (!W_ERROR_IS_OK(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
,
2865 if (!W_ERROR_IS_OK(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
;
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
,
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
,
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
) {
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;
2963 /* sort the whole array the first time */
2964 if (getnc_state
->la_sorted
== NULL
) {
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 */
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"));
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
;
2995 /* Repack the source GUID as NDR for sorting */
2996 status
= GUID_to_ndr_blob(&la
->identifier
->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"));
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
));
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
;
3069 ret
= drsuapi_DsReplicaHighWaterMark_cmp(&r
->out
.ctr
->ctr6
.old_highwatermark
,
3070 &r
->out
.ctr
->ctr6
.new_highwatermark
);
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
)));
3106 if (!r
->out
.ctr
->ctr6
.more_data
&& req10
->extended_op
!= DRSUAPI_EXOP_NONE
) {
3107 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges
, NDR_BOTH
, r
);