Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : implement the DSGetNCChanges call
5 :
6 : Copyright (C) Anatoliy Atanasov 2009
7 : Copyright (C) Andrew Tridgell 2009-2010
8 : Copyright (C) Andrew Bartlett 2010-2016
9 :
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.
14 :
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.
19 :
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/>.
22 : */
23 :
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 "../libcli/drsuapi/drsuapi.h"
35 : #include "lib/util/binsearch.h"
36 : #include "lib/util/tsort.h"
37 : #include "auth/session.h"
38 : #include "dsdb/common/util.h"
39 : #include "lib/dbwrap/dbwrap.h"
40 : #include "lib/dbwrap/dbwrap_rbt.h"
41 : #include "librpc/gen_ndr/ndr_misc.h"
42 :
43 : #undef DBGC_CLASS
44 : #define DBGC_CLASS DBGC_DRS_REPL
45 :
46 : #define DRS_GUID_SIZE 16
47 : #define DEFAULT_MAX_OBJECTS 1000
48 : #define DEFAULT_MAX_LINKS 1500
49 :
50 : /*
51 : * state of a partially-completed replication cycle. This state persists
52 : * over multiple calls to dcesrv_drsuapi_DsGetNCChanges()
53 : */
54 : struct drsuapi_getncchanges_state {
55 : struct db_context *obj_cache;
56 : struct GUID *guids;
57 : uint32_t num_records;
58 : uint32_t num_processed;
59 : struct ldb_dn *ncRoot_dn;
60 : struct GUID ncRoot_guid;
61 : bool is_schema_nc;
62 : bool is_get_anc;
63 : bool broken_samba_4_5_get_anc_emulation;
64 : bool is_get_tgt;
65 : bool send_nc_root_first;
66 : uint64_t min_usn;
67 : uint64_t max_usn;
68 : struct drsuapi_DsReplicaHighWaterMark last_hwm;
69 : struct ldb_dn *last_dn;
70 : struct drsuapi_DsReplicaHighWaterMark final_hwm;
71 : struct drsuapi_DsReplicaCursor2CtrEx *final_udv;
72 : struct drsuapi_DsReplicaLinkedAttribute *la_list;
73 : uint32_t la_count;
74 : uint32_t la_idx;
75 :
76 : /* these are just used for debugging the replication's progress */
77 : uint32_t links_given;
78 : uint32_t total_links;
79 : };
80 :
81 : /* We must keep the GUIDs in NDR form for sorting */
82 : struct la_for_sorting {
83 : const struct drsuapi_DsReplicaLinkedAttribute *link;
84 : uint8_t target_guid[DRS_GUID_SIZE];
85 : uint8_t source_guid[DRS_GUID_SIZE];
86 : };
87 :
88 : /*
89 : * stores the state for a chunk of replication data. This state information
90 : * only exists for a single call to dcesrv_drsuapi_DsGetNCChanges()
91 : */
92 : struct getncchanges_repl_chunk {
93 : uint32_t max_objects;
94 : uint32_t max_links;
95 : uint32_t tgt_la_count;
96 : bool immediate_link_sync;
97 : time_t max_wait;
98 : time_t start;
99 :
100 : /* stores the objects to be sent in this chunk */
101 : uint32_t object_count;
102 : struct drsuapi_DsReplicaObjectListItemEx *object_list;
103 :
104 : /* the last object added to this replication chunk */
105 : struct drsuapi_DsReplicaObjectListItemEx *last_object;
106 : };
107 :
108 4355 : static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1,
109 : const struct drsuapi_DsReplicaHighWaterMark *h2)
110 : {
111 4355 : if (h1->highest_usn < h2->highest_usn) {
112 0 : return -1;
113 4355 : } else if (h1->highest_usn > h2->highest_usn) {
114 0 : return 1;
115 4355 : } else if (h1->tmp_highest_usn < h2->tmp_highest_usn) {
116 1832 : return -1;
117 2523 : } else if (h1->tmp_highest_usn > h2->tmp_highest_usn) {
118 20 : return 1;
119 2503 : } else if (h1->reserved_usn < h2->reserved_usn) {
120 0 : return -1;
121 2503 : } else if (h1->reserved_usn > h2->reserved_usn) {
122 1 : return 1;
123 : }
124 :
125 2502 : return 0;
126 : }
127 :
128 : /*
129 : build a DsReplicaObjectIdentifier from a ldb msg
130 : */
131 674883 : static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
132 : const struct ldb_message *msg)
133 : {
134 0 : struct drsuapi_DsReplicaObjectIdentifier *identifier;
135 0 : struct dom_sid *sid;
136 :
137 674883 : identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
138 674883 : if (identifier == NULL) {
139 0 : return NULL;
140 : }
141 :
142 674883 : identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
143 674883 : identifier->guid = samdb_result_guid(msg, "objectGUID");
144 :
145 674883 : sid = samdb_result_dom_sid(identifier, msg, "objectSid");
146 674883 : if (sid) {
147 108633 : identifier->sid = *sid;
148 : } else {
149 566250 : ZERO_STRUCT(identifier->sid);
150 : }
151 674883 : return identifier;
152 : }
153 :
154 2142020 : static int udv_compare(const struct GUID *guid1, struct GUID guid2)
155 : {
156 2142020 : return GUID_compare(guid1, &guid2);
157 : }
158 :
159 : /*
160 : see if we can filter an attribute using the uptodateness_vector
161 : */
162 8205676 : static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
163 : const struct GUID *originating_invocation_id,
164 : uint64_t originating_usn)
165 : {
166 0 : const struct drsuapi_DsReplicaCursor *c;
167 8205676 : if (udv == NULL) return false;
168 2152104 : BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id,
169 : originating_invocation_id, udv_compare, c);
170 1331405 : if (c && originating_usn <= c->highest_usn) {
171 909443 : return true;
172 : }
173 421962 : return false;
174 : }
175 :
176 3689588 : static int uint32_t_cmp(uint32_t a1, uint32_t a2)
177 : {
178 3689588 : if (a1 == a2) return 0;
179 3315345 : return a1 > a2 ? 1 : -1;
180 : }
181 :
182 3766331 : static int uint32_t_ptr_cmp(uint32_t *a1, uint32_t *a2)
183 : {
184 3766331 : if (*a1 == *a2) return 0;
185 3766331 : return *a1 > *a2 ? 1 : -1;
186 : }
187 :
188 771277 : static WERROR getncchanges_attid_remote_to_local(const struct dsdb_schema *schema,
189 : const struct dsdb_syntax_ctx *ctx,
190 : enum drsuapi_DsAttributeId remote_attid_as_enum,
191 : enum drsuapi_DsAttributeId *local_attid_as_enum,
192 : const struct dsdb_attribute **_sa)
193 : {
194 0 : WERROR werr;
195 771277 : const struct dsdb_attribute *sa = NULL;
196 :
197 771277 : if (ctx->pfm_remote == NULL) {
198 180707 : DEBUG(7, ("No prefixMap supplied, falling back to local prefixMap.\n"));
199 180707 : goto fail;
200 : }
201 :
202 590570 : werr = dsdb_attribute_drsuapi_remote_to_local(ctx,
203 : remote_attid_as_enum,
204 : local_attid_as_enum,
205 : _sa);
206 590570 : if (!W_ERROR_IS_OK(werr)) {
207 3 : DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n"));
208 3 : goto fail;
209 : }
210 :
211 590567 : return werr;
212 180710 : fail:
213 :
214 180710 : sa = dsdb_attribute_by_attributeID_id(schema, remote_attid_as_enum);
215 180710 : if (sa == NULL) {
216 3 : return WERR_DS_DRA_SCHEMA_MISMATCH;
217 : } else {
218 180707 : if (local_attid_as_enum != NULL) {
219 180502 : *local_attid_as_enum = sa->attributeID_id;
220 : }
221 180707 : if (_sa != NULL) {
222 205 : *_sa = sa;
223 : }
224 180707 : return WERR_OK;
225 : }
226 : }
227 :
228 15577 : static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
229 : TALLOC_CTX *mem_ctx,
230 : struct ldb_message **msg,
231 : struct ldb_dn *object_dn,
232 : const struct GUID *object_guid,
233 : const struct dsdb_attribute *sa,
234 : struct replPropertyMetaData1 *meta_data,
235 : struct ldb_message *revealed_users)
236 : {
237 0 : enum ndr_err_code ndr_err;
238 0 : int ldb_err;
239 15577 : char *attr_str = NULL;
240 15577 : char *attr_hex = NULL;
241 0 : DATA_BLOB attr_blob;
242 15577 : struct ldb_message_element *existing = NULL, *el_add = NULL, *el_del = NULL;
243 15577 : const char * const * secret_attributes = ldb_get_opaque(sam_ctx, "LDB_SECRET_ATTRIBUTE_LIST");
244 :
245 15577 : if (!ldb_attr_in_list(secret_attributes,
246 15577 : sa->lDAPDisplayName)) {
247 237 : return WERR_OK;
248 : }
249 :
250 :
251 15340 : ndr_err = ndr_push_struct_blob(&attr_blob, mem_ctx, meta_data, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaData1);
252 15340 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
253 0 : return WERR_DS_DRA_INTERNAL_ERROR;
254 : }
255 :
256 15340 : attr_hex = hex_encode_talloc(mem_ctx, attr_blob.data, attr_blob.length);
257 15340 : if (attr_hex == NULL) {
258 0 : return WERR_NOT_ENOUGH_MEMORY;
259 : }
260 :
261 15340 : attr_str = talloc_asprintf(mem_ctx, "B:%zd:%s:%s", attr_blob.length*2, attr_hex, ldb_dn_get_linearized(object_dn));
262 15340 : if (attr_str == NULL) {
263 0 : return WERR_NOT_ENOUGH_MEMORY;
264 : }
265 :
266 15340 : existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
267 15340 : if (existing != NULL) {
268 : /* Replace the old value (if one exists) with the current one */
269 0 : struct parsed_dn *link_dns;
270 15050 : struct parsed_dn *exact = NULL, *unused = NULL;
271 0 : uint8_t attid[4];
272 0 : DATA_BLOB partial_meta;
273 :
274 15050 : ldb_err = get_parsed_dns_trusted(mem_ctx, existing, &link_dns);
275 15050 : if (ldb_err != LDB_SUCCESS) {
276 0 : return WERR_DS_DRA_INTERNAL_ERROR;
277 : }
278 :
279 : /* Construct a partial metadata blob to match on in the DB */
280 15050 : SIVAL(attid, 0, sa->attributeID_id);
281 15050 : partial_meta.length = 4;
282 15050 : partial_meta.data = attid;
283 :
284 : /* Binary search using GUID and attribute id for uniqueness */
285 15050 : ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values,
286 : object_guid, object_dn,
287 : partial_meta, 4,
288 : &exact, &unused,
289 : DSDB_SYNTAX_BINARY_DN, true);
290 :
291 15050 : if (ldb_err != LDB_SUCCESS) {
292 0 : DEBUG(0,(__location__ ": Failed parsed DN find - %s\n",
293 : ldb_errstring(sam_ctx)));
294 0 : return WERR_DS_DRA_INTERNAL_ERROR;
295 : }
296 :
297 15050 : if (exact != NULL) {
298 : /* Perform some verification of the blob */
299 0 : struct replPropertyMetaData1 existing_meta_data;
300 450 : ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_dn->extra_part,
301 : &existing_meta_data,
302 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
303 450 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
304 0 : return WERR_DS_DRA_INTERNAL_ERROR;
305 : }
306 :
307 450 : if (existing_meta_data.attid == sa->attributeID_id) {
308 450 : ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
309 450 : if (ldb_err != LDB_SUCCESS) {
310 0 : return WERR_DS_DRA_INTERNAL_ERROR;
311 : }
312 :
313 450 : el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
314 450 : if (el_del->values == NULL) {
315 0 : return WERR_NOT_ENOUGH_MEMORY;
316 : }
317 450 : el_del->values[0] = *exact->v;
318 450 : el_del->num_values = 1;
319 : } else {
320 0 : return WERR_DS_DRA_INTERNAL_ERROR;
321 : }
322 : }
323 : }
324 :
325 15340 : ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_ADD, &el_add);
326 15340 : if (ldb_err != LDB_SUCCESS) {
327 0 : return WERR_DS_DRA_INTERNAL_ERROR;
328 : }
329 :
330 15340 : el_add->values = talloc_array((*msg)->elements, struct ldb_val, 1);
331 15340 : if (el_add->values == NULL) {
332 0 : return WERR_NOT_ENOUGH_MEMORY;
333 :
334 : }
335 :
336 15340 : el_add->values[0] = data_blob_string_const(attr_str);
337 15340 : el_add->num_values = 1;
338 :
339 15340 : return WERR_OK;
340 : }
341 :
342 : /*
343 : * This function filter attributes for build_object based on the
344 : * uptodatenessvector and partial attribute set.
345 : *
346 : * Any secret attributes are forced here for REPL_SECRET, and audited at this
347 : * point with msDS-RevealedUsers.
348 : */
349 652557 : static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItemEx *obj,
350 : struct replPropertyMetaDataBlob md,
351 : struct ldb_context *sam_ctx,
352 : const struct ldb_message *msg,
353 : const struct GUID *guid,
354 : uint32_t *count,
355 : uint64_t highest_usn,
356 : const struct dsdb_attribute *rdn_sa,
357 : struct dsdb_schema *schema,
358 : struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
359 : struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
360 : uint32_t *local_pas,
361 : uint32_t *attids,
362 : bool exop_secret,
363 : struct ldb_message **revealed_list_msg,
364 : struct ldb_message *existing_revealed_list_msg)
365 : {
366 0 : uint32_t i, n;
367 0 : WERROR werr;
368 10242200 : for (n=i=0; i<md.ctr.ctr1.count; i++) {
369 0 : const struct dsdb_attribute *sa;
370 9589643 : bool force_attribute = false;
371 :
372 : /* if the attribute has not changed, and it is not the
373 : instanceType then don't include it */
374 9589643 : if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
375 130855 : !exop_secret &&
376 130855 : md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) continue;
377 :
378 : /* don't include the rDN */
379 9477749 : if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
380 :
381 8850659 : sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
382 8850659 : if (!sa) {
383 0 : DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n",
384 : (unsigned int)md.ctr.ctr1.array[i].attid,
385 : ldb_dn_get_linearized(msg->dn)));
386 0 : return WERR_DS_DRA_INTERNAL_ERROR;
387 : }
388 :
389 8850659 : if (sa->linkID) {
390 0 : struct ldb_message_element *el;
391 118 : el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
392 118 : if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
393 : /* don't send upgraded links inline */
394 0 : continue;
395 : }
396 : }
397 :
398 8850659 : if (exop_secret &&
399 68841 : !dsdb_attr_in_rodc_fas(sa)) {
400 15577 : force_attribute = true;
401 15577 : DEBUG(4,("Forcing attribute %s in %s\n",
402 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
403 15577 : werr = getncchanges_update_revealed_list(sam_ctx, obj,
404 : revealed_list_msg,
405 15577 : msg->dn, guid, sa,
406 15577 : &md.ctr.ctr1.array[i],
407 : existing_revealed_list_msg);
408 15577 : if (!W_ERROR_IS_OK(werr)) {
409 0 : return werr;
410 : }
411 : }
412 :
413 : /* filter by uptodateness_vector */
414 8850659 : if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType &&
415 16380627 : !force_attribute &&
416 8182525 : udv_filter(uptodateness_vector,
417 8182525 : &md.ctr.ctr1.array[i].originating_invocation_id,
418 8182525 : md.ctr.ctr1.array[i].originating_usn)) {
419 908618 : continue;
420 : }
421 :
422 : /* filter by partial_attribute_set */
423 7942041 : if (partial_attribute_set && !force_attribute) {
424 427359 : uint32_t *result = NULL;
425 3742704 : BINARY_ARRAY_SEARCH_V(local_pas, partial_attribute_set->num_attids, sa->attributeID_id,
426 : uint32_t_cmp, result);
427 427359 : if (result == NULL) {
428 53116 : continue;
429 : }
430 : }
431 :
432 7888925 : obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
433 7888925 : obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
434 7888925 : obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
435 7888925 : obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
436 7888925 : attids[n] = md.ctr.ctr1.array[i].attid;
437 :
438 7888925 : n++;
439 : }
440 :
441 652557 : *count = n;
442 :
443 652557 : return WERR_OK;
444 : }
445 :
446 : /*
447 : drsuapi_DsGetNCChanges for one object
448 : */
449 653070 : static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
450 : const struct ldb_message *msg,
451 : struct ldb_context *sam_ctx,
452 : struct drsuapi_getncchanges_state *getnc_state,
453 : struct dsdb_schema *schema,
454 : DATA_BLOB *session_key,
455 : struct drsuapi_DsGetNCChangesRequest10 *req10,
456 : bool force_object_return,
457 : uint32_t *local_pas,
458 : struct ldb_dn *machine_dn,
459 : const struct GUID *guid)
460 : {
461 0 : const struct ldb_val *md_value;
462 0 : uint32_t i, n;
463 0 : struct replPropertyMetaDataBlob md;
464 653070 : uint32_t rid = 0;
465 0 : int ldb_err;
466 0 : enum ndr_err_code ndr_err;
467 0 : uint32_t *attids;
468 0 : const char *rdn;
469 0 : const struct dsdb_attribute *rdn_sa;
470 0 : uint64_t uSNChanged;
471 0 : unsigned int instanceType;
472 0 : struct dsdb_syntax_ctx syntax_ctx;
473 653070 : struct ldb_result *res = NULL;
474 0 : WERROR werr;
475 0 : int ret;
476 653070 : uint32_t replica_flags = req10->replica_flags;
477 653070 : struct drsuapi_DsPartialAttributeSet *partial_attribute_set =
478 : req10->partial_attribute_set;
479 653070 : struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector =
480 : req10->uptodateness_vector;
481 653070 : enum drsuapi_DsExtendedOperation extended_op = req10->extended_op;
482 653070 : bool is_schema_nc = getnc_state->is_schema_nc;
483 653070 : uint64_t highest_usn = getnc_state->min_usn;
484 :
485 : /* make dsdb syntax context for conversions */
486 653070 : dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
487 653070 : syntax_ctx.is_schema_nc = is_schema_nc;
488 :
489 653070 : uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
490 653070 : instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
491 653070 : if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
492 1455 : obj->is_nc_prefix = true;
493 1455 : obj->parent_object_guid = NULL;
494 : } else {
495 651615 : obj->is_nc_prefix = false;
496 651615 : obj->parent_object_guid = talloc(obj, struct GUID);
497 651615 : if (obj->parent_object_guid == NULL) {
498 0 : return WERR_DS_DRA_INTERNAL_ERROR;
499 : }
500 651615 : *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
501 651615 : if (GUID_all_zero(obj->parent_object_guid)) {
502 0 : DEBUG(0,(__location__ ": missing parentGUID for %s\n",
503 : ldb_dn_get_linearized(msg->dn)));
504 0 : return WERR_DS_DRA_INTERNAL_ERROR;
505 : }
506 : }
507 653070 : obj->next_object = NULL;
508 :
509 653070 : md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
510 653070 : if (!md_value) {
511 : /* nothing to send */
512 0 : return WERR_OK;
513 : }
514 :
515 653070 : if (instanceType & INSTANCE_TYPE_UNINSTANT) {
516 : /* don't send uninstantiated objects */
517 0 : return WERR_OK;
518 : }
519 :
520 653070 : if (uSNChanged <= highest_usn) {
521 : /* nothing to send */
522 513 : return WERR_OK;
523 : }
524 :
525 652557 : ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
526 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
527 652557 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
528 0 : return WERR_DS_DRA_INTERNAL_ERROR;
529 : }
530 :
531 652557 : if (md.version != 1) {
532 0 : return WERR_DS_DRA_INTERNAL_ERROR;
533 : }
534 :
535 652557 : rdn = ldb_dn_get_rdn_name(msg->dn);
536 652557 : if (rdn == NULL) {
537 0 : DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
538 0 : return WERR_DS_DRA_INTERNAL_ERROR;
539 : }
540 :
541 652557 : rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
542 652557 : if (rdn_sa == NULL) {
543 0 : DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n",
544 : rdn, ldb_dn_get_linearized(msg->dn)));
545 0 : return WERR_DS_DRA_INTERNAL_ERROR;
546 : }
547 :
548 652557 : obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
549 652557 : attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
550 :
551 652557 : obj->object.identifier = get_object_identifier(obj, msg);
552 652557 : if (obj->object.identifier == NULL) {
553 0 : return WERR_NOT_ENOUGH_MEMORY;
554 : }
555 652557 : dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
556 :
557 652557 : obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
558 :
559 652557 : if (extended_op == DRSUAPI_EXOP_REPL_SECRET) {
560 : /* Get the existing revealed users for the destination */
561 3068 : struct ldb_message *revealed_list_msg = NULL;
562 3068 : struct ldb_message *existing_revealed_list_msg = NULL;
563 3068 : const char *machine_attrs[] = {
564 : "msDS-RevealedUsers",
565 : NULL
566 : };
567 :
568 3068 : revealed_list_msg = ldb_msg_new(sam_ctx);
569 3068 : if (revealed_list_msg == NULL) {
570 0 : return WERR_NOT_ENOUGH_MEMORY;
571 : }
572 3068 : revealed_list_msg->dn = machine_dn;
573 :
574 3068 : ret = ldb_transaction_start(sam_ctx);
575 3068 : if (ret != LDB_SUCCESS) {
576 0 : DEBUG(0,(__location__ ": Failed transaction start - %s\n",
577 : ldb_errstring(sam_ctx)));
578 0 : return WERR_DS_DRA_INTERNAL_ERROR;
579 : }
580 :
581 3068 : ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
582 3068 : if (ldb_err != LDB_SUCCESS || res->count != 1) {
583 0 : ldb_transaction_cancel(sam_ctx);
584 0 : return WERR_DS_DRA_INTERNAL_ERROR;
585 : }
586 :
587 3068 : existing_revealed_list_msg = res->msgs[0];
588 :
589 3068 : werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg,
590 : guid, &n, highest_usn,
591 : rdn_sa, schema,
592 : uptodateness_vector,
593 : partial_attribute_set, local_pas,
594 : attids,
595 : true,
596 : &revealed_list_msg,
597 : existing_revealed_list_msg);
598 3068 : if (!W_ERROR_IS_OK(werr)) {
599 0 : ldb_transaction_cancel(sam_ctx);
600 0 : return werr;
601 : }
602 :
603 3068 : if (revealed_list_msg != NULL) {
604 3068 : ret = ldb_modify(sam_ctx, revealed_list_msg);
605 3068 : if (ret != LDB_SUCCESS) {
606 0 : DEBUG(0,(__location__ ": Failed to alter revealed links - %s\n",
607 : ldb_errstring(sam_ctx)));
608 0 : ldb_transaction_cancel(sam_ctx);
609 0 : return WERR_DS_DRA_INTERNAL_ERROR;
610 : }
611 : }
612 :
613 3068 : ret = ldb_transaction_commit(sam_ctx);
614 3068 : if (ret != LDB_SUCCESS) {
615 0 : DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
616 : ldb_errstring(sam_ctx)));
617 0 : return WERR_DS_DRA_INTERNAL_ERROR;
618 : }
619 : } else {
620 649489 : werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid,
621 : &n, highest_usn, rdn_sa,
622 : schema, uptodateness_vector,
623 : partial_attribute_set, local_pas,
624 : attids,
625 : false,
626 : NULL,
627 : NULL);
628 649489 : if (!W_ERROR_IS_OK(werr)) {
629 0 : return werr;
630 : }
631 : }
632 :
633 : /* ignore it if its an empty change. Note that renames always
634 : * change the 'name' attribute, so they won't be ignored by
635 : * this
636 :
637 : * the force_object_return check is used to force an empty
638 : * object return when we timeout in the getncchanges loop.
639 : * This allows us to return an empty object, which keeps the
640 : * client happy while preventing timeouts
641 : */
642 652557 : if (n == 0 ||
643 652557 : (n == 1 &&
644 76069 : attids[0] == DRSUAPI_ATTID_instanceType &&
645 76046 : !force_object_return)) {
646 76046 : talloc_free(obj->meta_data_ctr);
647 76046 : obj->meta_data_ctr = NULL;
648 76046 : return WERR_OK;
649 : }
650 :
651 576511 : obj->meta_data_ctr->count = n;
652 :
653 576511 : obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
654 576511 : obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
655 576511 : obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
656 : obj->object.attribute_ctr.num_attributes);
657 576511 : if (obj->object.attribute_ctr.attributes == NULL) {
658 0 : return WERR_NOT_ENOUGH_MEMORY;
659 : }
660 :
661 : /*
662 : * Note that the meta_data array and the attributes array must
663 : * be the same size and in the same order
664 : */
665 8389390 : for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
666 0 : struct ldb_message_element *el;
667 0 : const struct dsdb_attribute *sa;
668 :
669 7812879 : sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
670 7812879 : if (!sa) {
671 0 : DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
672 0 : return WERR_DS_DRA_INTERNAL_ERROR;
673 : }
674 :
675 7812879 : el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
676 7812879 : if (el == NULL) {
677 : /* this happens for attributes that have been removed */
678 836013 : DEBUG(5,("No element '%s' for attributeID %u in message\n",
679 : sa->lDAPDisplayName, attids[i]));
680 836013 : ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
681 836013 : obj->object.attribute_ctr.attributes[i].attid =
682 836013 : dsdb_attribute_get_attid(sa, syntax_ctx.is_schema_nc);
683 : } else {
684 6976866 : werr = sa->syntax->ldb_to_drsuapi(&syntax_ctx, sa, el, obj,
685 6976866 : &obj->object.attribute_ctr.attributes[i]);
686 6976866 : if (!W_ERROR_IS_OK(werr)) {
687 0 : DEBUG(0,("Unable to convert %s on %s to DRS object - %s\n",
688 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
689 : win_errstr(werr)));
690 0 : return werr;
691 : }
692 : /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
693 : * check if attribute is secret and send a null value
694 : */
695 6976866 : if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
696 1191402 : drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
697 1191402 : &obj->meta_data_ctr->meta_data[i]);
698 : }
699 : /* some attributes needs to be encrypted
700 : before being sent */
701 6976866 : werr = drsuapi_encrypt_attribute(obj, session_key, rid,
702 6976866 : &obj->object.attribute_ctr.attributes[i]);
703 6976866 : if (!W_ERROR_IS_OK(werr)) {
704 0 : DEBUG(0,("Unable to encrypt %s on %s in DRS object - %s\n",
705 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
706 : win_errstr(werr)));
707 0 : return werr;
708 : }
709 : }
710 7812879 : if (attids[i] != obj->object.attribute_ctr.attributes[i].attid) {
711 0 : DEBUG(0, ("Unable to replicate attribute %s on %s via DRS, incorrect attributeID: "
712 : "0x%08x vs 0x%08x "
713 : "Run dbcheck!\n",
714 : sa->lDAPDisplayName,
715 : ldb_dn_get_linearized(msg->dn),
716 : attids[i],
717 : obj->object.attribute_ctr.attributes[i].attid));
718 0 : return WERR_DS_DATABASE_ERROR;
719 : }
720 : }
721 :
722 576511 : return WERR_OK;
723 : }
724 :
725 : /*
726 : add one linked attribute from an object to the list of linked
727 : attributes in a getncchanges request
728 : */
729 22326 : static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
730 : struct ldb_context *sam_ctx,
731 : const struct dsdb_schema *schema,
732 : const struct dsdb_attribute *sa,
733 : const struct ldb_message *msg,
734 : struct dsdb_dn *dsdb_dn,
735 : struct drsuapi_DsReplicaLinkedAttribute **la_list,
736 : uint32_t *la_count,
737 : bool is_schema_nc)
738 : {
739 0 : struct drsuapi_DsReplicaLinkedAttribute *la;
740 0 : bool active;
741 0 : NTSTATUS status;
742 0 : WERROR werr;
743 :
744 22326 : (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
745 22326 : W_ERROR_HAVE_NO_MEMORY(*la_list);
746 :
747 22326 : la = &(*la_list)[*la_count];
748 :
749 22326 : la->identifier = get_object_identifier(*la_list, msg);
750 22326 : W_ERROR_HAVE_NO_MEMORY(la->identifier);
751 :
752 22326 : active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
753 :
754 22326 : if (!active) {
755 : /* We have to check that the inactive link still point to an existing object */
756 0 : struct GUID guid;
757 0 : struct ldb_dn *tdn;
758 0 : int ret;
759 0 : const char *v;
760 :
761 406 : v = ldb_msg_find_attr_as_string(msg, "isDeleted", "FALSE");
762 406 : if (strncmp(v, "TRUE", 4) == 0) {
763 : /*
764 : * Note: we skip the transmission of the deleted link even if the other part used to
765 : * know about it because when we transmit the deletion of the object, the link will
766 : * be deleted too due to deletion of object where link points and Windows do so.
767 : */
768 0 : if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008_R2) {
769 0 : v = ldb_msg_find_attr_as_string(msg, "isRecycled", "FALSE");
770 : /*
771 : * On Windows 2008R2 isRecycled is always present even if FL or DL are < FL 2K8R2
772 : * if it join an existing domain with deleted objects, it firsts impose to have a
773 : * schema with the is-Recycled object and for all deleted objects it adds the isRecycled
774 : * either during initial replication or after the getNCChanges.
775 : * Behavior of samba has been changed to always have this attribute if it's present in the schema.
776 : *
777 : * So if FL <2K8R2 isRecycled might be here or not but we don't care, it's meaning less.
778 : * If FL >=2K8R2 we are sure that this attribute will be here.
779 : * For this kind of forest level we do not return the link if the object is recycled
780 : * (isRecycled = true).
781 : */
782 0 : if (strncmp(v, "TRUE", 4) == 0) {
783 0 : DEBUG(2, (" object %s is recycled, not returning linked attribute !\n",
784 : ldb_dn_get_linearized(msg->dn)));
785 0 : return WERR_OK;
786 : }
787 : } else {
788 0 : return WERR_OK;
789 : }
790 : }
791 406 : status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
792 406 : if (!NT_STATUS_IS_OK(status)) {
793 0 : DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
794 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
795 0 : return ntstatus_to_werror(status);
796 : }
797 406 : ret = dsdb_find_dn_by_guid(sam_ctx, mem_ctx, &guid, 0, &tdn);
798 406 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
799 66 : DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
800 : GUID_string(mem_ctx, &guid)));
801 66 : return WERR_OK;
802 340 : } else if (ret != LDB_SUCCESS) {
803 0 : DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
804 : GUID_string(mem_ctx, &guid),
805 : ret));
806 0 : return WERR_OK;
807 : }
808 : }
809 22260 : la->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
810 22260 : la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
811 :
812 22260 : status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
813 22260 : if (!NT_STATUS_IS_OK(status)) {
814 0 : DEBUG(0,(__location__ " No RMD_VERSION in linked attribute '%s' in '%s'\n",
815 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
816 0 : return ntstatus_to_werror(status);
817 : }
818 22260 : status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
819 22260 : if (!NT_STATUS_IS_OK(status)) {
820 0 : DEBUG(0,(__location__ " No RMD_CHANGETIME in linked attribute '%s' in '%s'\n",
821 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
822 0 : return ntstatus_to_werror(status);
823 : }
824 22260 : status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
825 22260 : if (!NT_STATUS_IS_OK(status)) {
826 0 : DEBUG(0,(__location__ " No RMD_INVOCID in linked attribute '%s' in '%s'\n",
827 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
828 0 : return ntstatus_to_werror(status);
829 : }
830 22260 : status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
831 22260 : if (!NT_STATUS_IS_OK(status)) {
832 0 : DEBUG(0,(__location__ " No RMD_ORIGINATING_USN in linked attribute '%s' in '%s'\n",
833 : sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
834 0 : return ntstatus_to_werror(status);
835 : }
836 :
837 22260 : status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
838 22260 : if (!NT_STATUS_IS_OK(status)) {
839 : /* this is possible for upgraded links */
840 0 : la->originating_add_time = la->meta_data.originating_change_time;
841 : }
842 :
843 22260 : werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
844 22260 : W_ERROR_NOT_OK_RETURN(werr);
845 :
846 22260 : (*la_count)++;
847 22260 : return WERR_OK;
848 : }
849 :
850 :
851 : /*
852 : add linked attributes from an object to the list of linked
853 : attributes in a getncchanges request
854 : */
855 651504 : static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
856 : TALLOC_CTX *mem_ctx,
857 : bool is_schema_nc,
858 : struct dsdb_schema *schema,
859 : uint64_t highest_usn,
860 : uint32_t replica_flags,
861 : const struct ldb_message *msg,
862 : struct drsuapi_DsReplicaLinkedAttribute **la_list,
863 : uint32_t *la_count,
864 : struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
865 : {
866 0 : unsigned int i;
867 651504 : TALLOC_CTX *tmp_ctx = NULL;
868 651504 : uint64_t uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
869 651504 : bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
870 :
871 651504 : if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
872 7496 : if (!is_critical) {
873 3 : return WERR_OK;
874 : }
875 : }
876 :
877 651501 : if (uSNChanged <= highest_usn) {
878 0 : return WERR_OK;
879 : }
880 :
881 651501 : tmp_ctx = talloc_new(mem_ctx);
882 651501 : if (tmp_ctx == NULL) {
883 0 : return WERR_NOT_ENOUGH_MEMORY;
884 : }
885 :
886 13829574 : for (i=0; i<msg->num_elements; i++) {
887 13178073 : struct ldb_message_element *el = &msg->elements[i];
888 0 : const struct dsdb_attribute *sa;
889 0 : unsigned int j;
890 :
891 13178073 : sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
892 :
893 13178073 : if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
894 : /* we only want forward links */
895 13166196 : continue;
896 : }
897 :
898 11877 : if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
899 : /* its an old style link, it will have been
900 : * sent in the main replication data */
901 123 : continue;
902 : }
903 :
904 45907 : for (j=0; j<el->num_values; j++) {
905 0 : struct dsdb_dn *dsdb_dn;
906 0 : uint64_t local_usn;
907 0 : uint64_t originating_usn;
908 0 : NTSTATUS status, status2;
909 0 : WERROR werr;
910 0 : struct GUID originating_invocation_id;
911 :
912 34153 : dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
913 34153 : if (dsdb_dn == NULL) {
914 0 : DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
915 : el->name, ldb_dn_get_linearized(msg->dn)));
916 0 : talloc_free(tmp_ctx);
917 0 : return WERR_DS_DRA_INTERNAL_ERROR;
918 : }
919 :
920 34153 : status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
921 34153 : if (!NT_STATUS_IS_OK(status)) {
922 : /* this can happen for attributes
923 : given to us with old style meta
924 : data */
925 11827 : continue;
926 : }
927 :
928 34153 : if (local_usn > uSNChanged) {
929 0 : DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
930 : el->name, ldb_dn_get_linearized(msg->dn)));
931 0 : talloc_free(tmp_ctx);
932 0 : return WERR_DS_DRA_INTERNAL_ERROR;
933 : }
934 :
935 34153 : if (local_usn <= highest_usn) {
936 11002 : continue;
937 : }
938 :
939 23151 : status = dsdb_get_extended_dn_guid(dsdb_dn->dn,
940 : &originating_invocation_id,
941 : "RMD_INVOCID");
942 23151 : status2 = dsdb_get_extended_dn_uint64(dsdb_dn->dn,
943 : &originating_usn,
944 : "RMD_ORIGINATING_USN");
945 :
946 23151 : if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2)) {
947 23151 : if (udv_filter(uptodateness_vector,
948 : &originating_invocation_id,
949 : originating_usn)) {
950 825 : continue;
951 : }
952 : }
953 :
954 22326 : werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema,
955 : sa, msg, dsdb_dn, la_list,
956 : la_count, is_schema_nc);
957 22326 : if (!W_ERROR_IS_OK(werr)) {
958 0 : talloc_free(tmp_ctx);
959 0 : return werr;
960 : }
961 : }
962 : }
963 :
964 651501 : talloc_free(tmp_ctx);
965 651501 : return WERR_OK;
966 : }
967 :
968 : /*
969 : fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
970 : */
971 6038 : static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
972 : struct ldb_dn *ncRoot_dn,
973 : struct drsuapi_DsReplicaCursor2CtrEx *udv)
974 : {
975 0 : int ret;
976 :
977 6038 : udv->version = 2;
978 6038 : udv->reserved1 = 0;
979 6038 : udv->reserved2 = 0;
980 :
981 6038 : ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
982 6038 : if (ret != LDB_SUCCESS) {
983 0 : DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
984 : ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
985 0 : return WERR_DS_DRA_INTERNAL_ERROR;
986 : }
987 :
988 6038 : return WERR_OK;
989 : }
990 :
991 :
992 : /* comparison function for linked attributes - see CompareLinks() in
993 : * MS-DRSR section 4.1.10.5.17 */
994 71984 : static int linked_attribute_compare(const struct la_for_sorting *la1,
995 : const struct la_for_sorting *la2)
996 : {
997 0 : int c;
998 71984 : c = memcmp(la1->source_guid,
999 71984 : la2->source_guid, sizeof(la2->source_guid));
1000 71984 : if (c != 0) {
1001 40363 : return c;
1002 : }
1003 :
1004 31621 : if (la1->link->attid != la2->link->attid) {
1005 4071 : return la1->link->attid < la2->link->attid? -1:1;
1006 : }
1007 :
1008 27550 : if ((la1->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
1009 27550 : (la2->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
1010 225 : return (la1->link->flags &
1011 225 : DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
1012 : }
1013 :
1014 27325 : return memcmp(la1->target_guid,
1015 27325 : la2->target_guid, sizeof(la2->target_guid));
1016 : }
1017 :
1018 : struct drsuapi_changed_objects {
1019 : struct ldb_dn *dn;
1020 : struct GUID guid;
1021 : uint64_t usn;
1022 : };
1023 :
1024 :
1025 : /*
1026 : sort the objects we send by tree order (Samba 4.5 emulation)
1027 : */
1028 37892 : static int site_res_cmp_anc_order(struct drsuapi_changed_objects *m1,
1029 : struct drsuapi_changed_objects *m2)
1030 : {
1031 37892 : return ldb_dn_compare(m2->dn, m1->dn);
1032 : }
1033 :
1034 : /*
1035 : sort the objects we send first by uSNChanged
1036 : */
1037 6409315 : static int site_res_cmp_usn_order(struct drsuapi_changed_objects *m1,
1038 : struct drsuapi_changed_objects *m2)
1039 : {
1040 6409315 : if (m1->usn == m2->usn) {
1041 0 : return ldb_dn_compare(m2->dn, m1->dn);
1042 : }
1043 :
1044 6409315 : if (m1->usn < m2->usn) {
1045 3158980 : return -1;
1046 : }
1047 :
1048 3250335 : return 1;
1049 : }
1050 :
1051 :
1052 : /*
1053 : handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
1054 : */
1055 41 : static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
1056 : TALLOC_CTX *mem_ctx,
1057 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1058 : struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1059 : struct ldb_dn **rid_manager_dn)
1060 : {
1061 41 : struct ldb_dn *req_dn, *ntds_dn = NULL;
1062 0 : int ret;
1063 41 : struct ldb_context *ldb = b_state->sam_ctx;
1064 0 : struct ldb_result *ext_res;
1065 0 : struct dsdb_fsmo_extended_op *exop;
1066 0 : bool is_us;
1067 :
1068 : /*
1069 : steps:
1070 : - verify that the DN being asked for is the RID Manager DN
1071 : - verify that we are the RID Manager
1072 : */
1073 :
1074 : /* work out who is the RID Manager, also return to caller */
1075 41 : ret = samdb_rid_manager_dn(ldb, mem_ctx, rid_manager_dn);
1076 41 : if (ret != LDB_SUCCESS) {
1077 0 : DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
1078 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1079 : }
1080 :
1081 41 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
1082 : ldb,
1083 : req10->naming_context,
1084 : &req_dn,
1085 : NULL);
1086 41 : if (ret != LDB_SUCCESS) {
1087 0 : DBG_ERR("RID Alloc request for invalid DN %s: %s\n",
1088 : drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
1089 : ldb_strerror(ret));
1090 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1091 0 : return WERR_OK;
1092 : }
1093 :
1094 41 : if (ldb_dn_compare(req_dn, *rid_manager_dn) != 0) {
1095 : /* that isn't the RID Manager DN */
1096 0 : DBG_ERR("RID Alloc request for wrong DN %s\n",
1097 : drs_ObjectIdentifier_to_debug_string(mem_ctx,
1098 : req10->naming_context));
1099 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1100 0 : return WERR_OK;
1101 : }
1102 :
1103 : /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1104 41 : ret = dsdb_find_dn_by_guid(ldb, mem_ctx, &req10->destination_dsa_guid, 0, &ntds_dn);
1105 41 : if (ret != LDB_SUCCESS) {
1106 0 : DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1107 : GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1108 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1109 0 : return WERR_OK;
1110 : }
1111 :
1112 : /* find the DN of the RID Manager */
1113 41 : ret = samdb_reference_dn_is_our_ntdsa(ldb, *rid_manager_dn, "fSMORoleOwner", &is_us);
1114 41 : if (ret != LDB_SUCCESS) {
1115 0 : DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1116 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1117 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1118 : }
1119 :
1120 41 : if (!is_us) {
1121 : /* we're not the RID Manager - go away */
1122 4 : DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
1123 4 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1124 4 : return WERR_OK;
1125 : }
1126 :
1127 37 : exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
1128 37 : W_ERROR_HAVE_NO_MEMORY(exop);
1129 :
1130 37 : exop->fsmo_info = req10->fsmo_info;
1131 37 : exop->destination_dsa_guid = req10->destination_dsa_guid;
1132 :
1133 37 : ret = ldb_transaction_start(ldb);
1134 37 : if (ret != LDB_SUCCESS) {
1135 0 : DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1136 : ldb_errstring(ldb)));
1137 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1138 : }
1139 :
1140 37 : ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
1141 37 : if (ret != LDB_SUCCESS) {
1142 0 : DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
1143 : ldb_errstring(ldb)));
1144 0 : ldb_transaction_cancel(ldb);
1145 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1146 : }
1147 :
1148 37 : ret = ldb_transaction_commit(ldb);
1149 37 : if (ret != LDB_SUCCESS) {
1150 0 : DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1151 : ldb_errstring(ldb)));
1152 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1153 : }
1154 :
1155 37 : talloc_free(ext_res);
1156 :
1157 37 : DEBUG(2,("Allocated RID pool for server %s\n",
1158 : GUID_string(mem_ctx, &req10->destination_dsa_guid)));
1159 :
1160 37 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1161 :
1162 37 : return WERR_OK;
1163 : }
1164 :
1165 : /*
1166 : handle a DRSUAPI_EXOP_REPL_SECRET call
1167 : */
1168 4403 : static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
1169 : TALLOC_CTX *mem_ctx,
1170 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1171 : struct dom_sid *user_sid,
1172 : struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1173 : bool has_get_all_changes,
1174 : struct ldb_dn **machine_dn)
1175 : {
1176 4403 : struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1177 4403 : struct ldb_dn *obj_dn = NULL;
1178 4403 : struct ldb_message *ntds_msg = NULL;
1179 4403 : struct ldb_dn *ntds_dn = NULL, *server_dn = NULL;
1180 0 : struct ldb_dn *rodc_dn, *krbtgt_link_dn;
1181 0 : int ret;
1182 4403 : const char *ntds_attrs[] = { NULL };
1183 4403 : const char *rodc_attrs[] = { "msDS-KrbTgtLink",
1184 : "msDS-NeverRevealGroup",
1185 : "msDS-RevealOnDemandGroup",
1186 : "userAccountControl",
1187 : NULL };
1188 4403 : const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
1189 4403 : struct ldb_result *rodc_res = NULL, *obj_res = NULL;
1190 0 : WERROR werr;
1191 0 : struct GUID_txt_buf guid_buf;
1192 :
1193 4403 : DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n",
1194 : drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot)));
1195 :
1196 : /*
1197 : * we need to work out if we will allow this DC to
1198 : * replicate the secrets for this object
1199 : *
1200 : * see 4.1.10.5.14 GetRevealSecretsPolicyForUser for details
1201 : * of this function
1202 : */
1203 :
1204 4403 : if (b_state->sam_ctx_system == NULL) {
1205 : /* this operation needs system level access */
1206 12 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_ACCESS_DENIED;
1207 12 : return WERR_DS_DRA_ACCESS_DENIED;
1208 : }
1209 :
1210 : /*
1211 : * Before we accept or deny, fetch the machine DN for the destination
1212 : * DSA GUID.
1213 : *
1214 : * If we are the RODC, we will check that this matches the SID.
1215 : */
1216 4391 : ret = samdb_get_ntds_obj_by_guid(mem_ctx,
1217 : b_state->sam_ctx_system,
1218 4391 : &req10->destination_dsa_guid,
1219 : ntds_attrs,
1220 : &ntds_msg);
1221 4391 : if (ret != LDB_SUCCESS) {
1222 9 : goto dest_dsa_error;
1223 : }
1224 :
1225 4382 : ntds_dn = ntds_msg->dn;
1226 :
1227 4382 : server_dn = ldb_dn_get_parent(mem_ctx, ntds_dn);
1228 4382 : if (server_dn == NULL) {
1229 0 : goto failed;
1230 : }
1231 :
1232 4382 : ret = samdb_reference_dn(b_state->sam_ctx_system, mem_ctx, server_dn,
1233 : "serverReference", machine_dn);
1234 :
1235 4382 : if (ret != LDB_SUCCESS) {
1236 0 : goto dest_dsa_error;
1237 : }
1238 :
1239 : /*
1240 : * In MS-DRSR.pdf 5.99 IsGetNCChangesPermissionGranted
1241 : *
1242 : * The pseudo code indicate
1243 : * revealsecrets = true
1244 : * if IsRevealSecretRequest(msgIn) then
1245 : * if AccessCheckCAR(ncRoot, Ds-Replication-Get-Changes-All) = false
1246 : * then
1247 : * if (msgIn.ulExtendedOp = EXOP_REPL_SECRETS) then
1248 : * <... check if this account is ok to be replicated on this DC ...>
1249 : * <... and if not reveal secrets = no ...>
1250 : * else
1251 : * reveal secrets = false
1252 : * endif
1253 : * endif
1254 : * endif
1255 : *
1256 : * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC)
1257 : * then you can do EXOP_REPL_SECRETS
1258 : */
1259 4382 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
1260 : b_state->sam_ctx_system,
1261 : ncRoot,
1262 : &obj_dn,
1263 : NULL);
1264 4382 : if (ret != LDB_SUCCESS) {
1265 0 : DBG_ERR("RevealSecretRequest for invalid DN %s\n",
1266 : drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot));
1267 0 : goto failed;
1268 : }
1269 :
1270 4382 : if (!ldb_dn_validate(obj_dn)) goto failed;
1271 :
1272 4382 : if (has_get_all_changes) {
1273 3043 : goto allowed;
1274 : }
1275 :
1276 1339 : rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "<SID=%s>",
1277 : dom_sid_string(mem_ctx, user_sid));
1278 1339 : if (!ldb_dn_validate(rodc_dn)) goto failed;
1279 :
1280 : /*
1281 : * do the two searches we need
1282 : * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID lists
1283 : * out of the extended DNs
1284 : */
1285 1339 : ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
1286 : DSDB_SEARCH_SHOW_EXTENDED_DN);
1287 1339 : if (ret != LDB_SUCCESS || rodc_res->count != 1) goto failed;
1288 :
1289 1339 : ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
1290 1339 : if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed;
1291 :
1292 : /*
1293 : * Must be an RODC account at this point, verify machine DN matches the
1294 : * SID account
1295 : */
1296 1339 : if (ldb_dn_compare(rodc_res->msgs[0]->dn, *machine_dn) != 0) {
1297 2 : goto denied;
1298 : }
1299 :
1300 : /* an RODC is allowed to get its own krbtgt account secrets */
1301 1337 : krbtgt_link_dn = samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
1302 1337 : rodc_res->msgs[0], "msDS-KrbTgtLink", NULL);
1303 2674 : if (krbtgt_link_dn != NULL &&
1304 1337 : ldb_dn_compare(obj_dn, krbtgt_link_dn) == 0) {
1305 5 : goto allowed;
1306 : }
1307 :
1308 1332 : werr = samdb_confirm_rodc_allowed_to_repl_to(b_state->sam_ctx_system,
1309 : user_sid,
1310 1332 : rodc_res->msgs[0],
1311 1332 : obj_res->msgs[0]);
1312 :
1313 1332 : if (W_ERROR_IS_OK(werr)) {
1314 20 : goto allowed;
1315 : }
1316 :
1317 : /* default deny */
1318 1312 : denied:
1319 1314 : DEBUG(2,(__location__ ": Denied single object with secret replication for %s by RODC %s\n",
1320 : ldb_dn_get_linearized(obj_dn), ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
1321 1314 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1322 1314 : return WERR_DS_DRA_SECRETS_DENIED;
1323 :
1324 3068 : allowed:
1325 3068 : DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
1326 : ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC",
1327 : ldb_dn_get_linearized(*machine_dn)));
1328 3068 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1329 3068 : req10->highwatermark.highest_usn = 0;
1330 3068 : return WERR_OK;
1331 :
1332 0 : failed:
1333 0 : DEBUG(2,(__location__ ": Failed single secret replication for %s by RODC %s\n",
1334 : ldb_dn_get_linearized(obj_dn), dom_sid_string(mem_ctx, user_sid)));
1335 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1336 0 : return WERR_DS_DRA_BAD_DN;
1337 :
1338 9 : dest_dsa_error:
1339 9 : DBG_WARNING("Failed secret replication for %s by RODC %s as dest_dsa_guid %s is invalid\n",
1340 : ldb_dn_get_linearized(obj_dn),
1341 : dom_sid_string(mem_ctx, user_sid),
1342 : GUID_buf_string(&req10->destination_dsa_guid,
1343 : &guid_buf));
1344 9 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1345 9 : return WERR_DS_DRA_DB_ERROR;
1346 : }
1347 :
1348 : /*
1349 : handle a DRSUAPI_EXOP_REPL_OBJ call
1350 : */
1351 167 : static WERROR getncchanges_repl_obj(struct drsuapi_bind_state *b_state,
1352 : TALLOC_CTX *mem_ctx,
1353 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1354 : struct dom_sid *user_sid,
1355 : struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1356 : {
1357 167 : struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1358 :
1359 167 : DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_OBJ extended op on %s\n",
1360 : drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot)));
1361 :
1362 167 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1363 167 : return WERR_OK;
1364 : }
1365 :
1366 :
1367 : /*
1368 : handle DRSUAPI_EXOP_FSMO_REQ_ROLE,
1369 : DRSUAPI_EXOP_FSMO_RID_REQ_ROLE,
1370 : and DRSUAPI_EXOP_FSMO_REQ_PDC calls
1371 : */
1372 25 : static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state,
1373 : TALLOC_CTX *mem_ctx,
1374 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1375 : struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1376 : {
1377 0 : struct ldb_dn *req_dn, *ntds_dn;
1378 0 : int ret;
1379 0 : unsigned int i;
1380 25 : struct ldb_context *ldb = b_state->sam_ctx;
1381 0 : struct ldb_message *msg;
1382 0 : bool is_us;
1383 :
1384 : /*
1385 : steps:
1386 : - verify that the client dn exists
1387 : - verify that we are the current master
1388 : */
1389 :
1390 25 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, ldb, req10->naming_context,
1391 : &req_dn, NULL);
1392 25 : if (ret != LDB_SUCCESS) {
1393 : /* that is not a valid dn */
1394 0 : DBG_ERR("FSMO role transfer request for invalid DN %s: %s\n",
1395 : drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
1396 : ldb_strerror(ret));
1397 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1398 0 : return WERR_OK;
1399 : }
1400 :
1401 : /* find the DN of the current role owner */
1402 25 : ret = samdb_reference_dn_is_our_ntdsa(ldb, req_dn, "fSMORoleOwner", &is_us);
1403 25 : if (ret != LDB_SUCCESS) {
1404 0 : DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1405 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1406 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1407 : }
1408 :
1409 25 : if (!is_us) {
1410 : /* we're not the RID Manager or role owner - go away */
1411 2 : DEBUG(0,(__location__ ": FSMO role or RID manager transfer owner request when not role owner\n"));
1412 2 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1413 2 : return WERR_OK;
1414 : }
1415 :
1416 : /* change the current master */
1417 23 : msg = ldb_msg_new(ldb);
1418 23 : W_ERROR_HAVE_NO_MEMORY(msg);
1419 23 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(msg, ldb, req10->naming_context,
1420 : &msg->dn, NULL);
1421 23 : if (ret != LDB_SUCCESS) {
1422 : /* that is not a valid dn */
1423 0 : DBG_ERR("FSMO role transfer request for invalid DN %s: %s\n",
1424 : drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
1425 : ldb_strerror(ret));
1426 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1427 0 : return WERR_OK;
1428 : }
1429 :
1430 : /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1431 23 : ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, 0, &ntds_dn);
1432 23 : if (ret != LDB_SUCCESS) {
1433 0 : DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1434 : GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1435 0 : talloc_free(msg);
1436 0 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1437 0 : return WERR_OK;
1438 : }
1439 :
1440 23 : ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn));
1441 23 : if (ret != 0) {
1442 0 : talloc_free(msg);
1443 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1444 : }
1445 :
1446 46 : for (i=0;i<msg->num_elements;i++) {
1447 23 : msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1448 : }
1449 :
1450 23 : ret = ldb_transaction_start(ldb);
1451 23 : if (ret != LDB_SUCCESS) {
1452 0 : DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1453 : ldb_errstring(ldb)));
1454 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1455 : }
1456 :
1457 23 : ret = ldb_modify(ldb, msg);
1458 23 : if (ret != LDB_SUCCESS) {
1459 0 : DEBUG(0,(__location__ ": Failed to change current owner - %s\n",
1460 : ldb_errstring(ldb)));
1461 0 : ldb_transaction_cancel(ldb);
1462 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1463 : }
1464 :
1465 23 : ret = ldb_transaction_commit(ldb);
1466 23 : if (ret != LDB_SUCCESS) {
1467 0 : DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1468 : ldb_errstring(ldb)));
1469 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1470 : }
1471 :
1472 23 : ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1473 :
1474 23 : return WERR_OK;
1475 : }
1476 :
1477 : /*
1478 : see if this getncchanges request includes a request to reveal secret information
1479 : */
1480 9537 : static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state *b_state,
1481 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1482 : struct dsdb_schema_prefixmap *pfm_remote,
1483 : bool *is_secret_request)
1484 : {
1485 0 : enum drsuapi_DsExtendedOperation exop;
1486 0 : uint32_t i;
1487 0 : struct dsdb_schema *schema;
1488 0 : struct dsdb_syntax_ctx syntax_ctx;
1489 :
1490 9537 : *is_secret_request = true;
1491 :
1492 9537 : exop = req10->extended_op;
1493 :
1494 9537 : switch (exop) {
1495 66 : case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1496 : case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1497 : case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1498 : case DRSUAPI_EXOP_FSMO_REQ_PDC:
1499 : case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1500 : /* FSMO exops can reveal secrets */
1501 66 : *is_secret_request = true;
1502 66 : return WERR_OK;
1503 9471 : case DRSUAPI_EXOP_REPL_SECRET:
1504 : case DRSUAPI_EXOP_REPL_OBJ:
1505 : case DRSUAPI_EXOP_NONE:
1506 9471 : break;
1507 : }
1508 :
1509 9471 : if (req10->replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
1510 865 : *is_secret_request = false;
1511 865 : return WERR_OK;
1512 : }
1513 :
1514 8606 : if (exop == DRSUAPI_EXOP_REPL_SECRET ||
1515 4199 : req10->partial_attribute_set == NULL) {
1516 : /* they want secrets */
1517 8589 : *is_secret_request = true;
1518 8589 : return WERR_OK;
1519 : }
1520 :
1521 17 : schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1522 17 : dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1523 17 : syntax_ctx.pfm_remote = pfm_remote;
1524 :
1525 : /* check the attributes they asked for */
1526 28 : for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1527 0 : const struct dsdb_attribute *sa;
1528 17 : WERROR werr = getncchanges_attid_remote_to_local(schema,
1529 : &syntax_ctx,
1530 17 : req10->partial_attribute_set->attids[i],
1531 : NULL,
1532 : &sa);
1533 :
1534 17 : if (!W_ERROR_IS_OK(werr)) {
1535 0 : DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1536 : req10->partial_attribute_set->attids[i], win_errstr(werr)));
1537 6 : return werr;
1538 : }
1539 :
1540 17 : if (!dsdb_attr_in_rodc_fas(sa)) {
1541 6 : *is_secret_request = true;
1542 6 : return WERR_OK;
1543 : }
1544 : }
1545 :
1546 11 : if (req10->partial_attribute_set_ex) {
1547 : /* check the extended attributes they asked for */
1548 3 : for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1549 0 : const struct dsdb_attribute *sa;
1550 3 : WERROR werr = getncchanges_attid_remote_to_local(schema,
1551 : &syntax_ctx,
1552 3 : req10->partial_attribute_set_ex->attids[i],
1553 : NULL,
1554 : &sa);
1555 :
1556 3 : if (!W_ERROR_IS_OK(werr)) {
1557 0 : DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1558 : req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1559 3 : return werr;
1560 : }
1561 :
1562 3 : if (!dsdb_attr_in_rodc_fas(sa)) {
1563 3 : *is_secret_request = true;
1564 3 : return WERR_OK;
1565 : }
1566 : }
1567 : }
1568 :
1569 8 : *is_secret_request = false;
1570 8 : return WERR_OK;
1571 : }
1572 :
1573 : /*
1574 : see if this getncchanges request is only for attributes in the GC
1575 : partial attribute set
1576 : */
1577 9549 : static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state,
1578 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1579 : struct dsdb_schema_prefixmap *pfm_remote,
1580 : bool *is_gc_pas_request)
1581 : {
1582 0 : enum drsuapi_DsExtendedOperation exop;
1583 0 : uint32_t i;
1584 0 : struct dsdb_schema *schema;
1585 0 : struct dsdb_syntax_ctx syntax_ctx;
1586 :
1587 9549 : exop = req10->extended_op;
1588 :
1589 9549 : switch (exop) {
1590 4473 : case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1591 : case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1592 : case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1593 : case DRSUAPI_EXOP_FSMO_REQ_PDC:
1594 : case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1595 : case DRSUAPI_EXOP_REPL_SECRET:
1596 4473 : *is_gc_pas_request = false;
1597 4473 : return WERR_OK;
1598 5076 : case DRSUAPI_EXOP_REPL_OBJ:
1599 : case DRSUAPI_EXOP_NONE:
1600 5076 : break;
1601 : }
1602 :
1603 5076 : if (req10->partial_attribute_set == NULL) {
1604 : /* they want it all */
1605 4458 : *is_gc_pas_request = false;
1606 4458 : return WERR_OK;
1607 : }
1608 :
1609 618 : schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1610 618 : dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1611 618 : syntax_ctx.pfm_remote = pfm_remote;
1612 :
1613 : /* check the attributes they asked for */
1614 1227 : for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1615 0 : const struct dsdb_attribute *sa;
1616 1207 : WERROR werr = getncchanges_attid_remote_to_local(schema,
1617 : &syntax_ctx,
1618 1207 : req10->partial_attribute_set->attids[i],
1619 : NULL,
1620 : &sa);
1621 :
1622 1207 : if (!W_ERROR_IS_OK(werr)) {
1623 3 : DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1624 : req10->partial_attribute_set->attids[i], win_errstr(werr)));
1625 598 : return werr;
1626 : }
1627 :
1628 1204 : if (!sa->isMemberOfPartialAttributeSet) {
1629 595 : *is_gc_pas_request = false;
1630 595 : return WERR_OK;
1631 : }
1632 : }
1633 :
1634 20 : if (req10->partial_attribute_set_ex) {
1635 : /* check the extended attributes they asked for */
1636 3 : for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1637 0 : const struct dsdb_attribute *sa;
1638 3 : WERROR werr = getncchanges_attid_remote_to_local(schema,
1639 : &syntax_ctx,
1640 3 : req10->partial_attribute_set_ex->attids[i],
1641 : NULL,
1642 : &sa);
1643 :
1644 3 : if (!W_ERROR_IS_OK(werr)) {
1645 0 : DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1646 : req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1647 3 : return werr;
1648 : }
1649 :
1650 3 : if (!sa->isMemberOfPartialAttributeSet) {
1651 3 : *is_gc_pas_request = false;
1652 3 : return WERR_OK;
1653 : }
1654 : }
1655 : }
1656 :
1657 17 : *is_gc_pas_request = true;
1658 17 : return WERR_OK;
1659 : }
1660 :
1661 :
1662 : /*
1663 : map from req8 to req10
1664 : */
1665 : static struct drsuapi_DsGetNCChangesRequest10 *
1666 3483 : getncchanges_map_req8(TALLOC_CTX *mem_ctx,
1667 : struct drsuapi_DsGetNCChangesRequest8 *req8)
1668 : {
1669 3483 : struct drsuapi_DsGetNCChangesRequest10 *req10 = talloc_zero(mem_ctx,
1670 : struct drsuapi_DsGetNCChangesRequest10);
1671 3483 : if (req10 == NULL) {
1672 0 : return NULL;
1673 : }
1674 :
1675 3483 : req10->destination_dsa_guid = req8->destination_dsa_guid;
1676 3483 : req10->source_dsa_invocation_id = req8->source_dsa_invocation_id;
1677 3483 : req10->naming_context = req8->naming_context;
1678 3483 : req10->highwatermark = req8->highwatermark;
1679 3483 : req10->uptodateness_vector = req8->uptodateness_vector;
1680 3483 : req10->replica_flags = req8->replica_flags;
1681 3483 : req10->max_object_count = req8->max_object_count;
1682 3483 : req10->max_ndr_size = req8->max_ndr_size;
1683 3483 : req10->extended_op = req8->extended_op;
1684 3483 : req10->fsmo_info = req8->fsmo_info;
1685 3483 : req10->partial_attribute_set = req8->partial_attribute_set;
1686 3483 : req10->partial_attribute_set_ex = req8->partial_attribute_set_ex;
1687 3483 : req10->mapping_ctr = req8->mapping_ctr;
1688 :
1689 3483 : return req10;
1690 : }
1691 :
1692 : static const char *collect_objects_attrs[] = { "uSNChanged",
1693 : "objectGUID" ,
1694 : NULL };
1695 :
1696 : /**
1697 : * Collects object for normal replication cycle.
1698 : */
1699 6001 : static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
1700 : TALLOC_CTX *mem_ctx,
1701 : struct drsuapi_getncchanges_state *getnc_state,
1702 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1703 : struct ldb_dn *search_dn,
1704 : const char *extra_filter,
1705 : struct ldb_result **search_res)
1706 : {
1707 0 : int ret;
1708 0 : char* search_filter;
1709 6001 : enum ldb_scope scope = LDB_SCOPE_SUBTREE;
1710 6001 : bool critical_only = false;
1711 :
1712 6001 : if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
1713 135 : critical_only = true;
1714 : }
1715 :
1716 6001 : if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ ||
1717 5834 : req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
1718 3235 : scope = LDB_SCOPE_BASE;
1719 3235 : critical_only = false;
1720 : }
1721 :
1722 : /* Construct response. */
1723 6001 : search_filter = talloc_asprintf(mem_ctx,
1724 : "(uSNChanged>=%llu)",
1725 6001 : (unsigned long long)(getnc_state->min_usn+1));
1726 :
1727 6001 : if (extra_filter) {
1728 0 : search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
1729 : }
1730 :
1731 6001 : if (critical_only) {
1732 132 : search_filter = talloc_asprintf(mem_ctx,
1733 : "(&%s(isCriticalSystemObject=TRUE))",
1734 : search_filter);
1735 : }
1736 :
1737 6001 : if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
1738 0 : scope = LDB_SCOPE_BASE;
1739 : }
1740 :
1741 6001 : if (!search_dn) {
1742 6001 : search_dn = getnc_state->ncRoot_dn;
1743 : }
1744 :
1745 6001 : DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n",
1746 : ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
1747 6001 : ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, search_res,
1748 : search_dn, scope,
1749 : collect_objects_attrs,
1750 : search_filter);
1751 6001 : if (ret != LDB_SUCCESS) {
1752 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1753 : }
1754 :
1755 6001 : return WERR_OK;
1756 : }
1757 :
1758 : /**
1759 : * Collects object for normal replication cycle.
1760 : */
1761 3295 : static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state,
1762 : TALLOC_CTX *mem_ctx,
1763 : struct drsuapi_getncchanges_state *getnc_state,
1764 : struct drsuapi_DsGetNCChangesRequest10 *req10,
1765 : struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1766 : struct ldb_dn *search_dn,
1767 : const char *extra_filter,
1768 : struct ldb_result **search_res)
1769 : {
1770 : /* we have nothing to do in case of ex-op failure */
1771 3295 : if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
1772 0 : return WERR_OK;
1773 : }
1774 :
1775 3295 : switch (req10->extended_op) {
1776 37 : case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1777 : {
1778 0 : int ret;
1779 37 : struct ldb_dn *ntds_dn = NULL;
1780 37 : struct ldb_dn *server_dn = NULL;
1781 37 : struct ldb_dn *machine_dn = NULL;
1782 37 : struct ldb_dn *rid_set_dn = NULL;
1783 37 : struct ldb_result *search_res2 = NULL;
1784 37 : struct ldb_result *search_res3 = NULL;
1785 37 : TALLOC_CTX *frame = talloc_stackframe();
1786 : /* get RID manager, RID set and server DN (in that order) */
1787 :
1788 : /* This first search will get the RID Manager */
1789 37 : ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1790 : search_res,
1791 : search_dn, LDB_SCOPE_BASE,
1792 : collect_objects_attrs,
1793 : NULL);
1794 37 : if (ret != LDB_SUCCESS) {
1795 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %s\n",
1796 : ldb_dn_get_linearized(search_dn),
1797 : ldb_errstring(b_state->sam_ctx)));
1798 0 : TALLOC_FREE(frame);
1799 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1800 : }
1801 :
1802 37 : if ((*search_res)->count != 1) {
1803 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %u objects returned\n",
1804 : ldb_dn_get_linearized(search_dn),
1805 : (*search_res)->count));
1806 0 : TALLOC_FREE(frame);
1807 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1808 : }
1809 :
1810 : /* Now extend it to the RID set */
1811 :
1812 : /* Find the computer account DN for the destination
1813 : * dsa GUID specified */
1814 :
1815 37 : ret = dsdb_find_dn_by_guid(b_state->sam_ctx, frame,
1816 37 : &req10->destination_dsa_guid, 0,
1817 : &ntds_dn);
1818 37 : if (ret != LDB_SUCCESS) {
1819 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Unable to find NTDS object for guid %s - %s\n",
1820 : GUID_string(frame,
1821 : &req10->destination_dsa_guid),
1822 : ldb_errstring(b_state->sam_ctx)));
1823 0 : TALLOC_FREE(frame);
1824 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1825 : }
1826 :
1827 37 : server_dn = ldb_dn_get_parent(frame, ntds_dn);
1828 37 : if (!server_dn) {
1829 0 : TALLOC_FREE(frame);
1830 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1831 : }
1832 :
1833 37 : ret = samdb_reference_dn(b_state->sam_ctx, frame, server_dn,
1834 : "serverReference", &machine_dn);
1835 37 : if (ret != LDB_SUCCESS) {
1836 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find serverReference in %s - %s\n",
1837 : ldb_dn_get_linearized(server_dn),
1838 : ldb_errstring(b_state->sam_ctx)));
1839 0 : TALLOC_FREE(frame);
1840 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1841 : }
1842 :
1843 37 : ret = samdb_reference_dn(b_state->sam_ctx, frame, machine_dn,
1844 : "rIDSetReferences", &rid_set_dn);
1845 37 : if (ret != LDB_SUCCESS) {
1846 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find rIDSetReferences in %s - %s\n",
1847 : ldb_dn_get_linearized(server_dn),
1848 : ldb_errstring(b_state->sam_ctx)));
1849 0 : TALLOC_FREE(frame);
1850 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1851 : }
1852 :
1853 :
1854 : /* This first search will get the RID Manager, now get the RID set */
1855 37 : ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1856 : &search_res2,
1857 : rid_set_dn, LDB_SCOPE_BASE,
1858 : collect_objects_attrs,
1859 : NULL);
1860 37 : if (ret != LDB_SUCCESS) {
1861 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %s\n",
1862 : ldb_dn_get_linearized(rid_set_dn),
1863 : ldb_errstring(b_state->sam_ctx)));
1864 0 : TALLOC_FREE(frame);
1865 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1866 : }
1867 :
1868 37 : if (search_res2->count != 1) {
1869 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %u objects returned\n",
1870 : ldb_dn_get_linearized(rid_set_dn),
1871 : search_res2->count));
1872 0 : TALLOC_FREE(frame);
1873 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1874 : }
1875 :
1876 : /* Finally get the server DN */
1877 37 : ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1878 : &search_res3,
1879 : machine_dn, LDB_SCOPE_BASE,
1880 : collect_objects_attrs,
1881 : NULL);
1882 37 : if (ret != LDB_SUCCESS) {
1883 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %s\n",
1884 : ldb_dn_get_linearized(server_dn),
1885 : ldb_errstring(b_state->sam_ctx)));
1886 0 : TALLOC_FREE(frame);
1887 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1888 : }
1889 :
1890 37 : if (search_res3->count != 1) {
1891 0 : DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %u objects returned\n",
1892 : ldb_dn_get_linearized(server_dn),
1893 : search_res3->count));
1894 0 : TALLOC_FREE(frame);
1895 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1896 : }
1897 :
1898 : /* Now extend the original search_res with these answers */
1899 37 : (*search_res)->count = 3;
1900 :
1901 37 : (*search_res)->msgs = talloc_realloc(frame, (*search_res)->msgs,
1902 : struct ldb_message *,
1903 : (*search_res)->count);
1904 37 : if ((*search_res)->msgs == NULL) {
1905 0 : TALLOC_FREE(frame);
1906 0 : return WERR_NOT_ENOUGH_MEMORY;
1907 : }
1908 :
1909 :
1910 37 : talloc_steal(mem_ctx, *search_res);
1911 37 : (*search_res)->msgs[1] =
1912 37 : talloc_steal((*search_res)->msgs, search_res2->msgs[0]);
1913 37 : (*search_res)->msgs[2] =
1914 37 : talloc_steal((*search_res)->msgs, search_res3->msgs[0]);
1915 :
1916 37 : TALLOC_FREE(frame);
1917 37 : return WERR_OK;
1918 : }
1919 3258 : default:
1920 : /* TODO: implement extended op specific collection
1921 : * of objects. Right now we just normal procedure
1922 : * for collecting objects */
1923 3258 : return getncchanges_collect_objects(b_state,
1924 : mem_ctx,
1925 : getnc_state,
1926 : req10,
1927 : search_dn,
1928 : extra_filter,
1929 : search_res);
1930 : }
1931 : }
1932 :
1933 651504 : static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
1934 : uint64_t max_usn,
1935 : struct drsuapi_DsReplicaHighWaterMark *hwm)
1936 : {
1937 651504 : uint64_t uSN = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
1938 :
1939 651504 : if (uSN > max_usn) {
1940 : /*
1941 : * Only report the max_usn we had at the start
1942 : * of the replication cycle.
1943 : *
1944 : * If this object has changed lately we better
1945 : * let the destination dsa refetch the change.
1946 : * This is better than the risk of losing some
1947 : * objects or linked attributes.
1948 : */
1949 3962 : return;
1950 : }
1951 :
1952 647542 : if (uSN <= hwm->tmp_highest_usn) {
1953 59877 : return;
1954 : }
1955 :
1956 587665 : hwm->tmp_highest_usn = uSN;
1957 587665 : hwm->reserved_usn = 0;
1958 : }
1959 :
1960 : /**
1961 : * Adds an object's GUID to the cache of objects already sent.
1962 : * This avoids us sending the same object multiple times when
1963 : * the GetNCChanges request uses a flag like GET_ANC.
1964 : */
1965 456971 : static WERROR dcesrv_drsuapi_obj_cache_add(struct db_context *obj_cache,
1966 : const struct GUID *guid)
1967 : {
1968 0 : enum ndr_err_code ndr_err;
1969 456971 : uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
1970 456971 : DATA_BLOB b = {
1971 : .data = guid_buf,
1972 : .length = sizeof(guid_buf),
1973 : };
1974 456971 : TDB_DATA key = {
1975 456971 : .dptr = b.data,
1976 456971 : .dsize = b.length,
1977 : };
1978 456971 : TDB_DATA val = {
1979 : .dptr = NULL,
1980 : .dsize = 0,
1981 : };
1982 0 : NTSTATUS status;
1983 :
1984 456971 : ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
1985 : (ndr_push_flags_fn_t)ndr_push_GUID);
1986 456971 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1987 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1988 : }
1989 :
1990 456971 : status = dbwrap_store(obj_cache, key, val, TDB_REPLACE);
1991 456971 : if (!NT_STATUS_IS_OK(status)) {
1992 0 : return WERR_DS_DRA_INTERNAL_ERROR;
1993 : }
1994 :
1995 456971 : return WERR_OK;
1996 : }
1997 :
1998 : /**
1999 : * Checks if the object with the GUID specified already exists in the
2000 : * object cache, i.e. it's already been sent in a GetNCChanges response.
2001 : */
2002 910647 : static WERROR dcesrv_drsuapi_obj_cache_exists(struct db_context *obj_cache,
2003 : const struct GUID *guid)
2004 : {
2005 0 : enum ndr_err_code ndr_err;
2006 910647 : uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
2007 910647 : DATA_BLOB b = {
2008 : .data = guid_buf,
2009 : .length = sizeof(guid_buf),
2010 : };
2011 910647 : TDB_DATA key = {
2012 910647 : .dptr = b.data,
2013 910647 : .dsize = b.length,
2014 : };
2015 0 : bool exists;
2016 :
2017 910647 : ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
2018 : (ndr_push_flags_fn_t)ndr_push_GUID);
2019 910647 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2020 0 : return WERR_DS_DRA_INTERNAL_ERROR;
2021 : }
2022 :
2023 910647 : exists = dbwrap_exists(obj_cache, key);
2024 910647 : if (!exists) {
2025 458227 : return WERR_OBJECT_NOT_FOUND;
2026 : }
2027 :
2028 452420 : return WERR_OBJECT_NAME_EXISTS;
2029 : }
2030 :
2031 : /**
2032 : * Copies the la_list specified into a sorted array, ready to be sent in a
2033 : * GetNCChanges response.
2034 : */
2035 2225 : static WERROR getncchanges_get_sorted_array(const struct drsuapi_DsReplicaLinkedAttribute *la_list,
2036 : const uint32_t link_count,
2037 : struct ldb_context *sam_ctx,
2038 : TALLOC_CTX *mem_ctx,
2039 : const struct dsdb_schema *schema,
2040 : struct la_for_sorting **ret_array)
2041 : {
2042 0 : int j;
2043 0 : struct la_for_sorting *guid_array;
2044 2225 : WERROR werr = WERR_OK;
2045 :
2046 2225 : *ret_array = NULL;
2047 2225 : guid_array = talloc_array(mem_ctx, struct la_for_sorting, link_count);
2048 2225 : if (guid_array == NULL) {
2049 0 : DEBUG(0, ("Out of memory allocating %u linked attributes for sorting\n", link_count));
2050 0 : return WERR_NOT_ENOUGH_MEMORY;
2051 : }
2052 :
2053 23841 : for (j = 0; j < link_count; j++) {
2054 :
2055 : /* we need to get the target GUIDs to compare */
2056 0 : struct dsdb_dn *dn;
2057 21616 : const struct drsuapi_DsReplicaLinkedAttribute *la = &la_list[j];
2058 0 : const struct dsdb_attribute *schema_attrib;
2059 0 : const struct ldb_val *target_guid;
2060 0 : DATA_BLOB source_guid;
2061 21616 : TALLOC_CTX *frame = talloc_stackframe();
2062 0 : NTSTATUS status;
2063 :
2064 21616 : schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid);
2065 :
2066 21616 : werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn);
2067 21616 : if (!W_ERROR_IS_OK(werr)) {
2068 0 : DEBUG(0,(__location__ ": Bad la blob in sort\n"));
2069 0 : TALLOC_FREE(frame);
2070 0 : return werr;
2071 : }
2072 :
2073 : /* Extract the target GUID in NDR form */
2074 21616 : target_guid = ldb_dn_get_extended_component(dn->dn, "GUID");
2075 21616 : if (target_guid == NULL
2076 21616 : || target_guid->length != sizeof(guid_array[0].target_guid)) {
2077 0 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2078 : } else {
2079 : /* Repack the source GUID as NDR for sorting */
2080 21616 : status = GUID_to_ndr_blob(&la->identifier->guid,
2081 : frame,
2082 : &source_guid);
2083 : }
2084 :
2085 21616 : if (!NT_STATUS_IS_OK(status)
2086 21616 : || source_guid.length != sizeof(guid_array[0].source_guid)) {
2087 0 : DEBUG(0,(__location__ ": Bad la guid in sort\n"));
2088 0 : TALLOC_FREE(frame);
2089 0 : return ntstatus_to_werror(status);
2090 : }
2091 :
2092 21616 : guid_array[j].link = &la_list[j];
2093 21616 : memcpy(guid_array[j].target_guid, target_guid->data,
2094 : sizeof(guid_array[j].target_guid));
2095 21616 : memcpy(guid_array[j].source_guid, source_guid.data,
2096 : sizeof(guid_array[j].source_guid));
2097 21616 : TALLOC_FREE(frame);
2098 : }
2099 :
2100 2225 : TYPESAFE_QSORT(guid_array, link_count, linked_attribute_compare);
2101 :
2102 2225 : *ret_array = guid_array;
2103 :
2104 2225 : return werr;
2105 : }
2106 :
2107 :
2108 : /**
2109 : * Adds any ancestor/parent objects of the child_obj specified.
2110 : * This is needed when the GET_ANC flag is specified in the request.
2111 : * @param new_objs if parents are added, this gets updated to point to a chain
2112 : * of parent objects (with the parents first and the child last)
2113 : */
2114 448661 : static WERROR getncchanges_add_ancestors(const struct GUID *parent_object_guid,
2115 : struct ldb_dn *child_dn,
2116 : TALLOC_CTX *mem_ctx,
2117 : struct ldb_context *sam_ctx,
2118 : struct drsuapi_getncchanges_state *getnc_state,
2119 : struct dsdb_schema *schema,
2120 : DATA_BLOB *session_key,
2121 : struct drsuapi_DsGetNCChangesRequest10 *req10,
2122 : uint32_t *local_pas,
2123 : struct ldb_dn *machine_dn,
2124 : struct drsuapi_DsReplicaObjectListItemEx **new_objs)
2125 : {
2126 0 : int ret;
2127 448661 : const struct GUID *next_anc_guid = NULL;
2128 448661 : WERROR werr = WERR_OK;
2129 0 : static const char * const msg_attrs[] = {
2130 : "*",
2131 : "nTSecurityDescriptor",
2132 : "parentGUID",
2133 : "replPropertyMetaData",
2134 : DSDB_SECRET_ATTRIBUTES,
2135 : NULL };
2136 :
2137 448661 : next_anc_guid = parent_object_guid;
2138 :
2139 451577 : while (next_anc_guid != NULL) {
2140 450132 : struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
2141 450132 : struct ldb_message *anc_msg = NULL;
2142 450132 : struct ldb_result *anc_res = NULL;
2143 450132 : struct ldb_dn *anc_dn = NULL;
2144 :
2145 : /*
2146 : * For the GET_ANC case (but not the 'send NC root
2147 : * first' case), don't send an object twice.
2148 : *
2149 : * (If we've sent the object, then we've also sent all
2150 : * its parents as well)
2151 : */
2152 450132 : if (getnc_state->obj_cache) {
2153 449341 : werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
2154 : next_anc_guid);
2155 449341 : if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2156 447216 : return WERR_OK;
2157 : }
2158 2125 : if (W_ERROR_IS_OK(werr)) {
2159 0 : return WERR_INTERNAL_ERROR;
2160 : }
2161 2125 : if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
2162 0 : return werr;
2163 : }
2164 : }
2165 :
2166 2916 : anc_obj = talloc_zero(mem_ctx,
2167 : struct drsuapi_DsReplicaObjectListItemEx);
2168 2916 : if (anc_obj == NULL) {
2169 0 : return WERR_NOT_ENOUGH_MEMORY;
2170 : }
2171 :
2172 2916 : anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
2173 : GUID_string(anc_obj, next_anc_guid));
2174 2916 : if (anc_dn == NULL) {
2175 0 : return WERR_NOT_ENOUGH_MEMORY;
2176 : }
2177 :
2178 2916 : ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
2179 : &anc_res, anc_dn,
2180 : LDB_SCOPE_BASE,
2181 : msg_attrs, NULL);
2182 2916 : if (ret != LDB_SUCCESS) {
2183 0 : const char *anc_str = NULL;
2184 0 : const char *obj_str = NULL;
2185 :
2186 0 : anc_str = ldb_dn_get_extended_linearized(anc_obj,
2187 : anc_dn,
2188 : 1);
2189 0 : obj_str = ldb_dn_get_extended_linearized(anc_obj,
2190 : child_dn,
2191 : 1);
2192 :
2193 0 : DBG_ERR("getncchanges: failed to fetch ANC "
2194 : "DN %s for DN %s - %s\n",
2195 : anc_str, obj_str, ldb_errstring(sam_ctx));
2196 0 : return WERR_DS_DRA_INCONSISTENT_DIT;
2197 : }
2198 :
2199 2916 : anc_msg = anc_res->msgs[0];
2200 :
2201 2916 : werr = get_nc_changes_build_object(anc_obj, anc_msg,
2202 : sam_ctx,
2203 : getnc_state,
2204 : schema, session_key,
2205 : req10,
2206 : false, /* force_object_return */
2207 : local_pas,
2208 : machine_dn,
2209 : next_anc_guid);
2210 2916 : if (!W_ERROR_IS_OK(werr)) {
2211 0 : return werr;
2212 : }
2213 :
2214 : /*
2215 : * Regardless of whether we actually use it or not,
2216 : * we add it to the cache so we don't look at it again
2217 : *
2218 : * The only time we are here without
2219 : * getnc_state->obj_cache is for the forced addition
2220 : * of the NC root to the start of the reply, this we
2221 : * want to add each and every call..
2222 : */
2223 2916 : if (getnc_state->obj_cache) {
2224 2125 : werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
2225 : next_anc_guid);
2226 2125 : if (!W_ERROR_IS_OK(werr)) {
2227 0 : return werr;
2228 : }
2229 : }
2230 :
2231 : /*
2232 : * Any ancestors which are below the highwatermark
2233 : * or uptodateness_vector shouldn't be added,
2234 : * but we still look further up the
2235 : * tree for ones which have been changed recently.
2236 : */
2237 2916 : if (anc_obj->meta_data_ctr != NULL) {
2238 :
2239 : /*
2240 : * prepend the parent to the list so that the client-side
2241 : * adds the parent object before it adds the children
2242 : */
2243 2461 : anc_obj->next_object = *new_objs;
2244 2461 : *new_objs = anc_obj;
2245 : }
2246 :
2247 2916 : anc_msg = NULL;
2248 2916 : TALLOC_FREE(anc_res);
2249 2916 : TALLOC_FREE(anc_dn);
2250 :
2251 : /*
2252 : * We may need to resolve more parents...
2253 : */
2254 2916 : next_anc_guid = anc_obj->parent_object_guid;
2255 : }
2256 1445 : return werr;
2257 : }
2258 :
2259 : /**
2260 : * Adds a list of new objects into the current chunk of replication data to send
2261 : */
2262 576070 : static void getncchanges_chunk_add_objects(struct getncchanges_repl_chunk *repl_chunk,
2263 : struct drsuapi_DsReplicaObjectListItemEx *obj_list)
2264 : {
2265 0 : struct drsuapi_DsReplicaObjectListItemEx *obj;
2266 :
2267 : /*
2268 : * We track the last object added to the replication chunk, so just add
2269 : * the new object-list onto the end
2270 : */
2271 576070 : if (repl_chunk->object_list == NULL) {
2272 8099 : repl_chunk->object_list = obj_list;
2273 : } else {
2274 567971 : repl_chunk->last_object->next_object = obj_list;
2275 : }
2276 :
2277 1152581 : for (obj = obj_list; obj != NULL; obj = obj->next_object) {
2278 576511 : repl_chunk->object_count += 1;
2279 :
2280 : /*
2281 : * Remember the last object in the response - we'll use this to
2282 : * link the next object(s) processed onto the existing list
2283 : */
2284 576511 : if (obj->next_object == NULL) {
2285 575286 : repl_chunk->last_object = obj;
2286 : }
2287 : }
2288 576070 : }
2289 :
2290 : /**
2291 : * Gets the object to send, packed into an RPC struct ready to send. This also
2292 : * adds the object to the object cache, and adds any ancestors (if needed).
2293 : * @param msg - DB search result for the object to add
2294 : * @param guid - GUID of the object to add
2295 : * @param ret_obj_list - returns the object ready to be sent (in a list, along
2296 : * with any ancestors that might be needed). NULL if nothing to send.
2297 : */
2298 650154 : static WERROR getncchanges_get_obj_to_send(const struct ldb_message *msg,
2299 : TALLOC_CTX *mem_ctx,
2300 : struct ldb_context *sam_ctx,
2301 : struct drsuapi_getncchanges_state *getnc_state,
2302 : struct dsdb_schema *schema,
2303 : DATA_BLOB *session_key,
2304 : struct drsuapi_DsGetNCChangesRequest10 *req10,
2305 : bool force_object_return,
2306 : uint32_t *local_pas,
2307 : struct ldb_dn *machine_dn,
2308 : const struct GUID *guid,
2309 : struct drsuapi_DsReplicaObjectListItemEx **ret_obj_list)
2310 : {
2311 0 : struct drsuapi_DsReplicaObjectListItemEx *obj;
2312 0 : WERROR werr;
2313 :
2314 650154 : *ret_obj_list = NULL;
2315 :
2316 650154 : obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
2317 650154 : W_ERROR_HAVE_NO_MEMORY(obj);
2318 :
2319 650154 : werr = get_nc_changes_build_object(obj, msg, sam_ctx, getnc_state,
2320 : schema, session_key, req10,
2321 : force_object_return,
2322 : local_pas, machine_dn, guid);
2323 650154 : if (!W_ERROR_IS_OK(werr)) {
2324 0 : return werr;
2325 : }
2326 :
2327 : /*
2328 : * The object may get filtered out by the UTDV's USN and not actually
2329 : * sent, in which case there's nothing more to do here
2330 : */
2331 650154 : if (obj->meta_data_ctr == NULL) {
2332 76104 : TALLOC_FREE(obj);
2333 76104 : return WERR_OK;
2334 : }
2335 :
2336 574050 : if (getnc_state->obj_cache != NULL) {
2337 454846 : werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
2338 : guid);
2339 454846 : if (!W_ERROR_IS_OK(werr)) {
2340 0 : return werr;
2341 : }
2342 : }
2343 :
2344 574050 : *ret_obj_list = obj;
2345 :
2346 : /*
2347 : * If required, also add any ancestors that the client may need to know
2348 : * about before it can resolve this object. These get prepended to the
2349 : * ret_obj_list so the client adds them first.
2350 : *
2351 : * We allow this to be disabled to permit testing of a
2352 : * client-side fallback for the broken behaviour in Samba 4.5
2353 : * and earlier.
2354 : */
2355 574050 : if (getnc_state->is_get_anc
2356 450815 : && !getnc_state->broken_samba_4_5_get_anc_emulation) {
2357 446641 : werr = getncchanges_add_ancestors(obj->parent_object_guid,
2358 446641 : msg->dn, mem_ctx,
2359 : sam_ctx, getnc_state,
2360 : schema, session_key,
2361 : req10, local_pas,
2362 : machine_dn, ret_obj_list);
2363 : }
2364 :
2365 574050 : return werr;
2366 : }
2367 :
2368 : /**
2369 : * Returns the number of links that are waiting to be sent
2370 : */
2371 221907 : static uint32_t getncchanges_chunk_links_pending(struct getncchanges_repl_chunk *repl_chunk,
2372 : struct drsuapi_getncchanges_state *getnc_state)
2373 : {
2374 221907 : uint32_t links_to_send = 0;
2375 :
2376 221907 : if (getnc_state->is_get_tgt) {
2377 :
2378 : /*
2379 : * when the GET_TGT flag is set, only include the linked
2380 : * attributes whose target object has already been checked
2381 : * (i.e. they're ready to send).
2382 : */
2383 115446 : if (repl_chunk->tgt_la_count > getnc_state->la_idx) {
2384 23165 : links_to_send = (repl_chunk->tgt_la_count -
2385 23165 : getnc_state->la_idx);
2386 : }
2387 : } else {
2388 106461 : links_to_send = getnc_state->la_count - getnc_state->la_idx;
2389 : }
2390 :
2391 221907 : return links_to_send;
2392 : }
2393 :
2394 : /**
2395 : * Returns the max number of links that will fit in the current replication chunk
2396 : */
2397 332684 : static uint32_t getncchanges_chunk_max_links(struct getncchanges_repl_chunk *repl_chunk)
2398 : {
2399 332684 : uint32_t max_links = 0;
2400 :
2401 332684 : if (repl_chunk->max_links != DEFAULT_MAX_LINKS ||
2402 204028 : repl_chunk->max_objects != DEFAULT_MAX_OBJECTS) {
2403 :
2404 : /*
2405 : * We're using non-default settings, so don't try to adjust
2406 : * them, just trust the user has configured decent values
2407 : */
2408 329419 : max_links = repl_chunk->max_links;
2409 :
2410 3265 : } else if (repl_chunk->max_links > repl_chunk->object_count) {
2411 :
2412 : /*
2413 : * This is just an approximate guess to avoid overfilling the
2414 : * replication chunk. It's the logic we've used historically.
2415 : * E.g. if we've already sent 1000 objects, then send 1000 fewer
2416 : * links. For comparison, the max that Windows seems to send is
2417 : * ~2700 links and ~250 objects (although this may vary based
2418 : * on timeouts)
2419 : */
2420 3265 : max_links = repl_chunk->max_links - repl_chunk->object_count;
2421 : }
2422 :
2423 332684 : return max_links;
2424 : }
2425 :
2426 : /**
2427 : * Returns true if the current GetNCChanges() call has taken longer than its
2428 : * allotted time. This prevents the client from timing out.
2429 : */
2430 1231271 : static bool getncchanges_chunk_timed_out(struct getncchanges_repl_chunk *repl_chunk)
2431 : {
2432 1231271 : return (time(NULL) - repl_chunk->start > repl_chunk->max_wait);
2433 : }
2434 :
2435 : /**
2436 : * Returns true if the current chunk of replication data has reached the
2437 : * max_objects and/or max_links thresholds.
2438 : */
2439 658392 : static bool getncchanges_chunk_is_full(struct getncchanges_repl_chunk *repl_chunk,
2440 : struct drsuapi_getncchanges_state *getnc_state)
2441 : {
2442 658392 : bool chunk_full = false;
2443 0 : uint32_t links_to_send;
2444 0 : uint32_t chunk_limit;
2445 :
2446 : /* check if the current chunk is already full with objects */
2447 658392 : if (repl_chunk->object_count >= repl_chunk->max_objects) {
2448 1948 : chunk_full = true;
2449 :
2450 1239525 : } else if (repl_chunk->object_count > 0 &&
2451 583081 : getncchanges_chunk_timed_out(repl_chunk)) {
2452 :
2453 : /*
2454 : * We've exceeded our allotted time building this chunk,
2455 : * and we have at least one object to send back to the client
2456 : */
2457 0 : chunk_full = true;
2458 :
2459 656444 : } else if (repl_chunk->immediate_link_sync) {
2460 :
2461 : /* check if the chunk is already full with links */
2462 214911 : links_to_send = getncchanges_chunk_links_pending(repl_chunk,
2463 : getnc_state);
2464 :
2465 214911 : chunk_limit = getncchanges_chunk_max_links(repl_chunk);
2466 :
2467 : /*
2468 : * The chunk is full if we've got more links to send than will
2469 : * fit in one chunk
2470 : */
2471 214911 : if (links_to_send > 0 && chunk_limit <= links_to_send) {
2472 16 : chunk_full = true;
2473 : }
2474 : }
2475 :
2476 658392 : return chunk_full;
2477 : }
2478 :
2479 : /**
2480 : * Goes through any new linked attributes and checks that the target object
2481 : * will be known to the client, i.e. we've already sent it in an replication
2482 : * chunk. If not, then it adds the target object to the current replication
2483 : * chunk. This is only done when the client specifies DRS_GET_TGT.
2484 : */
2485 110509 : static WERROR getncchanges_chunk_add_la_targets(struct getncchanges_repl_chunk *repl_chunk,
2486 : struct drsuapi_getncchanges_state *getnc_state,
2487 : uint32_t start_la_index,
2488 : TALLOC_CTX *mem_ctx,
2489 : struct ldb_context *sam_ctx,
2490 : struct dsdb_schema *schema,
2491 : DATA_BLOB *session_key,
2492 : struct drsuapi_DsGetNCChangesRequest10 *req10,
2493 : uint32_t *local_pas,
2494 : struct ldb_dn *machine_dn)
2495 : {
2496 0 : int ret;
2497 0 : uint32_t i;
2498 0 : uint32_t max_la_index;
2499 0 : uint32_t max_links;
2500 110509 : uint32_t target_count = 0;
2501 110509 : WERROR werr = WERR_OK;
2502 0 : static const char * const msg_attrs[] = {
2503 : "*",
2504 : "nTSecurityDescriptor",
2505 : "parentGUID",
2506 : "replPropertyMetaData",
2507 : DSDB_SECRET_ATTRIBUTES,
2508 : NULL };
2509 :
2510 : /*
2511 : * A object can potentially link to thousands of targets. Only bother
2512 : * checking as many targets as will fit into the current response
2513 : */
2514 110509 : max_links = getncchanges_chunk_max_links(repl_chunk);
2515 110509 : max_la_index = MIN(getnc_state->la_count,
2516 : start_la_index + max_links);
2517 :
2518 : /* loop through any linked attributes to check */
2519 110509 : for (i = start_la_index;
2520 115433 : (i < max_la_index &&
2521 4932 : !getncchanges_chunk_is_full(repl_chunk, getnc_state));
2522 4924 : i++) {
2523 :
2524 0 : struct GUID target_guid;
2525 4924 : struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
2526 0 : const struct drsuapi_DsReplicaLinkedAttribute *la;
2527 0 : struct ldb_result *msg_res;
2528 0 : struct ldb_dn *search_dn;
2529 0 : TALLOC_CTX *tmp_ctx;
2530 0 : struct dsdb_dn *dn;
2531 0 : const struct dsdb_attribute *schema_attrib;
2532 0 : NTSTATUS status;
2533 0 : bool same_nc;
2534 :
2535 4924 : la = &getnc_state->la_list[i];
2536 4924 : tmp_ctx = talloc_new(mem_ctx);
2537 :
2538 : /*
2539 : * Track what linked attribute targets we've checked. We might
2540 : * not have time to check them all, so we should only send back
2541 : * the ones we've actually checked.
2542 : */
2543 4924 : repl_chunk->tgt_la_count = i + 1;
2544 :
2545 : /* get the GUID of the linked attribute's target object */
2546 4924 : schema_attrib = dsdb_attribute_by_attributeID_id(schema,
2547 4924 : la->attid);
2548 :
2549 4924 : werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema,
2550 4924 : tmp_ctx, la->value.blob, &dn);
2551 :
2552 4924 : if (!W_ERROR_IS_OK(werr)) {
2553 0 : DEBUG(0,(__location__ ": Bad la blob\n"));
2554 0 : return werr;
2555 : }
2556 :
2557 4924 : status = dsdb_get_extended_dn_guid(dn->dn, &target_guid, "GUID");
2558 :
2559 4924 : if (!NT_STATUS_IS_OK(status)) {
2560 0 : return ntstatus_to_werror(status);
2561 : }
2562 :
2563 : /*
2564 : * if the target isn't in the cache, then the client
2565 : * might not know about it, so send the target now
2566 : */
2567 4924 : werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
2568 : &target_guid);
2569 :
2570 4924 : if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2571 :
2572 : /* target already sent, nothing to do */
2573 2108 : TALLOC_FREE(tmp_ctx);
2574 2108 : continue;
2575 : }
2576 :
2577 2816 : same_nc = dsdb_objects_have_same_nc(sam_ctx, tmp_ctx, dn->dn,
2578 : getnc_state->ncRoot_dn);
2579 :
2580 : /* don't try to fetch target objects from another partition */
2581 2816 : if (!same_nc) {
2582 852 : TALLOC_FREE(tmp_ctx);
2583 852 : continue;
2584 : }
2585 :
2586 1964 : search_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
2587 : GUID_string(tmp_ctx, &target_guid));
2588 1964 : W_ERROR_HAVE_NO_MEMORY(search_dn);
2589 :
2590 1964 : ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx,
2591 : &msg_res, search_dn,
2592 : LDB_SCOPE_BASE,
2593 : msg_attrs, NULL);
2594 :
2595 : /*
2596 : * Don't fail the replication if we can't find the target.
2597 : * This could happen for a one-way linked attribute, if the
2598 : * target is deleted and then later expunged (thus, the source
2599 : * object can be left with a hanging link). Continue to send
2600 : * the the link (the client-side has already tried once with
2601 : * GET_TGT, so it should just end up ignoring it).
2602 : */
2603 1964 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
2604 0 : DBG_WARNING("Encountered unknown link target DN %s\n",
2605 : ldb_dn_get_extended_linearized(tmp_ctx, dn->dn, 1));
2606 0 : TALLOC_FREE(tmp_ctx);
2607 0 : continue;
2608 :
2609 1964 : } else if (ret != LDB_SUCCESS) {
2610 0 : DBG_ERR("Failed to fetch link target DN %s - %s\n",
2611 : ldb_dn_get_extended_linearized(tmp_ctx, dn->dn, 1),
2612 : ldb_errstring(sam_ctx));
2613 0 : return WERR_DS_DRA_INCONSISTENT_DIT;
2614 : }
2615 :
2616 : /*
2617 : * Construct an object, ready to send (this will include
2618 : * the object's ancestors as well, if GET_ANC is set)
2619 : */
2620 1964 : werr = getncchanges_get_obj_to_send(msg_res->msgs[0], mem_ctx,
2621 : sam_ctx, getnc_state,
2622 : schema, session_key, req10,
2623 : false, local_pas,
2624 : machine_dn, &target_guid,
2625 : &new_objs);
2626 1964 : if (!W_ERROR_IS_OK(werr)) {
2627 0 : return werr;
2628 : }
2629 :
2630 1964 : if (new_objs != NULL) {
2631 1854 : target_count++;
2632 1854 : getncchanges_chunk_add_objects(repl_chunk, new_objs);
2633 : }
2634 1964 : TALLOC_FREE(tmp_ctx);
2635 : }
2636 :
2637 110509 : if (target_count > 0) {
2638 1282 : DEBUG(3, ("GET_TGT: checked %u link-attrs, added %u target objs\n",
2639 : i - start_la_index, target_count));
2640 : }
2641 :
2642 110509 : return WERR_OK;
2643 : }
2644 :
2645 : /**
2646 : * Creates a helper struct used for building a chunk of replication data,
2647 : * i.e. used over a single call to dcesrv_drsuapi_DsGetNCChanges().
2648 : */
2649 10301 : static struct getncchanges_repl_chunk * getncchanges_chunk_new(TALLOC_CTX *mem_ctx,
2650 : struct dcesrv_call_state *dce_call,
2651 : struct drsuapi_DsGetNCChangesRequest10 *req10)
2652 : {
2653 0 : struct getncchanges_repl_chunk *repl_chunk;
2654 :
2655 10301 : repl_chunk = talloc_zero(mem_ctx, struct getncchanges_repl_chunk);
2656 :
2657 10301 : repl_chunk->start = time(NULL);
2658 :
2659 10301 : repl_chunk->max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL,
2660 : "drs", "max object sync",
2661 : DEFAULT_MAX_OBJECTS);
2662 :
2663 : /*
2664 : * The client control here only applies in normal replication, not extended
2665 : * operations, which return a fixed set, even if the caller
2666 : * sets max_object_count == 0
2667 : */
2668 10301 : if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2669 :
2670 : /*
2671 : * use this to force single objects at a time, which is useful
2672 : * for working out what object is giving problems
2673 : */
2674 5066 : if (req10->max_object_count < repl_chunk->max_objects) {
2675 5066 : repl_chunk->max_objects = req10->max_object_count;
2676 : }
2677 : }
2678 :
2679 10301 : repl_chunk->max_links =
2680 10301 : lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL,
2681 : "drs", "max link sync",
2682 : DEFAULT_MAX_LINKS);
2683 :
2684 10301 : repl_chunk->immediate_link_sync =
2685 10301 : lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL,
2686 : "drs", "immediate link sync", false);
2687 :
2688 : /*
2689 : * Maximum time that we can spend in a getncchanges
2690 : * in order to avoid timeout of the other part.
2691 : * 10 seconds by default.
2692 : */
2693 10301 : repl_chunk->max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx,
2694 : NULL, "drs", "max work time", 10);
2695 :
2696 10301 : return repl_chunk;
2697 : }
2698 :
2699 : /*
2700 : drsuapi_DsGetNCChanges
2701 :
2702 : see MS-DRSR 4.1.10.5.2 for basic logic of this function
2703 : */
2704 10301 : WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2705 : struct drsuapi_DsGetNCChanges *r)
2706 : {
2707 0 : struct auth_session_info *session_info =
2708 10301 : dcesrv_call_session_info(dce_call);
2709 0 : struct imessaging_context *imsg_ctx =
2710 10301 : dcesrv_imessaging_context(dce_call->conn);
2711 0 : struct drsuapi_DsReplicaObjectIdentifier *untrusted_ncRoot;
2712 0 : int ret;
2713 0 : uint32_t i, k;
2714 0 : struct dsdb_schema *schema;
2715 0 : struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
2716 0 : struct getncchanges_repl_chunk *repl_chunk;
2717 0 : NTSTATUS status;
2718 0 : DATA_BLOB session_key;
2719 0 : WERROR werr;
2720 0 : struct dcesrv_handle *h;
2721 0 : struct drsuapi_bind_state *b_state;
2722 10301 : struct drsuapi_getncchanges_state *getnc_state = NULL;
2723 0 : struct drsuapi_DsGetNCChangesRequest10 *req10;
2724 0 : uint32_t options;
2725 10301 : uint32_t link_count = 0;
2726 10301 : struct ldb_dn *search_dn = NULL;
2727 0 : bool am_rodc;
2728 0 : enum security_user_level security_level;
2729 0 : struct ldb_context *sam_ctx;
2730 0 : struct dom_sid *user_sid;
2731 0 : bool is_secret_request;
2732 0 : bool is_gc_pas_request;
2733 0 : struct drsuapi_changed_objects *changes;
2734 10301 : bool has_get_all_changes = false;
2735 0 : struct GUID invocation_id;
2736 0 : static const struct drsuapi_DsReplicaLinkedAttribute no_linked_attr;
2737 10301 : struct dsdb_schema_prefixmap *pfm_remote = NULL;
2738 10301 : bool full = true;
2739 10301 : uint32_t *local_pas = NULL;
2740 10301 : struct ldb_dn *machine_dn = NULL; /* Only used for REPL SECRET EXOP */
2741 :
2742 10301 : DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
2743 10301 : b_state = h->data;
2744 :
2745 : /* sam_ctx_system is not present for non-administrator users */
2746 10301 : sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
2747 :
2748 10301 : invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2749 :
2750 10301 : *r->out.level_out = 6;
2751 :
2752 10301 : r->out.ctr->ctr6.linked_attributes_count = 0;
2753 10301 : r->out.ctr->ctr6.linked_attributes = discard_const_p(struct drsuapi_DsReplicaLinkedAttribute, &no_linked_attr);
2754 :
2755 10301 : r->out.ctr->ctr6.object_count = 0;
2756 10301 : r->out.ctr->ctr6.nc_object_count = 0;
2757 10301 : r->out.ctr->ctr6.more_data = false;
2758 10301 : r->out.ctr->ctr6.uptodateness_vector = NULL;
2759 10301 : r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
2760 10301 : r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2761 10301 : r->out.ctr->ctr6.first_object = NULL;
2762 :
2763 : /* Check request revision.
2764 : */
2765 10301 : switch (r->in.level) {
2766 3483 : case 8:
2767 3483 : req10 = getncchanges_map_req8(mem_ctx, &r->in.req->req8);
2768 3483 : if (req10 == NULL) {
2769 0 : return WERR_NOT_ENOUGH_MEMORY;
2770 : }
2771 3483 : break;
2772 6818 : case 10:
2773 6818 : req10 = &r->in.req->req10;
2774 6818 : break;
2775 0 : default:
2776 0 : DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
2777 : r->in.level));
2778 0 : return WERR_REVISION_MISMATCH;
2779 : }
2780 :
2781 10301 : repl_chunk = getncchanges_chunk_new(mem_ctx, dce_call, req10);
2782 :
2783 10301 : if (repl_chunk == NULL) {
2784 0 : return WERR_NOT_ENOUGH_MEMORY;
2785 : }
2786 :
2787 : /* a RODC doesn't allow for any replication */
2788 10301 : ret = samdb_rodc(sam_ctx, &am_rodc);
2789 10301 : if (ret == LDB_SUCCESS && am_rodc) {
2790 0 : DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
2791 0 : return WERR_DS_DRA_SOURCE_DISABLED;
2792 : }
2793 :
2794 : /*
2795 : * Help our tests pass by pre-checking the
2796 : * destination_dsa_guid before the NC permissions. Info on
2797 : * valid DSA GUIDs is not sensitive so this isn't a leak
2798 : */
2799 10301 : switch (req10->extended_op) {
2800 79 : case DRSUAPI_EXOP_FSMO_REQ_ROLE:
2801 : case DRSUAPI_EXOP_FSMO_RID_ALLOC:
2802 : case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
2803 : case DRSUAPI_EXOP_FSMO_REQ_PDC:
2804 : case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
2805 : {
2806 79 : const char *attrs[] = { NULL };
2807 :
2808 79 : ret = samdb_get_ntds_obj_by_guid(mem_ctx,
2809 : sam_ctx,
2810 79 : &req10->destination_dsa_guid,
2811 : attrs,
2812 : NULL);
2813 79 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
2814 : /*
2815 : * Error out with an EXOP error but success at
2816 : * the top level return value
2817 : */
2818 13 : r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
2819 13 : return WERR_OK;
2820 66 : } else if (ret != LDB_SUCCESS) {
2821 0 : return WERR_DS_DRA_INTERNAL_ERROR;
2822 : }
2823 :
2824 66 : break;
2825 : }
2826 10222 : case DRSUAPI_EXOP_REPL_SECRET:
2827 : case DRSUAPI_EXOP_REPL_OBJ:
2828 : case DRSUAPI_EXOP_NONE:
2829 10222 : break;
2830 : }
2831 :
2832 : /* Perform access checks. */
2833 10288 : untrusted_ncRoot = req10->naming_context;
2834 10288 : if (untrusted_ncRoot == NULL) {
2835 0 : DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
2836 0 : return WERR_DS_DRA_INVALID_PARAMETER;
2837 : }
2838 :
2839 10288 : if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) {
2840 0 : return WERR_DS_DRA_INTERNAL_ERROR;
2841 : }
2842 :
2843 10288 : if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
2844 1268 : !(req10->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
2845 687 : return WERR_DS_DRA_SOURCE_DISABLED;
2846 : }
2847 :
2848 9601 : user_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
2849 :
2850 : /*
2851 : * all clients must have GUID_DRS_GET_CHANGES. This finds the
2852 : * actual NC root of the given value and checks that, allowing
2853 : * REPL_OBJ to work safely
2854 : */
2855 9601 : werr = drs_security_access_check_nc_root(sam_ctx,
2856 : mem_ctx,
2857 : session_info->security_token,
2858 : req10->naming_context,
2859 : GUID_DRS_GET_CHANGES);
2860 :
2861 9601 : if (W_ERROR_EQUAL(werr, WERR_DS_DRA_BAD_NC)) {
2862 : /*
2863 : * These extended operations need a different error if
2864 : * the supplied DN can't be found
2865 : */
2866 8 : switch (req10->extended_op) {
2867 6 : case DRSUAPI_EXOP_REPL_OBJ:
2868 : case DRSUAPI_EXOP_REPL_SECRET:
2869 6 : return WERR_DS_DRA_BAD_DN;
2870 2 : default:
2871 2 : return werr;
2872 : }
2873 : }
2874 9593 : if (!W_ERROR_IS_OK(werr)) {
2875 44 : return werr;
2876 : }
2877 :
2878 9549 : if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) {
2879 13773 : full = req10->partial_attribute_set == NULL &&
2880 4421 : req10->partial_attribute_set_ex == NULL;
2881 : } else {
2882 197 : full = (options & DRSUAPI_DRS_WRIT_REP) != 0;
2883 : }
2884 :
2885 9549 : werr = dsdb_schema_pfm_from_drsuapi_pfm(&req10->mapping_ctr, true,
2886 : mem_ctx, &pfm_remote, NULL);
2887 :
2888 : /* We were supplied a partial attribute set, without the prefix map! */
2889 9549 : if (!full && !W_ERROR_IS_OK(werr)) {
2890 3303 : if (req10->mapping_ctr.num_mappings == 0) {
2891 : /*
2892 : * Despite the fact MS-DRSR specifies that this shouldn't
2893 : * happen, Windows RODCs will in fact not provide a prefixMap.
2894 : */
2895 3303 : DEBUG(5,(__location__ ": Failed to provide a remote prefixMap,"
2896 : " falling back to local prefixMap\n"));
2897 : } else {
2898 0 : DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
2899 : win_errstr(werr)));
2900 0 : return werr;
2901 : }
2902 : }
2903 :
2904 : /* allowed if the GC PAS and client has
2905 : GUID_DRS_GET_FILTERED_ATTRIBUTES */
2906 9549 : werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, pfm_remote, &is_gc_pas_request);
2907 9549 : if (!W_ERROR_IS_OK(werr)) {
2908 3 : return werr;
2909 : }
2910 9546 : if (is_gc_pas_request) {
2911 17 : werr = drs_security_access_check_nc_root(sam_ctx,
2912 : mem_ctx,
2913 : session_info->security_token,
2914 : req10->naming_context,
2915 : GUID_DRS_GET_FILTERED_ATTRIBUTES);
2916 17 : if (W_ERROR_IS_OK(werr)) {
2917 9 : goto allowed;
2918 : }
2919 : }
2920 :
2921 9537 : werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10,
2922 : pfm_remote,
2923 : &is_secret_request);
2924 9537 : if (!W_ERROR_IS_OK(werr)) {
2925 0 : return werr;
2926 : }
2927 9537 : if (is_secret_request) {
2928 8664 : werr = drs_security_access_check_nc_root(sam_ctx,
2929 : mem_ctx,
2930 : session_info->security_token,
2931 : req10->naming_context,
2932 : GUID_DRS_GET_ALL_CHANGES);
2933 8664 : if (!W_ERROR_IS_OK(werr)) {
2934 : /* Only bail if this is not a EXOP_REPL_SECRET */
2935 1363 : if (req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
2936 10 : return werr;
2937 : }
2938 : } else {
2939 7301 : has_get_all_changes = true;
2940 : }
2941 : }
2942 :
2943 873 : allowed:
2944 : /* for non-administrator replications, check that they have
2945 : given the correct source_dsa_invocation_id */
2946 9536 : security_level = security_session_user_level(session_info,
2947 : samdb_domain_sid(sam_ctx));
2948 9536 : if (security_level == SECURITY_RO_DOMAIN_CONTROLLER) {
2949 1954 : if (req10->replica_flags & DRSUAPI_DRS_WRIT_REP) {
2950 : /* we rely on this flag being unset for RODC requests */
2951 0 : req10->replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
2952 : }
2953 : }
2954 :
2955 9536 : if (req10->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
2956 : /* Ignore the _in_ uptodateness vector*/
2957 0 : req10->uptodateness_vector = NULL;
2958 : }
2959 :
2960 9536 : if (GUID_all_zero(&req10->source_dsa_invocation_id)) {
2961 4395 : req10->source_dsa_invocation_id = invocation_id;
2962 : }
2963 :
2964 9536 : if (!GUID_equal(&req10->source_dsa_invocation_id, &invocation_id)) {
2965 : /*
2966 : * The given highwatermark is only valid relative to the
2967 : * specified source_dsa_invocation_id.
2968 : */
2969 2 : ZERO_STRUCT(req10->highwatermark);
2970 : }
2971 :
2972 : /*
2973 : * An extended operation is "special single-response cycle"
2974 : * per MS-DRSR 4.1.10.1.1 "Start and Finish" so we don't need
2975 : * to guess if this is a continuation of any long-term
2976 : * state.
2977 : *
2978 : * Otherwise, maintain (including marking as stale, which is
2979 : * what the below is for) the replication state.
2980 : *
2981 : * Note that point 5 "The server implementation MAY declare
2982 : * the supplied values ... as too stale to use." would allow
2983 : * resetting the state at almost any point, Microsoft Azure AD
2984 : * Connect will switch back and forth between a REPL_OBJ and a
2985 : * full replication, so we must not reset the statue during
2986 : * extended operations.
2987 : */
2988 9536 : if (req10->extended_op == DRSUAPI_EXOP_NONE &&
2989 4894 : b_state->getncchanges_full_repl_state != NULL) {
2990 : /*
2991 : * Knowing that this is not an extended operation, we
2992 : * can access (and validate) the full replication
2993 : * state
2994 : */
2995 2174 : getnc_state = b_state->getncchanges_full_repl_state;
2996 : }
2997 :
2998 : /* see if a previous replication has been abandoned */
2999 9536 : if (getnc_state != NULL) {
3000 0 : struct ldb_dn *new_dn;
3001 2174 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(getnc_state,
3002 : sam_ctx,
3003 : untrusted_ncRoot,
3004 : &new_dn,
3005 : NULL);
3006 2174 : if (ret != LDB_SUCCESS) {
3007 : /*
3008 : * This can't fail as we have done this above
3009 : * implicitly but not got the DN out, but
3010 : * print a good error message regardless just
3011 : * in case.
3012 : */
3013 0 : DBG_ERR("Bad DN '%s' as Naming Context for GetNCChanges: %s\n",
3014 : drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3015 : ldb_strerror(ret));
3016 0 : return WERR_DS_DRA_INVALID_PARAMETER;
3017 : }
3018 2174 : if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
3019 9 : DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
3020 : ldb_dn_get_linearized(new_dn),
3021 : ldb_dn_get_linearized(getnc_state->ncRoot_dn),
3022 : ldb_dn_get_linearized(getnc_state->last_dn)));
3023 9 : TALLOC_FREE(getnc_state);
3024 9 : b_state->getncchanges_full_repl_state = NULL;
3025 : }
3026 : }
3027 :
3028 9536 : if (getnc_state != NULL) {
3029 2165 : ret = drsuapi_DsReplicaHighWaterMark_cmp(&getnc_state->last_hwm,
3030 2165 : &req10->highwatermark);
3031 2165 : if (ret != 0) {
3032 21 : DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication "
3033 : "on DN %s %s highwatermark (last_dn %s)\n",
3034 : ldb_dn_get_linearized(getnc_state->ncRoot_dn),
3035 : (ret > 0) ? "older" : "newer",
3036 : ldb_dn_get_linearized(getnc_state->last_dn)));
3037 21 : TALLOC_FREE(getnc_state);
3038 21 : b_state->getncchanges_full_repl_state = NULL;
3039 : }
3040 : }
3041 :
3042 : /*
3043 : * This is either a new replication cycle, or an extended
3044 : * operation. A new cycle is triggered above by the
3045 : * TALLOC_FREE() which sets getnc_state to NULL.
3046 : */
3047 9536 : if (getnc_state == NULL) {
3048 7392 : struct ldb_result *res = NULL;
3049 7392 : const char *attrs[] = {
3050 : "instanceType",
3051 : "objectGuID",
3052 : NULL
3053 : };
3054 0 : uint32_t nc_instanceType;
3055 0 : struct ldb_dn *ncRoot_dn;
3056 :
3057 7392 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
3058 : sam_ctx,
3059 : untrusted_ncRoot,
3060 : &ncRoot_dn,
3061 : NULL);
3062 7392 : if (ret != LDB_SUCCESS) {
3063 8 : DBG_ERR("Bad DN '%s' as Naming Context or EXOP DN for GetNCChanges: %s\n",
3064 : drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3065 : ldb_strerror(ret));
3066 8 : return WERR_DS_DRA_BAD_DN;
3067 : }
3068 :
3069 7384 : ret = dsdb_search_dn(sam_ctx, mem_ctx, &res,
3070 : ncRoot_dn, attrs,
3071 : DSDB_SEARCH_SHOW_DELETED |
3072 : DSDB_SEARCH_SHOW_RECYCLED);
3073 7384 : if (ret != LDB_SUCCESS) {
3074 0 : DBG_WARNING("Failed to find ncRoot_dn %s\n",
3075 : ldb_dn_get_linearized(ncRoot_dn));
3076 0 : return WERR_DS_DRA_BAD_DN;
3077 : }
3078 7384 : nc_instanceType = ldb_msg_find_attr_as_int(res->msgs[0],
3079 : "instanceType",
3080 : 0);
3081 :
3082 7384 : if (req10->extended_op != DRSUAPI_EXOP_NONE) {
3083 4636 : r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
3084 : }
3085 :
3086 : /*
3087 : * This is the first replication cycle and it is
3088 : * a good place to handle extended operations
3089 : *
3090 : * FIXME: we don't fully support extended operations yet
3091 : */
3092 7384 : switch (req10->extended_op) {
3093 2748 : case DRSUAPI_EXOP_NONE:
3094 2748 : if ((nc_instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) {
3095 0 : const char *dn_str
3096 5 : = ldb_dn_get_linearized(ncRoot_dn);
3097 :
3098 5 : DBG_NOTICE("Rejecting full replication on "
3099 : "not NC %s\n", dn_str);
3100 :
3101 5 : return WERR_DS_CANT_FIND_EXPECTED_NC;
3102 : }
3103 :
3104 6038 : break;
3105 41 : case DRSUAPI_EXOP_FSMO_RID_ALLOC:
3106 41 : werr = getncchanges_rid_alloc(b_state, mem_ctx, req10, &r->out.ctr->ctr6, &search_dn);
3107 41 : W_ERROR_NOT_OK_RETURN(werr);
3108 41 : if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3109 4 : return WERR_OK;
3110 : }
3111 37 : break;
3112 4403 : case DRSUAPI_EXOP_REPL_SECRET:
3113 4403 : werr = getncchanges_repl_secret(b_state, mem_ctx, req10,
3114 : user_sid,
3115 4403 : &r->out.ctr->ctr6,
3116 : has_get_all_changes,
3117 : &machine_dn);
3118 4403 : r->out.result = werr;
3119 4403 : W_ERROR_NOT_OK_RETURN(werr);
3120 3068 : break;
3121 15 : case DRSUAPI_EXOP_FSMO_REQ_ROLE:
3122 15 : werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
3123 15 : W_ERROR_NOT_OK_RETURN(werr);
3124 15 : if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3125 2 : return WERR_OK;
3126 : }
3127 13 : break;
3128 6 : case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
3129 6 : werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
3130 6 : W_ERROR_NOT_OK_RETURN(werr);
3131 6 : if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3132 0 : return WERR_OK;
3133 : }
3134 6 : break;
3135 4 : case DRSUAPI_EXOP_FSMO_REQ_PDC:
3136 4 : werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
3137 4 : W_ERROR_NOT_OK_RETURN(werr);
3138 4 : if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3139 0 : return WERR_OK;
3140 : }
3141 4 : break;
3142 167 : case DRSUAPI_EXOP_REPL_OBJ:
3143 167 : werr = getncchanges_repl_obj(b_state, mem_ctx, req10, user_sid, &r->out.ctr->ctr6);
3144 167 : r->out.result = werr;
3145 167 : W_ERROR_NOT_OK_RETURN(werr);
3146 167 : break;
3147 :
3148 0 : case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
3149 :
3150 0 : DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
3151 : (unsigned)req10->extended_op));
3152 0 : return WERR_DS_DRA_NOT_SUPPORTED;
3153 : }
3154 :
3155 : /*
3156 : * Initialize the state, initially for the remainder
3157 : * of this call (EXOPs)
3158 : *
3159 : * An extended operation is a "special single-response
3160 : * cycle" per MS-DRSR 4.1.10.1.1 "Start and Finish"
3161 : *
3162 : */
3163 6038 : getnc_state = talloc_zero(mem_ctx, struct drsuapi_getncchanges_state);
3164 6038 : if (getnc_state == NULL) {
3165 0 : return WERR_NOT_ENOUGH_MEMORY;
3166 : }
3167 :
3168 6038 : if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3169 : /*
3170 : * Promote the memory to being a store of
3171 : * long-term state that we will use over the
3172 : * replication cycle for full replication
3173 : * requests
3174 : *
3175 : * Store the state in a clearly named location
3176 : * for pulling back only during full
3177 : * replications
3178 : */
3179 0 : b_state->getncchanges_full_repl_state
3180 2743 : = talloc_steal(b_state, getnc_state);
3181 : }
3182 :
3183 6038 : getnc_state->ncRoot_dn = ncRoot_dn;
3184 6038 : talloc_steal(getnc_state, ncRoot_dn);
3185 :
3186 6038 : getnc_state->ncRoot_guid = samdb_result_guid(res->msgs[0],
3187 : "objectGUID");
3188 :
3189 : /* find out if we are to replicate Schema NC */
3190 6038 : ret = ldb_dn_compare_base(ldb_get_schema_basedn(sam_ctx),
3191 : ncRoot_dn);
3192 6038 : getnc_state->is_schema_nc = (0 == ret);
3193 :
3194 6038 : TALLOC_FREE(res);
3195 : }
3196 :
3197 : /* we need the session key for encrypting password attributes */
3198 8182 : status = dcesrv_auth_session_key(dce_call, &session_key);
3199 8182 : if (!NT_STATUS_IS_OK(status)) {
3200 0 : DEBUG(0,(__location__ ": Failed to get session key\n"));
3201 0 : return WERR_DS_DRA_INTERNAL_ERROR;
3202 : }
3203 :
3204 : /*
3205 : TODO: MS-DRSR section 4.1.10.1.1
3206 : Work out if this is the start of a new cycle */
3207 :
3208 8182 : if (getnc_state->guids == NULL) {
3209 0 : const char *extra_filter;
3210 6038 : struct ldb_result *search_res = NULL;
3211 0 : static const struct drsuapi_DsReplicaCursorCtrEx empty_udv;
3212 6038 : const struct drsuapi_DsReplicaCursorCtrEx *udv = NULL;
3213 :
3214 6038 : extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
3215 :
3216 6038 : if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3217 2743 : if (req10->uptodateness_vector != NULL) {
3218 2070 : udv = req10->uptodateness_vector;
3219 : } else {
3220 673 : udv = &empty_udv;
3221 : }
3222 :
3223 2743 : getnc_state->min_usn = req10->highwatermark.highest_usn;
3224 4072 : for (i = 0; i < udv->count; i++) {
3225 0 : bool match;
3226 3351 : const struct drsuapi_DsReplicaCursor *cur =
3227 3351 : &udv->cursors[i];
3228 :
3229 3351 : match = GUID_equal(&invocation_id,
3230 : &cur->source_dsa_invocation_id);
3231 3351 : if (!match) {
3232 1329 : continue;
3233 : }
3234 2022 : if (cur->highest_usn > getnc_state->min_usn) {
3235 441 : getnc_state->min_usn = cur->highest_usn;
3236 : }
3237 2022 : break;
3238 : }
3239 : } else {
3240 : /* We do not want REPL_SECRETS or REPL_SINGLE to return empty-handed */
3241 3295 : udv = &empty_udv;
3242 3295 : getnc_state->min_usn = 0;
3243 : }
3244 :
3245 6038 : getnc_state->max_usn = getnc_state->min_usn;
3246 :
3247 6038 : getnc_state->final_udv = talloc_zero(getnc_state,
3248 : struct drsuapi_DsReplicaCursor2CtrEx);
3249 6038 : if (getnc_state->final_udv == NULL) {
3250 0 : return WERR_NOT_ENOUGH_MEMORY;
3251 : }
3252 6038 : werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn,
3253 : getnc_state->final_udv);
3254 6038 : if (!W_ERROR_IS_OK(werr)) {
3255 0 : return werr;
3256 : }
3257 :
3258 6038 : if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3259 2743 : werr = getncchanges_collect_objects(b_state, mem_ctx,
3260 : getnc_state, req10,
3261 : search_dn, extra_filter,
3262 : &search_res);
3263 : } else {
3264 3295 : werr = getncchanges_collect_objects_exop(b_state, mem_ctx,
3265 : getnc_state, req10,
3266 3295 : &r->out.ctr->ctr6,
3267 : search_dn, extra_filter,
3268 : &search_res);
3269 : }
3270 6038 : W_ERROR_NOT_OK_RETURN(werr);
3271 :
3272 : /* extract out the GUIDs list */
3273 6038 : getnc_state->num_records = search_res ? search_res->count : 0;
3274 6038 : getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records);
3275 6038 : W_ERROR_HAVE_NO_MEMORY(getnc_state->guids);
3276 :
3277 6038 : changes = talloc_array(getnc_state,
3278 : struct drsuapi_changed_objects,
3279 : getnc_state->num_records);
3280 6038 : W_ERROR_HAVE_NO_MEMORY(changes);
3281 :
3282 696486 : for (i=0; i<getnc_state->num_records; i++) {
3283 690448 : changes[i].dn = search_res->msgs[i]->dn;
3284 690448 : changes[i].guid = samdb_result_guid(search_res->msgs[i], "objectGUID");
3285 690448 : changes[i].usn = ldb_msg_find_attr_as_uint64(search_res->msgs[i], "uSNChanged", 0);
3286 :
3287 690448 : if (changes[i].usn > getnc_state->max_usn) {
3288 13299 : getnc_state->max_usn = changes[i].usn;
3289 : }
3290 :
3291 1361786 : if (req10->extended_op == DRSUAPI_EXOP_NONE &&
3292 671338 : GUID_equal(&changes[i].guid, &getnc_state->ncRoot_guid))
3293 : {
3294 727 : getnc_state->send_nc_root_first = true;
3295 : }
3296 : }
3297 :
3298 6038 : if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3299 2743 : getnc_state->is_get_anc =
3300 2743 : ((req10->replica_flags & DRSUAPI_DRS_GET_ANC) != 0);
3301 2743 : if (getnc_state->is_get_anc
3302 667 : && lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
3303 : NULL,
3304 : "drs",
3305 : "broken_samba_4.5_get_anc_emulation",
3306 : false)) {
3307 8 : getnc_state->broken_samba_4_5_get_anc_emulation = true;
3308 : }
3309 2743 : if (lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
3310 : NULL,
3311 : "drs",
3312 : "get_tgt_support",
3313 : true)) {
3314 2732 : getnc_state->is_get_tgt =
3315 2732 : ((req10->more_flags & DRSUAPI_DRS_GET_TGT) != 0);
3316 : }
3317 : }
3318 :
3319 : /* RID_ALLOC returns 3 objects in a fixed order */
3320 6038 : if (req10->extended_op == DRSUAPI_EXOP_FSMO_RID_ALLOC) {
3321 : /* Do nothing */
3322 6001 : } else if (getnc_state->broken_samba_4_5_get_anc_emulation) {
3323 8 : TYPESAFE_QSORT(changes,
3324 : getnc_state->num_records,
3325 : site_res_cmp_anc_order);
3326 : } else {
3327 5993 : TYPESAFE_QSORT(changes,
3328 : getnc_state->num_records,
3329 : site_res_cmp_usn_order);
3330 : }
3331 :
3332 696486 : for (i=0; i < getnc_state->num_records; i++) {
3333 690448 : getnc_state->guids[i] = changes[i].guid;
3334 690448 : if (GUID_all_zero(&getnc_state->guids[i])) {
3335 0 : DEBUG(2,("getncchanges: bad objectGUID from %s\n",
3336 : ldb_dn_get_linearized(search_res->msgs[i]->dn)));
3337 0 : return WERR_DS_DRA_INTERNAL_ERROR;
3338 : }
3339 : }
3340 :
3341 6038 : getnc_state->final_hwm.tmp_highest_usn = getnc_state->max_usn;
3342 6038 : getnc_state->final_hwm.reserved_usn = 0;
3343 6038 : getnc_state->final_hwm.highest_usn = getnc_state->max_usn;
3344 :
3345 6038 : talloc_free(search_res);
3346 6038 : talloc_free(changes);
3347 :
3348 : /*
3349 : * when using GET_ANC or GET_TGT, cache the objects that have
3350 : * been already sent, to avoid sending them multiple times
3351 : */
3352 6038 : if (getnc_state->is_get_anc || getnc_state->is_get_tgt) {
3353 699 : DEBUG(3,("Using object cache, GET_ANC %u, GET_TGT %u\n",
3354 : getnc_state->is_get_anc,
3355 : getnc_state->is_get_tgt));
3356 :
3357 699 : getnc_state->obj_cache = db_open_rbt(getnc_state);
3358 699 : if (getnc_state->obj_cache == NULL) {
3359 0 : return WERR_NOT_ENOUGH_MEMORY;
3360 : }
3361 : }
3362 : }
3363 :
3364 8182 : if (req10->uptodateness_vector) {
3365 : /* make sure its sorted */
3366 2415 : TYPESAFE_QSORT(req10->uptodateness_vector->cursors,
3367 : req10->uptodateness_vector->count,
3368 : drsuapi_DsReplicaCursor_compare);
3369 : }
3370 :
3371 : /* Prefix mapping */
3372 8182 : schema = dsdb_get_schema(sam_ctx, mem_ctx);
3373 8182 : if (!schema) {
3374 0 : DEBUG(0,("No schema in sam_ctx\n"));
3375 0 : return WERR_DS_DRA_INTERNAL_ERROR;
3376 : }
3377 :
3378 8182 : r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
3379 8182 : if (r->out.ctr->ctr6.naming_context == NULL) {
3380 0 : return WERR_NOT_ENOUGH_MEMORY;
3381 : }
3382 :
3383 : /*
3384 : * Match Windows and echo back the original values from the request, even if
3385 : * they say DummyDN for the string NC
3386 : */
3387 8182 : *r->out.ctr->ctr6.naming_context = *untrusted_ncRoot;
3388 :
3389 : /* find the SID if there is one */
3390 8182 : dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
3391 :
3392 : /* Set GUID */
3393 8182 : r->out.ctr->ctr6.naming_context->guid = getnc_state->ncRoot_guid;
3394 :
3395 8182 : dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
3396 8182 : r->out.ctr->ctr6.mapping_ctr = *ctr;
3397 :
3398 8182 : r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
3399 8182 : r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
3400 :
3401 8182 : r->out.ctr->ctr6.old_highwatermark = req10->highwatermark;
3402 8182 : r->out.ctr->ctr6.new_highwatermark = req10->highwatermark;
3403 :
3404 : /*
3405 : * If the client has already set GET_TGT then we know they can handle
3406 : * receiving the linked attributes interleaved with the source objects
3407 : */
3408 8182 : if (getnc_state->is_get_tgt) {
3409 423 : repl_chunk->immediate_link_sync = true;
3410 : }
3411 :
3412 8182 : if (req10->partial_attribute_set != NULL) {
3413 0 : struct dsdb_syntax_ctx syntax_ctx;
3414 3683 : uint32_t j = 0;
3415 :
3416 3683 : dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
3417 3683 : syntax_ctx.pfm_remote = pfm_remote;
3418 :
3419 3683 : local_pas = talloc_array(b_state, uint32_t, req10->partial_attribute_set->num_attids);
3420 :
3421 773730 : for (j = 0; j < req10->partial_attribute_set->num_attids; j++) {
3422 770047 : getncchanges_attid_remote_to_local(schema,
3423 : &syntax_ctx,
3424 770047 : req10->partial_attribute_set->attids[j],
3425 770047 : (enum drsuapi_DsAttributeId *)&local_pas[j],
3426 : NULL);
3427 : }
3428 :
3429 3683 : TYPESAFE_QSORT(local_pas,
3430 : req10->partial_attribute_set->num_attids,
3431 : uint32_t_ptr_cmp);
3432 : }
3433 :
3434 : /*
3435 : * If we have the NC root in this replication, send it
3436 : * first regardless. However, don't bump the USN now,
3437 : * treat it as if it was sent early due to GET_ANC
3438 : *
3439 : * This is triggered for each call, so every page of responses
3440 : * gets the NC root as the first object, up to the point where
3441 : * it naturally occurs in the replication.
3442 : */
3443 :
3444 8182 : if (getnc_state->send_nc_root_first) {
3445 2020 : struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
3446 :
3447 2020 : werr = getncchanges_add_ancestors(&getnc_state->ncRoot_guid,
3448 : NULL, mem_ctx,
3449 : sam_ctx, getnc_state,
3450 : schema, &session_key,
3451 : req10, local_pas,
3452 : machine_dn, &new_objs);
3453 :
3454 2020 : if (!W_ERROR_IS_OK(werr)) {
3455 0 : return werr;
3456 : }
3457 :
3458 2020 : getncchanges_chunk_add_objects(repl_chunk, new_objs);
3459 :
3460 2020 : DEBUG(8,(__location__ ": replicating NC root %s\n",
3461 : ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
3462 : }
3463 :
3464 : /*
3465 : * Check in case we're still processing the links from an object in the
3466 : * previous chunk. We want to send the links (and any targets needed)
3467 : * before moving on to the next object.
3468 : */
3469 8182 : if (getnc_state->is_get_tgt) {
3470 423 : werr = getncchanges_chunk_add_la_targets(repl_chunk,
3471 : getnc_state,
3472 : getnc_state->la_idx,
3473 : mem_ctx, sam_ctx,
3474 : schema, &session_key,
3475 : req10, local_pas,
3476 : machine_dn);
3477 :
3478 423 : if (!W_ERROR_IS_OK(werr)) {
3479 0 : return werr;
3480 : }
3481 : }
3482 :
3483 8182 : for (i=getnc_state->num_processed;
3484 659686 : i<getnc_state->num_records &&
3485 653460 : !getncchanges_chunk_is_full(repl_chunk, getnc_state);
3486 651504 : i++) {
3487 651504 : struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
3488 0 : struct ldb_message *msg;
3489 0 : static const char * const msg_attrs[] = {
3490 : "*",
3491 : "nTSecurityDescriptor",
3492 : "parentGUID",
3493 : "replPropertyMetaData",
3494 : DSDB_SECRET_ATTRIBUTES,
3495 : NULL };
3496 0 : struct ldb_result *msg_res;
3497 0 : struct ldb_dn *msg_dn;
3498 651504 : bool obj_already_sent = false;
3499 651504 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3500 0 : uint32_t old_la_index;
3501 :
3502 : /*
3503 : * Once we get to the 'natural' place to send the NC
3504 : * root, stop sending it at the front of each reply
3505 : * and make sure to suppress sending it now
3506 : *
3507 : * We don't just 'continue' here as we must send links
3508 : * and unlike Windows we want to update the
3509 : * tmp_highest_usn
3510 : */
3511 :
3512 1087545 : if (getnc_state->send_nc_root_first &&
3513 436041 : GUID_equal(&getnc_state->guids[i], &getnc_state->ncRoot_guid))
3514 : {
3515 711 : getnc_state->send_nc_root_first = false;
3516 711 : obj_already_sent = true;
3517 : }
3518 :
3519 651504 : msg_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
3520 651504 : GUID_string(tmp_ctx, &getnc_state->guids[i]));
3521 651504 : W_ERROR_HAVE_NO_MEMORY(msg_dn);
3522 :
3523 : /*
3524 : * by re-searching here we avoid having a lot of full
3525 : * records in memory between calls to getncchanges.
3526 : *
3527 : * We expect that we may get some objects that vanish
3528 : * (tombstone expunge) between the first and second
3529 : * check.
3530 : */
3531 651504 : ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx, &msg_res,
3532 : msg_dn,
3533 : LDB_SCOPE_BASE, msg_attrs, NULL);
3534 651504 : if (ret != LDB_SUCCESS) {
3535 0 : if (ret != LDB_ERR_NO_SUCH_OBJECT) {
3536 0 : DEBUG(1,("getncchanges: failed to fetch DN %s - %s\n",
3537 : ldb_dn_get_extended_linearized(tmp_ctx, msg_dn, 1),
3538 : ldb_errstring(sam_ctx)));
3539 : }
3540 0 : TALLOC_FREE(tmp_ctx);
3541 0 : continue;
3542 : }
3543 :
3544 651504 : if (msg_res->count == 0) {
3545 0 : DEBUG(1,("getncchanges: got LDB_SUCCESS but failed"
3546 : "to get any results in fetch of DN "
3547 : "%s (race with tombstone expunge?)\n",
3548 : ldb_dn_get_extended_linearized(tmp_ctx,
3549 : msg_dn, 1)));
3550 0 : TALLOC_FREE(tmp_ctx);
3551 0 : continue;
3552 : }
3553 :
3554 651504 : msg = msg_res->msgs[0];
3555 :
3556 : /*
3557 : * Check if we've already sent the object as an ancestor of
3558 : * another object. If so, we don't need to send it again
3559 : */
3560 651504 : if (getnc_state->obj_cache != NULL) {
3561 456382 : werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
3562 456382 : &getnc_state->guids[i]);
3563 456382 : if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
3564 3096 : obj_already_sent = true;
3565 : }
3566 : }
3567 :
3568 651504 : if (!obj_already_sent) {
3569 0 : bool max_wait_reached;
3570 :
3571 648190 : max_wait_reached = getncchanges_chunk_timed_out(repl_chunk);
3572 :
3573 : /*
3574 : * Construct an object, ready to send (this will include
3575 : * the object's ancestors as well, if needed)
3576 : */
3577 648190 : werr = getncchanges_get_obj_to_send(msg, mem_ctx, sam_ctx,
3578 : getnc_state, schema,
3579 : &session_key, req10,
3580 : max_wait_reached,
3581 : local_pas, machine_dn,
3582 648190 : &getnc_state->guids[i],
3583 : &new_objs);
3584 648190 : if (!W_ERROR_IS_OK(werr)) {
3585 0 : return werr;
3586 : }
3587 : }
3588 :
3589 651504 : old_la_index = getnc_state->la_count;
3590 :
3591 : /*
3592 : * We've reached the USN where this object naturally occurs.
3593 : * Regardless of whether we've already sent the object (as an
3594 : * ancestor), we add its links and update the HWM at this point
3595 : */
3596 651504 : werr = get_nc_changes_add_links(sam_ctx, getnc_state,
3597 651504 : getnc_state->is_schema_nc,
3598 : schema, getnc_state->min_usn,
3599 : req10->replica_flags,
3600 : msg,
3601 : &getnc_state->la_list,
3602 : &getnc_state->la_count,
3603 : req10->uptodateness_vector);
3604 651504 : if (!W_ERROR_IS_OK(werr)) {
3605 0 : return werr;
3606 : }
3607 :
3608 651504 : dcesrv_drsuapi_update_highwatermark(msg,
3609 : getnc_state->max_usn,
3610 651504 : &r->out.ctr->ctr6.new_highwatermark);
3611 :
3612 651504 : if (new_objs != NULL) {
3613 :
3614 : /*
3615 : * Add the object (and, if GET_ANC, any parents it may
3616 : * have) into the current chunk of replication data
3617 : */
3618 572196 : getncchanges_chunk_add_objects(repl_chunk, new_objs);
3619 :
3620 572196 : talloc_free(getnc_state->last_dn);
3621 : /*
3622 : * talloc_steal() as we still need msg->dn to
3623 : * be a valid pointer for the log on the next
3624 : * line.
3625 : *
3626 : * msg only remains in scope for the next 25
3627 : * lines or so anyway.
3628 : */
3629 572196 : getnc_state->last_dn = talloc_steal(getnc_state, msg->dn);
3630 : }
3631 :
3632 651504 : DEBUG(8,(__location__ ": %s object %s new tmp_highest_usn=%" PRIu64 "\n",
3633 : new_objs ? "replicating" : "skipping send of",
3634 : ldb_dn_get_linearized(msg->dn),
3635 : r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn));
3636 :
3637 651504 : getnc_state->total_links += (getnc_state->la_count - old_la_index);
3638 :
3639 : /*
3640 : * If the GET_TGT flag was set, check any new links added to
3641 : * make sure the client knows about the link target object
3642 : */
3643 651504 : if (getnc_state->is_get_tgt) {
3644 110086 : werr = getncchanges_chunk_add_la_targets(repl_chunk,
3645 : getnc_state,
3646 : old_la_index,
3647 : mem_ctx, sam_ctx,
3648 : schema, &session_key,
3649 : req10, local_pas,
3650 : machine_dn);
3651 :
3652 110086 : if (!W_ERROR_IS_OK(werr)) {
3653 0 : return werr;
3654 : }
3655 : }
3656 :
3657 651504 : TALLOC_FREE(tmp_ctx);
3658 : }
3659 :
3660 : /* copy the constructed object list into the response message */
3661 8182 : r->out.ctr->ctr6.object_count = repl_chunk->object_count;
3662 8182 : r->out.ctr->ctr6.first_object = repl_chunk->object_list;
3663 :
3664 8182 : getnc_state->num_processed = i;
3665 :
3666 8182 : if (i < getnc_state->num_records) {
3667 1956 : r->out.ctr->ctr6.more_data = true;
3668 : }
3669 :
3670 : /* the client can us to call UpdateRefs on its behalf to
3671 : re-establish monitoring of the NC */
3672 8182 : if ((req10->replica_flags & (DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_REF_GCSPN)) &&
3673 1273 : !GUID_all_zero(&req10->destination_dsa_guid)) {
3674 0 : struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
3675 1273 : DEBUG(3,("UpdateRefs on getncchanges for %s\n",
3676 : GUID_string(mem_ctx, &req10->destination_dsa_guid)));
3677 :
3678 : /*
3679 : * We pass the pre-validation NC root here as
3680 : * drsuapi_UpdateRefs() has to check its own input
3681 : * values due to being called from
3682 : * dcesrv_drsuapi_DsReplicaUpdateRefs()
3683 : */
3684 :
3685 1273 : ureq.naming_context = untrusted_ncRoot;
3686 2546 : ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(sam_ctx, mem_ctx,
3687 1273 : &req10->destination_dsa_guid);
3688 1273 : if (!ureq.dest_dsa_dns_name) {
3689 0 : return WERR_NOT_ENOUGH_MEMORY;
3690 : }
3691 1273 : ureq.dest_dsa_guid = req10->destination_dsa_guid;
3692 1273 : ureq.options = DRSUAPI_DRS_ADD_REF |
3693 : DRSUAPI_DRS_ASYNC_OP |
3694 : DRSUAPI_DRS_GETCHG_CHECK;
3695 :
3696 : /* we also need to pass through the
3697 : DRSUAPI_DRS_REF_GCSPN bit so that repsTo gets flagged
3698 : to send notifies using the GC SPN */
3699 1273 : ureq.options |= (req10->replica_flags & DRSUAPI_DRS_REF_GCSPN);
3700 :
3701 1273 : werr = drsuapi_UpdateRefs(imsg_ctx,
3702 : dce_call->event_ctx,
3703 : b_state,
3704 : mem_ctx,
3705 : &ureq);
3706 1273 : if (!W_ERROR_IS_OK(werr)) {
3707 0 : DEBUG(0,(__location__ ": Failed UpdateRefs on %s for %s in DsGetNCChanges - %s\n",
3708 : drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3709 : ureq.dest_dsa_dns_name,
3710 : win_errstr(werr)));
3711 : }
3712 : }
3713 :
3714 : /*
3715 : * Work out how many links we can send in this chunk. The default is to
3716 : * send all the links last, but there is a config option to send them
3717 : * immediately, in the same chunk as their source object
3718 : */
3719 8182 : if (!r->out.ctr->ctr6.more_data || repl_chunk->immediate_link_sync) {
3720 6996 : link_count = getncchanges_chunk_links_pending(repl_chunk,
3721 : getnc_state);
3722 6996 : link_count = MIN(link_count,
3723 : getncchanges_chunk_max_links(repl_chunk));
3724 : }
3725 :
3726 : /* If we've got linked attributes to send, add them now */
3727 8182 : if (link_count > 0) {
3728 0 : struct la_for_sorting *la_sorted;
3729 :
3730 : /*
3731 : * Grab a chunk of linked attributes off the list and put them
3732 : * in sorted array, ready to send
3733 : */
3734 2225 : werr = getncchanges_get_sorted_array(&getnc_state->la_list[getnc_state->la_idx],
3735 : link_count,
3736 : sam_ctx, getnc_state,
3737 : schema,
3738 : &la_sorted);
3739 2225 : if (!W_ERROR_IS_OK(werr)) {
3740 0 : return werr;
3741 : }
3742 :
3743 2225 : r->out.ctr->ctr6.linked_attributes_count = link_count;
3744 2225 : r->out.ctr->ctr6.linked_attributes = talloc_array(r->out.ctr, struct drsuapi_DsReplicaLinkedAttribute, link_count);
3745 2225 : if (r->out.ctr->ctr6.linked_attributes == NULL) {
3746 0 : DEBUG(0, ("Out of memory allocating %u linked attributes for output\n", link_count));
3747 0 : return WERR_NOT_ENOUGH_MEMORY;
3748 : }
3749 :
3750 23841 : for (k = 0; k < link_count; k++) {
3751 21616 : r->out.ctr->ctr6.linked_attributes[k] = *la_sorted[k].link;
3752 : }
3753 :
3754 2225 : getnc_state->la_idx += link_count;
3755 2225 : getnc_state->links_given += link_count;
3756 :
3757 2225 : if (getnc_state->la_idx < getnc_state->la_count) {
3758 252 : r->out.ctr->ctr6.more_data = true;
3759 : } else {
3760 :
3761 : /*
3762 : * We've now sent all the links seen so far, so we can
3763 : * reset la_list back to an empty list again. Note that
3764 : * the steal means the linked attribute memory gets
3765 : * freed after this RPC message is sent on the wire.
3766 : */
3767 1973 : talloc_steal(mem_ctx, getnc_state->la_list);
3768 1973 : getnc_state->la_list = NULL;
3769 1973 : getnc_state->la_idx = 0;
3770 1973 : getnc_state->la_count = 0;
3771 : }
3772 :
3773 2225 : TALLOC_FREE(la_sorted);
3774 : }
3775 :
3776 8182 : if (req10->replica_flags & DRSUAPI_DRS_GET_NC_SIZE) {
3777 : /*
3778 : * TODO: This implementation is wrong
3779 : * we should find out the total number of
3780 : * objects and links in the whole naming context
3781 : * at the start of the cycle and return these
3782 : * values in each message.
3783 : *
3784 : * For now we keep our current strategy and return
3785 : * the number of objects for this cycle and the number
3786 : * of links we found so far during the cycle.
3787 : */
3788 1530 : r->out.ctr->ctr6.nc_object_count = getnc_state->num_records;
3789 1530 : r->out.ctr->ctr6.nc_linked_attributes_count = getnc_state->total_links;
3790 : }
3791 :
3792 8182 : if (req10->extended_op != DRSUAPI_EXOP_NONE) {
3793 3295 : r->out.ctr->ctr6.uptodateness_vector = NULL;
3794 3295 : r->out.ctr->ctr6.nc_object_count = 0;
3795 3295 : ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
3796 4887 : } else if (!r->out.ctr->ctr6.more_data) {
3797 :
3798 : /* this is the last response in the replication cycle */
3799 2697 : r->out.ctr->ctr6.new_highwatermark = getnc_state->final_hwm;
3800 2697 : r->out.ctr->ctr6.uptodateness_vector = talloc_move(mem_ctx,
3801 : &getnc_state->final_udv);
3802 :
3803 : /*
3804 : * Free the state info stored for the replication cycle. Note
3805 : * that the RPC message we're sending contains links stored in
3806 : * getnc_state. mem_ctx is local to this RPC call, so the memory
3807 : * will get freed after the RPC message is sent on the wire.
3808 : *
3809 : * We must not do this for an EXOP, as that should not
3810 : * end the replication state, which is why that is
3811 : * checked first above.
3812 : */
3813 2697 : talloc_steal(mem_ctx, getnc_state);
3814 2697 : b_state->getncchanges_full_repl_state = NULL;
3815 : } else {
3816 2190 : ret = drsuapi_DsReplicaHighWaterMark_cmp(&r->out.ctr->ctr6.old_highwatermark,
3817 2190 : &r->out.ctr->ctr6.new_highwatermark);
3818 2190 : if (ret == 0) {
3819 : /*
3820 : * We need to make sure that we never return the
3821 : * same highwatermark within the same replication
3822 : * cycle more than once. Otherwise we cannot detect
3823 : * when the client uses an unexpected highwatermark.
3824 : *
3825 : * This is a HACK which is needed because our
3826 : * object ordering is wrong and set tmp_highest_usn
3827 : * to a value that is higher than what we already
3828 : * sent to the client (destination dsa).
3829 : */
3830 358 : r->out.ctr->ctr6.new_highwatermark.reserved_usn += 1;
3831 : }
3832 :
3833 2190 : getnc_state->last_hwm = r->out.ctr->ctr6.new_highwatermark;
3834 : }
3835 :
3836 8182 : TALLOC_FREE(repl_chunk);
3837 :
3838 8182 : DEBUG(r->out.ctr->ctr6.more_data?4:2,
3839 : ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %u/%u) %u links (done %u/%u (as %s))\n",
3840 : (unsigned long long)(req10->highwatermark.highest_usn+1),
3841 : req10->replica_flags,
3842 : drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3843 : r->out.ctr->ctr6.object_count,
3844 : i, r->out.ctr->ctr6.more_data?getnc_state->num_records:i,
3845 : r->out.ctr->ctr6.linked_attributes_count,
3846 : getnc_state->links_given, getnc_state->total_links,
3847 : dom_sid_string(mem_ctx, user_sid)));
3848 :
3849 : #if 0
3850 : if (!r->out.ctr->ctr6.more_data && req10->extended_op != DRSUAPI_EXOP_NONE) {
3851 : NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);
3852 : }
3853 : #endif
3854 :
3855 8182 : return WERR_OK;
3856 : }
3857 :
|