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