Line data Source code
1 : /*
2 : ldb database library
3 :
4 : Copyright (C) Simo Sorce 2004-2008
5 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 : Copyright (C) Andrew Tridgell 2005-2009
7 : Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 : Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
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 : /*
25 : * Name: ldb
26 : *
27 : * Component: ldb repl_meta_data module
28 : *
29 : * Description: - add a unique objectGUID onto every new record,
30 : * - handle whenCreated, whenChanged timestamps
31 : * - handle uSNCreated, uSNChanged numbers
32 : * - handle replPropertyMetaData attribute
33 : *
34 : * Author: Simo Sorce
35 : * Author: Stefan Metzmacher
36 : */
37 :
38 : #include "includes.h"
39 : #include "ldb_module.h"
40 : #include "dsdb/samdb/samdb.h"
41 : #include "dsdb/common/proto.h"
42 : #include "dsdb/common/util.h"
43 : #include "../libds/common/flags.h"
44 : #include "librpc/gen_ndr/irpc.h"
45 : #include "librpc/gen_ndr/ndr_misc.h"
46 : #include "librpc/gen_ndr/ndr_drsuapi.h"
47 : #include "librpc/gen_ndr/ndr_drsblobs.h"
48 : #include "param/param.h"
49 : #include "libcli/security/security.h"
50 : #include "lib/util/dlinklist.h"
51 : #include "dsdb/samdb/ldb_modules/util.h"
52 : #include "lib/util/tsort.h"
53 : #include "lib/util/binsearch.h"
54 :
55 : #undef strcasecmp
56 :
57 : #undef DBGC_CLASS
58 : #define DBGC_CLASS DBGC_DRS_REPL
59 :
60 : /* the RMD_VERSION for linked attributes starts from 1 */
61 : #define RMD_VERSION_INITIAL 1
62 :
63 : /*
64 : * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
65 : * Deleted Objects Container
66 : */
67 : static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
68 :
69 : struct replmd_private {
70 : TALLOC_CTX *la_ctx;
71 : struct la_group *la_list;
72 : struct nc_entry {
73 : struct nc_entry *prev, *next;
74 : struct ldb_dn *dn;
75 : uint64_t mod_usn;
76 : uint64_t mod_usn_urgent;
77 : } *ncs;
78 : struct ldb_dn *schema_dn;
79 : bool originating_updates;
80 : bool sorted_links;
81 : uint32_t total_links;
82 : uint32_t num_processed;
83 : bool recyclebin_enabled;
84 : bool recyclebin_state_known;
85 : };
86 :
87 : /*
88 : * groups link attributes together by source-object and attribute-ID,
89 : * to improve processing efficiency (i.e. for 'member' attribute, which
90 : * could have 100s or 1000s of links).
91 : * Note this grouping is best effort - the same source object could still
92 : * correspond to several la_groups (a lot depends on the order DRS sends
93 : * the links in). The groups currently don't span replication chunks (which
94 : * caps the size to ~1500 links by default).
95 : */
96 : struct la_group {
97 : struct la_group *next, *prev;
98 : struct la_entry *la_entries;
99 : };
100 :
101 : struct la_entry {
102 : struct la_entry *next, *prev;
103 : struct drsuapi_DsReplicaLinkedAttribute *la;
104 : uint32_t dsdb_repl_flags;
105 : };
106 :
107 : struct replmd_replicated_request {
108 : struct ldb_module *module;
109 : struct ldb_request *req;
110 :
111 : const struct dsdb_schema *schema;
112 : struct GUID our_invocation_id;
113 :
114 : /* the controls we pass down */
115 : struct ldb_control **controls;
116 :
117 : /*
118 : * Backlinks for the replmd_add() case (we want to create
119 : * backlinks after creating the user, but before the end of
120 : * the ADD request)
121 : */
122 : struct la_backlink *la_backlinks;
123 :
124 : /* details for the mode where we apply a bunch of inbound replication meessages */
125 : bool apply_mode;
126 : uint32_t index_current;
127 : struct dsdb_extended_replicated_objects *objs;
128 :
129 : struct ldb_message *search_msg;
130 : struct GUID local_parent_guid;
131 :
132 : uint64_t seq_num;
133 : bool is_urgent;
134 :
135 : bool isDeleted;
136 :
137 : bool fix_link_sid;
138 : };
139 :
140 : /*
141 : * the result of replmd_process_linked_attribute(): either there was no change
142 : * (update was ignored), a new link was added (either inactive or active), or
143 : * an existing link was modified (active/inactive status may have changed).
144 : */
145 : typedef enum {
146 : LINK_CHANGE_NONE,
147 : LINK_CHANGE_ADDED,
148 : LINK_CHANGE_MODIFIED,
149 : } replmd_link_changed;
150 :
151 : static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
152 : static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
153 : static int replmd_check_upgrade_links(struct ldb_context *ldb,
154 : struct parsed_dn *dns, uint32_t count,
155 : struct ldb_message_element *el,
156 : const char *ldap_oid);
157 : static int replmd_verify_link_target(struct replmd_replicated_request *ar,
158 : TALLOC_CTX *mem_ctx,
159 : struct la_entry *la_entry,
160 : struct ldb_dn *src_dn,
161 : const struct dsdb_attribute *attr);
162 : static int replmd_get_la_entry_source(struct ldb_module *module,
163 : struct la_entry *la_entry,
164 : TALLOC_CTX *mem_ctx,
165 : const struct dsdb_attribute **ret_attr,
166 : struct ldb_message **source_msg);
167 : static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
168 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
169 : uint64_t usn, uint64_t local_usn, NTTIME nttime,
170 : uint32_t version, bool deleted);
171 :
172 : static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
173 : struct ldb_context *ldb,
174 : struct ldb_dn *dn,
175 : const char *rdn_name,
176 : const struct ldb_val *rdn_value,
177 : struct GUID guid);
178 :
179 : enum urgent_situation {
180 : REPL_URGENT_ON_CREATE = 1,
181 : REPL_URGENT_ON_UPDATE = 2,
182 : REPL_URGENT_ON_DELETE = 4
183 : };
184 :
185 : enum deletion_state {
186 : OBJECT_NOT_DELETED=1,
187 : OBJECT_DELETED=2,
188 : OBJECT_RECYCLED=3,
189 : OBJECT_TOMBSTONE=4,
190 : OBJECT_REMOVED=5
191 : };
192 :
193 250579 : static bool replmd_recyclebin_enabled(struct ldb_module *module)
194 : {
195 250579 : bool enabled = false;
196 146 : struct replmd_private *replmd_private =
197 250579 : talloc_get_type_abort(ldb_module_get_private(module),
198 : struct replmd_private);
199 :
200 : /*
201 : * only lookup the recycle-bin state once per replication, then cache
202 : * the result. This can save us 1000s of DB searches
203 : */
204 250579 : if (!replmd_private->recyclebin_state_known) {
205 46120 : int ret = dsdb_recyclebin_enabled(module, &enabled);
206 46120 : if (ret != LDB_SUCCESS) {
207 1606 : return false;
208 : }
209 :
210 44514 : replmd_private->recyclebin_enabled = enabled;
211 44514 : replmd_private->recyclebin_state_known = true;
212 : }
213 :
214 248973 : return replmd_private->recyclebin_enabled;
215 : }
216 :
217 250579 : static void replmd_deletion_state(struct ldb_module *module,
218 : const struct ldb_message *msg,
219 : enum deletion_state *current_state,
220 : enum deletion_state *next_state)
221 : {
222 250579 : bool enabled = false;
223 :
224 250579 : if (msg == NULL) {
225 0 : *current_state = OBJECT_REMOVED;
226 0 : if (next_state != NULL) {
227 0 : *next_state = OBJECT_REMOVED;
228 : }
229 0 : return;
230 : }
231 :
232 250579 : enabled = replmd_recyclebin_enabled(module);
233 :
234 250579 : if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
235 151516 : if (!enabled) {
236 151516 : *current_state = OBJECT_TOMBSTONE;
237 151516 : if (next_state != NULL) {
238 151303 : *next_state = OBJECT_REMOVED;
239 : }
240 151516 : return;
241 : }
242 :
243 0 : if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
244 0 : *current_state = OBJECT_RECYCLED;
245 0 : if (next_state != NULL) {
246 0 : *next_state = OBJECT_REMOVED;
247 : }
248 0 : return;
249 : }
250 :
251 0 : *current_state = OBJECT_DELETED;
252 0 : if (next_state != NULL) {
253 0 : *next_state = OBJECT_RECYCLED;
254 : }
255 0 : return;
256 : }
257 :
258 99063 : *current_state = OBJECT_NOT_DELETED;
259 99063 : if (next_state == NULL) {
260 26261 : return;
261 : }
262 :
263 72802 : if (enabled) {
264 0 : *next_state = OBJECT_DELETED;
265 : } else {
266 72802 : *next_state = OBJECT_TOMBSTONE;
267 : }
268 : }
269 :
270 : static const struct {
271 : const char *update_name;
272 : enum urgent_situation repl_situation;
273 : } urgent_objects[] = {
274 : {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
275 : {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
276 : {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
277 : {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
278 : {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
279 : {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
280 : {NULL, 0}
281 : };
282 :
283 : /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
284 : static const char *urgent_attrs[] = {
285 : "lockoutTime",
286 : "pwdLastSet",
287 : "userAccountControl",
288 : NULL
289 : };
290 :
291 :
292 1057286 : static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
293 : enum urgent_situation situation)
294 : {
295 106751 : unsigned int i, j;
296 6376744 : for (i=0; urgent_objects[i].update_name; i++) {
297 :
298 5593354 : if ((situation & urgent_objects[i].repl_situation) == 0) {
299 1171288 : continue;
300 : }
301 :
302 14671895 : for (j=0; j<objectclass_el->num_values; j++) {
303 10523725 : const struct ldb_val *v = &objectclass_el->values[j];
304 10523725 : if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
305 224022 : return true;
306 : }
307 : }
308 : }
309 726513 : return false;
310 : }
311 :
312 638133 : static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
313 : {
314 638133 : if (ldb_attr_in_list(urgent_attrs, el->name)) {
315 32606 : return true;
316 : }
317 586705 : return false;
318 : }
319 :
320 : static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
321 :
322 : /*
323 : initialise the module
324 : allocate the private structure and build the list
325 : of partition DNs for use by replmd_notify()
326 : */
327 181589 : static int replmd_init(struct ldb_module *module)
328 : {
329 6078 : struct replmd_private *replmd_private;
330 181589 : struct ldb_context *ldb = ldb_module_get_ctx(module);
331 6078 : int ret;
332 :
333 181589 : replmd_private = talloc_zero(module, struct replmd_private);
334 181589 : if (replmd_private == NULL) {
335 0 : ldb_oom(ldb);
336 0 : return LDB_ERR_OPERATIONS_ERROR;
337 : }
338 :
339 181589 : ret = dsdb_check_samba_compatible_feature(module,
340 : SAMBA_SORTED_LINKS_FEATURE,
341 : &replmd_private->sorted_links);
342 181589 : if (ret != LDB_SUCCESS) {
343 0 : talloc_free(replmd_private);
344 0 : return ret;
345 : }
346 :
347 181589 : replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
348 181589 : ldb_module_set_private(module, replmd_private);
349 181589 : return ldb_next_init(module);
350 : }
351 :
352 : /*
353 : cleanup our per-transaction contexts
354 : */
355 700339 : static void replmd_txn_cleanup(struct replmd_private *replmd_private)
356 : {
357 700339 : talloc_free(replmd_private->la_ctx);
358 700339 : replmd_private->la_list = NULL;
359 700339 : replmd_private->la_ctx = NULL;
360 700339 : replmd_private->recyclebin_state_known = false;
361 698147 : }
362 :
363 :
364 : struct la_backlink {
365 : struct la_backlink *next, *prev;
366 : const char *attr_name;
367 : struct ldb_dn *forward_dn;
368 : struct GUID target_guid;
369 : bool active;
370 : bool bl_maybe_invisible;
371 : bool bl_invisible;
372 : };
373 :
374 : /*
375 : a ldb_modify request operating on modules below the
376 : current module
377 : */
378 3656 : static int linked_attr_modify(struct ldb_module *module,
379 : const struct ldb_message *message,
380 : struct ldb_request *parent)
381 : {
382 0 : struct ldb_request *mod_req;
383 0 : int ret;
384 3656 : struct ldb_context *ldb = ldb_module_get_ctx(module);
385 3656 : TALLOC_CTX *tmp_ctx = talloc_new(module);
386 0 : struct ldb_result *res;
387 :
388 3656 : res = talloc_zero(tmp_ctx, struct ldb_result);
389 3656 : if (!res) {
390 0 : talloc_free(tmp_ctx);
391 0 : return ldb_oom(ldb_module_get_ctx(module));
392 : }
393 :
394 3656 : ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
395 : message,
396 : NULL,
397 : res,
398 : ldb_modify_default_callback,
399 : parent);
400 3656 : LDB_REQ_SET_LOCATION(mod_req);
401 3656 : if (ret != LDB_SUCCESS) {
402 0 : talloc_free(tmp_ctx);
403 0 : return ret;
404 : }
405 :
406 3656 : ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
407 : false, NULL);
408 3656 : if (ret != LDB_SUCCESS) {
409 0 : return ret;
410 : }
411 :
412 : /* Run the new request */
413 3656 : ret = ldb_next_request(module, mod_req);
414 :
415 3656 : if (ret == LDB_SUCCESS) {
416 3656 : ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
417 : }
418 :
419 3656 : talloc_free(tmp_ctx);
420 3656 : return ret;
421 : }
422 :
423 76390 : static int replmd_backlink_invisible(struct ldb_module *module,
424 : struct ldb_message *msg,
425 : struct la_backlink *bl)
426 : {
427 76390 : struct ldb_context *ldb = ldb_module_get_ctx(module);
428 76390 : const struct dsdb_schema *schema = NULL;
429 76390 : TALLOC_CTX *frame = NULL;
430 76390 : struct ldb_message_element *oc_element = NULL;
431 76390 : const char **allowed_attrs = NULL;
432 1600 : bool bl_allowed;
433 :
434 76390 : if (!bl->active || !bl->bl_maybe_invisible || bl->bl_invisible) {
435 73277 : return LDB_SUCCESS;
436 : }
437 :
438 1513 : schema = dsdb_get_schema(ldb, NULL);
439 1513 : if (schema == NULL) {
440 0 : return ldb_module_operr(module);
441 : }
442 :
443 1513 : oc_element = ldb_msg_find_element(msg, "objectClass");
444 1513 : if (oc_element == NULL) {
445 0 : return ldb_module_operr(module);
446 : }
447 :
448 1513 : frame = talloc_stackframe();
449 :
450 1513 : allowed_attrs = dsdb_full_attribute_list(frame,
451 : schema,
452 : oc_element,
453 : DSDB_SCHEMA_ALL);
454 1513 : if (allowed_attrs == NULL) {
455 0 : TALLOC_FREE(frame);
456 0 : return ldb_module_oom(module);
457 : }
458 :
459 1513 : bl_allowed = str_list_check(allowed_attrs, bl->attr_name);
460 1513 : if (!bl_allowed) {
461 16 : bl->bl_maybe_invisible = false;
462 16 : bl->bl_invisible = true;
463 : }
464 :
465 1513 : TALLOC_FREE(frame);
466 1513 : return LDB_SUCCESS;
467 : }
468 :
469 : /*
470 : process a backlinks we accumulated during a transaction, adding and
471 : deleting the backlinks from the target objects
472 : */
473 76428 : static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
474 : {
475 1602 : struct ldb_dn *target_dn, *source_dn;
476 76428 : struct ldb_message *old_msg = NULL;
477 76428 : const char * const empty_attrs[] = { NULL };
478 76428 : const char * const oc_attrs[] = { "objectClass", NULL };
479 76428 : const char * const *attrs = NULL;
480 76428 : uint32_t rmd_flags = 0;
481 1602 : int ret;
482 76428 : struct ldb_context *ldb = ldb_module_get_ctx(module);
483 1602 : struct ldb_message *msg;
484 76428 : TALLOC_CTX *frame = talloc_stackframe();
485 1602 : char *dn_string;
486 :
487 76428 : if (bl->active && bl->bl_maybe_invisible) {
488 1513 : attrs = oc_attrs;
489 : } else {
490 74915 : attrs = empty_attrs;
491 : }
492 :
493 : /*
494 : - find DN of target
495 : - find DN of source
496 : - construct ldb_message
497 : - either an add or a delete
498 : */
499 78030 : ret = dsdb_module_obj_by_guid(module,
500 : frame,
501 : &old_msg,
502 76428 : &bl->target_guid,
503 : attrs,
504 : parent);
505 76428 : if (ret != LDB_SUCCESS) {
506 2 : struct GUID_txt_buf guid_str;
507 38 : DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
508 : GUID_buf_string(&bl->target_guid, &guid_str));
509 38 : DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
510 38 : talloc_free(frame);
511 38 : return LDB_SUCCESS;
512 : }
513 76390 : target_dn = old_msg->dn;
514 :
515 76390 : ret = replmd_backlink_invisible(module, old_msg, bl);
516 76390 : if (ret != LDB_SUCCESS) {
517 0 : talloc_free(frame);
518 0 : return ret;
519 : }
520 :
521 76390 : if (bl->active && bl->bl_invisible) {
522 16 : rmd_flags |= DSDB_RMD_FLAG_HIDDEN_BL;
523 : }
524 :
525 76390 : msg = ldb_msg_new(frame);
526 76390 : if (msg == NULL) {
527 0 : ldb_module_oom(module);
528 0 : talloc_free(frame);
529 0 : return LDB_ERR_OPERATIONS_ERROR;
530 : }
531 :
532 76390 : source_dn = ldb_dn_copy(frame, bl->forward_dn);
533 76390 : if (!source_dn) {
534 0 : ldb_module_oom(module);
535 0 : talloc_free(frame);
536 0 : return LDB_ERR_OPERATIONS_ERROR;
537 : } else {
538 : /* Filter down to the attributes we want in the backlink */
539 76390 : const char *accept[] = { "GUID", "SID", NULL };
540 76390 : ldb_dn_extended_filter(source_dn, accept);
541 : }
542 :
543 76390 : if (rmd_flags != 0) {
544 16 : const char *flags_string = NULL;
545 0 : struct ldb_val flagsv;
546 :
547 16 : flags_string = talloc_asprintf(frame, "%u", rmd_flags);
548 16 : if (flags_string == NULL) {
549 0 : talloc_free(frame);
550 0 : return ldb_module_oom(module);
551 : }
552 :
553 16 : flagsv = data_blob_string_const(flags_string);
554 :
555 16 : ret = ldb_dn_set_extended_component(source_dn, "RMD_FLAGS", &flagsv);
556 16 : if (ret != LDB_SUCCESS) {
557 0 : talloc_free(frame);
558 0 : return ret;
559 : }
560 : }
561 :
562 : /* construct a ldb_message for adding/deleting the backlink */
563 76390 : msg->dn = target_dn;
564 76390 : dn_string = ldb_dn_get_extended_linearized(frame, source_dn, 1);
565 76390 : if (!dn_string) {
566 0 : ldb_module_oom(module);
567 0 : talloc_free(frame);
568 0 : return LDB_ERR_OPERATIONS_ERROR;
569 : }
570 76390 : ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
571 76390 : if (ret != LDB_SUCCESS) {
572 0 : talloc_free(frame);
573 0 : return ret;
574 : }
575 76390 : msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
576 :
577 : /* a backlink should never be single valued. Unfortunately the
578 : exchange schema has a attribute
579 : msExchBridgeheadedLocalConnectorsDNBL which is single
580 : valued and a backlink. We need to cope with that by
581 : ignoring the single value flag */
582 76390 : msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
583 :
584 76390 : ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
585 76390 : if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
586 : /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
587 : cope with possible corruption where the backlink has
588 : already been removed */
589 963 : DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
590 : ldb_dn_get_linearized(target_dn),
591 : ldb_dn_get_linearized(source_dn),
592 : ldb_errstring(ldb)));
593 936 : ret = LDB_SUCCESS;
594 75427 : } else if (ret != LDB_SUCCESS) {
595 0 : ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
596 0 : bl->active?"add":"remove",
597 : ldb_dn_get_linearized(source_dn),
598 : ldb_dn_get_linearized(target_dn),
599 : ldb_errstring(ldb));
600 0 : talloc_free(frame);
601 0 : return ret;
602 : }
603 76390 : talloc_free(frame);
604 76390 : return ret;
605 : }
606 :
607 : /*
608 : add a backlink to the list of backlinks to add/delete in the prepare
609 : commit
610 :
611 : forward_dn is stolen onto the defereed context
612 : */
613 13725 : static int replmd_defer_add_backlink(struct ldb_module *module,
614 : struct replmd_private *replmd_private,
615 : const struct dsdb_schema *schema,
616 : struct replmd_replicated_request *ac,
617 : struct ldb_dn *forward_dn,
618 : struct GUID *target_guid, bool active,
619 : const struct dsdb_attribute *schema_attr,
620 : struct ldb_request *parent)
621 : {
622 1627 : const struct dsdb_attribute *target_attr;
623 1627 : struct la_backlink *bl;
624 :
625 13725 : bl = talloc(ac, struct la_backlink);
626 13725 : if (bl == NULL) {
627 0 : ldb_module_oom(module);
628 0 : return LDB_ERR_OPERATIONS_ERROR;
629 : }
630 :
631 13725 : target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
632 13725 : if (!target_attr) {
633 : /*
634 : * windows 2003 has a broken schema where the
635 : * definition of msDS-IsDomainFor is missing (which is
636 : * supposed to be the backlink of the
637 : * msDS-HasDomainNCs attribute
638 : */
639 1716 : return LDB_SUCCESS;
640 : }
641 :
642 11791 : bl->attr_name = target_attr->lDAPDisplayName;
643 11791 : bl->forward_dn = talloc_steal(bl, forward_dn);
644 11791 : bl->target_guid = *target_guid;
645 11791 : bl->active = active;
646 11791 : bl->bl_maybe_invisible = target_attr->bl_maybe_invisible;
647 11791 : bl->bl_invisible = false;
648 :
649 11791 : DLIST_ADD(ac->la_backlinks, bl);
650 :
651 10382 : return LDB_SUCCESS;
652 : }
653 :
654 : /*
655 : add a backlink to the list of backlinks to add/delete in the prepare
656 : commit
657 : */
658 72349 : static int replmd_add_backlink(struct ldb_module *module,
659 : struct replmd_private *replmd_private,
660 : const struct dsdb_schema *schema,
661 : struct ldb_dn *forward_dn,
662 : struct GUID *target_guid, bool active,
663 : const struct dsdb_attribute *schema_attr,
664 : struct ldb_request *parent)
665 : {
666 252 : const struct dsdb_attribute *target_attr;
667 252 : struct la_backlink bl;
668 252 : int ret;
669 :
670 72349 : target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
671 72349 : if (!target_attr) {
672 : /*
673 : * windows 2003 has a broken schema where the
674 : * definition of msDS-IsDomainFor is missing (which is
675 : * supposed to be the backlink of the
676 : * msDS-HasDomainNCs attribute
677 : */
678 7651 : return LDB_SUCCESS;
679 : }
680 :
681 64639 : bl.attr_name = target_attr->lDAPDisplayName;
682 64639 : bl.forward_dn = forward_dn;
683 64639 : bl.target_guid = *target_guid;
684 64639 : bl.active = active;
685 64639 : bl.bl_maybe_invisible = target_attr->bl_maybe_invisible;
686 64639 : bl.bl_invisible = false;
687 :
688 64639 : ret = replmd_process_backlink(module, &bl, parent);
689 64639 : return ret;
690 : }
691 :
692 :
693 : /*
694 : * Callback for most write operations in this module:
695 : *
696 : * notify the repl task that a object has changed. The notifies are
697 : * gathered up in the replmd_private structure then written to the
698 : * @REPLCHANGED object in each partition during the prepare_commit
699 : */
700 1542749 : static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
701 : {
702 107002 : int ret;
703 107002 : struct replmd_replicated_request *ac =
704 1542749 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
705 107002 : struct replmd_private *replmd_private =
706 1542749 : talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
707 107002 : struct nc_entry *modified_partition;
708 107002 : struct ldb_control *partition_ctrl;
709 107002 : const struct dsdb_control_current_partition *partition;
710 :
711 107002 : struct ldb_control **controls;
712 :
713 1542749 : partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
714 :
715 1542749 : controls = ares->controls;
716 1542749 : if (ldb_request_get_control(ac->req,
717 : DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
718 : /*
719 : * Remove the current partition control from what we pass up
720 : * the chain if it hasn't been requested manually.
721 : */
722 1542749 : controls = ldb_controls_except_specified(ares->controls, ares,
723 : partition_ctrl);
724 : }
725 :
726 1542749 : if (ares->error != LDB_SUCCESS) {
727 1 : struct GUID_txt_buf guid_txt;
728 731 : struct ldb_message *msg = NULL;
729 731 : char *s = NULL;
730 :
731 731 : if (ac->apply_mode == false) {
732 731 : DBG_NOTICE("Originating update failure. Error is: %s\n",
733 : ldb_strerror(ares->error));
734 731 : return ldb_module_done(ac->req, controls,
735 : ares->response, ares->error);
736 : }
737 :
738 0 : msg = ac->objs->objects[ac->index_current].msg;
739 : /*
740 : * Set at DBG_NOTICE as once these start to happe, they
741 : * will happen a lot until resolved, due to repeated
742 : * replication. The caller will probably print the
743 : * ldb error string anyway.
744 : */
745 0 : DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
746 : ldb_dn_get_linearized(msg->dn),
747 : ldb_strerror(ares->error));
748 :
749 0 : s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
750 : ac,
751 : LDB_CHANGETYPE_ADD,
752 : msg);
753 :
754 0 : DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
755 : ac->search_msg == NULL ? "ADD" : "MODIFY",
756 : GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
757 : &guid_txt),
758 : s);
759 0 : talloc_free(s);
760 0 : return ldb_module_done(ac->req, controls,
761 : ares->response, ares->error);
762 : }
763 :
764 1542018 : if (ares->type != LDB_REPLY_DONE) {
765 0 : ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
766 0 : return ldb_module_done(ac->req, NULL,
767 : NULL, LDB_ERR_OPERATIONS_ERROR);
768 : }
769 :
770 1542018 : if (ac->apply_mode == false) {
771 107001 : struct la_backlink *bl;
772 : /*
773 : * process our backlink list after an replmd_add(),
774 : * creating and deleting backlinks as necessary (this
775 : * code is sync). The other cases are handled inline
776 : * with the modify.
777 : */
778 1100905 : for (bl=ac->la_backlinks; bl; bl=bl->next) {
779 11789 : ret = replmd_process_backlink(ac->module, bl, ac->req);
780 11789 : if (ret != LDB_SUCCESS) {
781 0 : return ldb_module_done(ac->req, NULL,
782 : NULL, ret);
783 : }
784 : }
785 : }
786 :
787 1542018 : if (!partition_ctrl) {
788 0 : ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
789 0 : return ldb_module_done(ac->req, NULL,
790 : NULL, LDB_ERR_OPERATIONS_ERROR);
791 : }
792 :
793 1542018 : partition = talloc_get_type_abort(partition_ctrl->data,
794 : struct dsdb_control_current_partition);
795 :
796 1542018 : if (ac->seq_num > 0) {
797 1541753 : for (modified_partition = replmd_private->ncs; modified_partition;
798 294711 : modified_partition = modified_partition->next) {
799 1314104 : if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
800 925050 : break;
801 : }
802 : }
803 :
804 1247042 : if (modified_partition == NULL) {
805 227649 : modified_partition = talloc_zero(replmd_private, struct nc_entry);
806 227649 : if (!modified_partition) {
807 0 : ldb_oom(ldb_module_get_ctx(ac->module));
808 0 : return ldb_module_done(ac->req, NULL,
809 : NULL, LDB_ERR_OPERATIONS_ERROR);
810 : }
811 227649 : modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
812 227649 : if (!modified_partition->dn) {
813 0 : ldb_oom(ldb_module_get_ctx(ac->module));
814 0 : return ldb_module_done(ac->req, NULL,
815 : NULL, LDB_ERR_OPERATIONS_ERROR);
816 : }
817 227649 : DLIST_ADD(replmd_private->ncs, modified_partition);
818 : }
819 :
820 1247042 : if (ac->seq_num > modified_partition->mod_usn) {
821 1247002 : modified_partition->mod_usn = ac->seq_num;
822 1247002 : if (ac->is_urgent) {
823 290471 : modified_partition->mod_usn_urgent = ac->seq_num;
824 : }
825 : }
826 1247042 : if (!ac->apply_mode) {
827 794140 : replmd_private->originating_updates = true;
828 : }
829 : }
830 :
831 1542018 : if (ac->apply_mode) {
832 452902 : ret = replmd_replicated_apply_isDeleted(ac);
833 452902 : if (ret != LDB_SUCCESS) {
834 33 : return ldb_module_done(ac->req, NULL, NULL, ret);
835 : }
836 452869 : return ret;
837 : } else {
838 : /* free the partition control container here, for the
839 : * common path. Other cases will have it cleaned up
840 : * eventually with the ares */
841 1089116 : talloc_free(partition_ctrl);
842 1089116 : return ldb_module_done(ac->req, controls,
843 : ares->response, LDB_SUCCESS);
844 : }
845 : }
846 :
847 :
848 : /*
849 : * update a @REPLCHANGED record in each partition if there have been
850 : * any writes of replicated data in the partition
851 : */
852 304372 : static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
853 : {
854 2188 : struct replmd_private *replmd_private =
855 304372 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
856 :
857 531785 : while (replmd_private->ncs) {
858 1106 : int ret;
859 227413 : struct nc_entry *modified_partition = replmd_private->ncs;
860 :
861 227413 : ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
862 : modified_partition->mod_usn,
863 : modified_partition->mod_usn_urgent, parent);
864 227413 : if (ret != LDB_SUCCESS) {
865 0 : DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
866 : ldb_dn_get_linearized(modified_partition->dn)));
867 0 : return ret;
868 : }
869 :
870 227413 : if (ldb_dn_compare(modified_partition->dn,
871 : replmd_private->schema_dn) == 0) {
872 47 : struct ldb_result *ext_res;
873 2041 : ret = dsdb_module_extended(module,
874 1994 : replmd_private->schema_dn,
875 : &ext_res,
876 : DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
877 : ext_res,
878 : DSDB_FLAG_NEXT_MODULE,
879 : parent);
880 1994 : if (ret != LDB_SUCCESS) {
881 0 : return ret;
882 : }
883 1994 : talloc_free(ext_res);
884 : }
885 :
886 227413 : DLIST_REMOVE(replmd_private->ncs, modified_partition);
887 227413 : talloc_free(modified_partition);
888 : }
889 :
890 302184 : return LDB_SUCCESS;
891 : }
892 :
893 :
894 : /*
895 : created a replmd_replicated_request context
896 : */
897 1093861 : static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
898 : struct ldb_request *req)
899 : {
900 107002 : struct ldb_context *ldb;
901 107002 : struct replmd_replicated_request *ac;
902 107002 : const struct GUID *our_invocation_id;
903 :
904 1093861 : ldb = ldb_module_get_ctx(module);
905 :
906 1093861 : ac = talloc_zero(req, struct replmd_replicated_request);
907 1093861 : if (ac == NULL) {
908 0 : ldb_oom(ldb);
909 0 : return NULL;
910 : }
911 :
912 1093861 : ac->module = module;
913 1093861 : ac->req = req;
914 :
915 1093861 : ac->schema = dsdb_get_schema(ldb, ac);
916 1093861 : if (!ac->schema) {
917 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
918 : "replmd_modify: no dsdb_schema loaded");
919 0 : DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
920 0 : talloc_free(ac);
921 0 : return NULL;
922 : }
923 :
924 : /* get our invocationId */
925 1093861 : our_invocation_id = samdb_ntds_invocation_id(ldb);
926 1093861 : if (!our_invocation_id) {
927 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
928 : "replmd_add: unable to find invocationId\n");
929 0 : talloc_free(ac);
930 0 : return NULL;
931 : }
932 1093861 : ac->our_invocation_id = *our_invocation_id;
933 :
934 1093861 : return ac;
935 : }
936 :
937 : /*
938 : add a time element to a record
939 : */
940 255772 : static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
941 : {
942 11839 : struct ldb_message_element *el;
943 11839 : char *s;
944 11839 : int ret;
945 :
946 255772 : if (ldb_msg_find_element(msg, attr) != NULL) {
947 0 : return LDB_SUCCESS;
948 : }
949 :
950 255772 : s = ldb_timestring(msg, t);
951 255772 : if (s == NULL) {
952 0 : return LDB_ERR_OPERATIONS_ERROR;
953 : }
954 :
955 255772 : ret = ldb_msg_add_string(msg, attr, s);
956 255772 : if (ret != LDB_SUCCESS) {
957 0 : return ret;
958 : }
959 :
960 255772 : el = ldb_msg_find_element(msg, attr);
961 : /* always set as replace. This works because on add ops, the flag
962 : is ignored */
963 255772 : el->flags = LDB_FLAG_MOD_REPLACE;
964 :
965 255772 : return LDB_SUCCESS;
966 : }
967 :
968 : /*
969 : add a uint64_t element to a record
970 : */
971 255772 : static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
972 : const char *attr, uint64_t v)
973 : {
974 11839 : struct ldb_message_element *el;
975 11839 : int ret;
976 :
977 255772 : if (ldb_msg_find_element(msg, attr) != NULL) {
978 0 : return LDB_SUCCESS;
979 : }
980 :
981 255772 : ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
982 255772 : if (ret != LDB_SUCCESS) {
983 0 : return ret;
984 : }
985 :
986 255772 : el = ldb_msg_find_element(msg, attr);
987 : /* always set as replace. This works because on add ops, the flag
988 : is ignored */
989 255772 : el->flags = LDB_FLAG_MOD_REPLACE;
990 :
991 255772 : return LDB_SUCCESS;
992 : }
993 :
994 49696654 : static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
995 : const struct replPropertyMetaData1 *m2)
996 : {
997 : /*
998 : * This assignment seems inoccous, but it is critical for the
999 : * system, as we need to do the comparisons as a unsigned
1000 : * quantity, not signed (enums are signed integers)
1001 : */
1002 49696654 : uint32_t attid_1 = m1->attid;
1003 49696654 : uint32_t attid_2 = m2->attid;
1004 :
1005 49696654 : if (attid_1 == attid_2) {
1006 0 : return 0;
1007 : }
1008 :
1009 : /*
1010 : * See above regarding this being an unsigned comparison.
1011 : * Otherwise when the high bit is set on non-standard
1012 : * attributes, they would end up first, before objectClass
1013 : * (0).
1014 : */
1015 49696654 : return attid_1 > attid_2 ? 1 : -1;
1016 : }
1017 :
1018 1328782 : static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
1019 : struct replPropertyMetaDataCtr1 *ctr1,
1020 : struct ldb_dn *dn)
1021 : {
1022 1328782 : if (ctr1->count == 0) {
1023 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1024 : "No elements found in replPropertyMetaData for %s!\n",
1025 : ldb_dn_get_linearized(dn));
1026 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
1027 : }
1028 :
1029 : /* the objectClass attribute is value 0x00000000, so must be first */
1030 1328782 : if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
1031 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1032 : "No objectClass found in replPropertyMetaData for %s!\n",
1033 : ldb_dn_get_linearized(dn));
1034 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
1035 : }
1036 :
1037 1223234 : return LDB_SUCCESS;
1038 : }
1039 :
1040 1328782 : static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
1041 : struct replPropertyMetaDataCtr1 *ctr1,
1042 : struct ldb_dn *dn)
1043 : {
1044 : /* Note this is O(n^2) for the almost-sorted case, which this is */
1045 1328782 : TYPESAFE_QSORT(ctr1->array, ctr1->count,
1046 : replmd_replPropertyMetaData1_attid_sort);
1047 1328782 : return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
1048 : }
1049 :
1050 89890579 : static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
1051 : const struct ldb_message_element *e2,
1052 : const struct dsdb_schema *schema)
1053 : {
1054 8157030 : const struct dsdb_attribute *a1;
1055 8157030 : const struct dsdb_attribute *a2;
1056 :
1057 : /*
1058 : * TODO: make this faster by caching the dsdb_attribute pointer
1059 : * on the ldb_messag_element
1060 : */
1061 :
1062 89890579 : a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
1063 89890579 : a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
1064 :
1065 : /*
1066 : * If the elements do not have valid attribute names in the schema
1067 : * (which we would prefer to think can't happen), we need to sort them
1068 : * somehow. The current strategy is to put them at the end, sorted by
1069 : * attribute name.
1070 : */
1071 89890579 : if (a1 == NULL && a2 == NULL) {
1072 0 : return strcasecmp(e1->name, e2->name);
1073 : }
1074 89890579 : if (a1 == NULL) {
1075 0 : return 1;
1076 : }
1077 89890579 : if (a2 == NULL) {
1078 0 : return -1;
1079 : }
1080 89890579 : return NUMERIC_CMP(a1->attributeID_id, a2->attributeID_id);
1081 : }
1082 :
1083 995414 : static void replmd_ldb_message_sort(struct ldb_message *msg,
1084 : const struct dsdb_schema *schema)
1085 : {
1086 995414 : LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
1087 911803 : }
1088 :
1089 : static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1090 : const struct GUID *invocation_id,
1091 : uint64_t local_usn, NTTIME nttime);
1092 :
1093 : static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
1094 :
1095 : static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1096 : struct ldb_message_element *el, struct parsed_dn **pdn,
1097 : const char *ldap_oid, struct ldb_request *parent);
1098 :
1099 : static int check_parsed_dn_duplicates(struct ldb_module *module,
1100 : struct ldb_message_element *el,
1101 : struct parsed_dn *pdn);
1102 :
1103 : /*
1104 : fix up linked attributes in replmd_add.
1105 : This involves setting up the right meta-data in extended DN
1106 : components, and creating backlinks to the object
1107 : */
1108 7045 : static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1109 : struct replmd_private *replmd_private,
1110 : struct ldb_message_element *el,
1111 : struct replmd_replicated_request *ac,
1112 : NTTIME now,
1113 : struct ldb_dn *forward_dn,
1114 : const struct dsdb_attribute *sa,
1115 : struct ldb_request *parent)
1116 : {
1117 827 : unsigned int i;
1118 7045 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1119 7045 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1120 827 : struct parsed_dn *pdn;
1121 : /* We will take a reference to the schema in replmd_add_backlink */
1122 7045 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
1123 7045 : struct ldb_val *new_values = NULL;
1124 827 : int ret;
1125 :
1126 7045 : if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1127 7044 : el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1128 : } else {
1129 1 : ldb_asprintf_errstring(ldb,
1130 : "Attribute %s is single valued but "
1131 : "more than one value has been supplied",
1132 : el->name);
1133 1 : talloc_free(tmp_ctx);
1134 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
1135 : }
1136 :
1137 : /*
1138 : * At the successful end of these functions el->values is
1139 : * overwritten with new_values. However get_parsed_dns()
1140 : * points p->v at the supplied el and it effectively gets used
1141 : * as a working area by replmd_build_la_val(). So we must
1142 : * duplicate it because our caller only called
1143 : * ldb_msg_copy_shallow().
1144 : */
1145 :
1146 7044 : el->values = talloc_memdup(tmp_ctx,
1147 : el->values,
1148 : sizeof(el->values[0]) * el->num_values);
1149 7044 : if (el->values == NULL) {
1150 0 : ldb_module_oom(module);
1151 0 : talloc_free(tmp_ctx);
1152 0 : return LDB_ERR_OPERATIONS_ERROR;
1153 : }
1154 :
1155 7871 : ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1156 7044 : sa->syntax->ldap_oid, parent);
1157 7044 : if (ret != LDB_SUCCESS) {
1158 0 : talloc_free(tmp_ctx);
1159 0 : return ret;
1160 : }
1161 :
1162 7044 : ret = check_parsed_dn_duplicates(module, el, pdn);
1163 7044 : if (ret != LDB_SUCCESS) {
1164 2 : talloc_free(tmp_ctx);
1165 2 : return ret;
1166 : }
1167 :
1168 7042 : new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1169 7042 : if (new_values == NULL) {
1170 0 : ldb_module_oom(module);
1171 0 : talloc_free(tmp_ctx);
1172 0 : return LDB_ERR_OPERATIONS_ERROR;
1173 : }
1174 :
1175 20767 : for (i = 0; i < el->num_values; i++) {
1176 13725 : struct parsed_dn *p = &pdn[i];
1177 15352 : ret = replmd_build_la_val(new_values, p->v, p->dsdb_dn,
1178 13725 : &ac->our_invocation_id,
1179 : ac->seq_num, now);
1180 13725 : if (ret != LDB_SUCCESS) {
1181 0 : talloc_free(tmp_ctx);
1182 0 : return ret;
1183 : }
1184 :
1185 13725 : ret = replmd_defer_add_backlink(module, replmd_private,
1186 : schema, ac,
1187 : forward_dn, &p->guid, true, sa,
1188 : parent);
1189 13725 : if (ret != LDB_SUCCESS) {
1190 0 : talloc_free(tmp_ctx);
1191 0 : return ret;
1192 : }
1193 :
1194 13725 : new_values[i] = *p->v;
1195 : }
1196 7042 : el->values = talloc_steal(mem_ctx, new_values);
1197 :
1198 7042 : talloc_free(tmp_ctx);
1199 7042 : return LDB_SUCCESS;
1200 : }
1201 :
1202 6141 : static int replmd_add_make_extended_dn(struct ldb_request *req,
1203 : const DATA_BLOB *guid_blob,
1204 : struct ldb_dn **_extended_dn)
1205 : {
1206 761 : int ret;
1207 761 : const DATA_BLOB *sid_blob;
1208 : /* Calculate an extended DN for any linked attributes */
1209 6141 : struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1210 6141 : if (!extended_dn) {
1211 0 : return LDB_ERR_OPERATIONS_ERROR;
1212 : }
1213 6141 : ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1214 6141 : if (ret != LDB_SUCCESS) {
1215 0 : return ret;
1216 : }
1217 :
1218 6141 : sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1219 6141 : if (sid_blob != NULL) {
1220 2830 : ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1221 2830 : if (ret != LDB_SUCCESS) {
1222 0 : return ret;
1223 : }
1224 : }
1225 6141 : *_extended_dn = extended_dn;
1226 6141 : return LDB_SUCCESS;
1227 : }
1228 :
1229 : /*
1230 : intercept add requests
1231 : */
1232 543053 : static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1233 : {
1234 83679 : struct ldb_context *ldb;
1235 83679 : struct ldb_control *control;
1236 83679 : struct replmd_replicated_request *ac;
1237 83679 : enum ndr_err_code ndr_err;
1238 83679 : struct ldb_request *down_req;
1239 83679 : struct ldb_message *msg;
1240 83679 : const DATA_BLOB *guid_blob;
1241 83679 : DATA_BLOB guid_blob_stack;
1242 83679 : struct GUID guid;
1243 83679 : uint8_t guid_data[16];
1244 83679 : struct replPropertyMetaDataBlob nmd;
1245 83679 : struct ldb_val nmd_value;
1246 543053 : struct ldb_dn *extended_dn = NULL;
1247 :
1248 : /*
1249 : * The use of a time_t here seems odd, but as the NTTIME
1250 : * elements are actually declared as NTTIME_1sec in the IDL,
1251 : * getting a higher resolution timestamp is not required.
1252 : */
1253 543053 : time_t t = time(NULL);
1254 83679 : NTTIME now;
1255 83679 : char *time_str;
1256 83679 : int ret;
1257 83679 : unsigned int i;
1258 83679 : unsigned int functional_level;
1259 543053 : uint32_t ni=0;
1260 543053 : bool allow_add_guid = false;
1261 543053 : bool remove_current_guid = false;
1262 543053 : bool is_urgent = false;
1263 543053 : bool is_schema_nc = false;
1264 83679 : struct ldb_message_element *objectclass_el;
1265 83679 : struct replmd_private *replmd_private =
1266 543053 : talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1267 :
1268 : /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1269 543053 : control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1270 543053 : if (control) {
1271 271837 : allow_add_guid = true;
1272 : }
1273 :
1274 : /* do not manipulate our control entries */
1275 543053 : if (ldb_dn_is_special(req->op.add.message->dn)) {
1276 538 : return ldb_next_request(module, req);
1277 : }
1278 :
1279 542515 : ldb = ldb_module_get_ctx(module);
1280 :
1281 542515 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1282 :
1283 542515 : guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1284 542515 : if (guid_blob != NULL) {
1285 216251 : if (!allow_add_guid) {
1286 1 : ldb_set_errstring(ldb,
1287 : "replmd_add: it's not allowed to add an object with objectGUID!");
1288 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
1289 : } else {
1290 216250 : NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1291 216250 : if (!NT_STATUS_IS_OK(status)) {
1292 0 : ldb_set_errstring(ldb,
1293 : "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1294 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
1295 : }
1296 : /* we remove this attribute as it can be a string and
1297 : * will not be treated correctly and then we will re-add
1298 : * it later on in the good format */
1299 216250 : remove_current_guid = true;
1300 : }
1301 : } else {
1302 : /* a new GUID */
1303 326264 : guid = GUID_random();
1304 :
1305 326264 : guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1306 :
1307 : /* This can't fail */
1308 326264 : ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1309 : (ndr_push_flags_fn_t)ndr_push_GUID);
1310 326264 : guid_blob = &guid_blob_stack;
1311 : }
1312 :
1313 542514 : ac = replmd_ctx_init(module, req);
1314 542514 : if (ac == NULL) {
1315 0 : return ldb_module_oom(module);
1316 : }
1317 :
1318 542514 : functional_level = dsdb_functional_level(ldb);
1319 :
1320 : /* Get a sequence number from the backend */
1321 542514 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1322 542514 : if (ret != LDB_SUCCESS) {
1323 0 : talloc_free(ac);
1324 0 : return ret;
1325 : }
1326 :
1327 : /* we have to copy the message as the caller might have it as a const */
1328 542514 : msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1329 542514 : if (msg == NULL) {
1330 0 : ldb_oom(ldb);
1331 0 : talloc_free(ac);
1332 0 : return LDB_ERR_OPERATIONS_ERROR;
1333 : }
1334 :
1335 : /* generated times */
1336 542514 : unix_to_nt_time(&now, t);
1337 542514 : time_str = ldb_timestring(msg, t);
1338 542514 : if (!time_str) {
1339 0 : ldb_oom(ldb);
1340 0 : talloc_free(ac);
1341 0 : return LDB_ERR_OPERATIONS_ERROR;
1342 : }
1343 542514 : if (remove_current_guid) {
1344 216250 : ldb_msg_remove_attr(msg,"objectGUID");
1345 : }
1346 :
1347 : /*
1348 : * remove autogenerated attributes
1349 : */
1350 542514 : ldb_msg_remove_attr(msg, "whenCreated");
1351 542514 : ldb_msg_remove_attr(msg, "whenChanged");
1352 542514 : ldb_msg_remove_attr(msg, "uSNCreated");
1353 542514 : ldb_msg_remove_attr(msg, "uSNChanged");
1354 542514 : ldb_msg_remove_attr(msg, "replPropertyMetaData");
1355 :
1356 : /*
1357 : * readd replicated attributes
1358 : */
1359 542514 : ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1360 542514 : if (ret != LDB_SUCCESS) {
1361 0 : ldb_oom(ldb);
1362 0 : talloc_free(ac);
1363 0 : return ret;
1364 : }
1365 :
1366 : /* build the replication meta_data */
1367 542514 : ZERO_STRUCT(nmd);
1368 542514 : nmd.version = 1;
1369 542514 : nmd.ctr.ctr1.count = msg->num_elements;
1370 542514 : nmd.ctr.ctr1.array = talloc_array(msg,
1371 : struct replPropertyMetaData1,
1372 : nmd.ctr.ctr1.count);
1373 542514 : if (!nmd.ctr.ctr1.array) {
1374 0 : ldb_oom(ldb);
1375 0 : talloc_free(ac);
1376 0 : return LDB_ERR_OPERATIONS_ERROR;
1377 : }
1378 :
1379 542514 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1380 :
1381 9112200 : for (i=0; i < msg->num_elements;) {
1382 8569689 : struct ldb_message_element *e = &msg->elements[i];
1383 8569689 : struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1384 1319919 : const struct dsdb_attribute *sa;
1385 :
1386 8569689 : if (e->name[0] == '@') {
1387 0 : i++;
1388 0 : continue;
1389 : }
1390 :
1391 8569689 : sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1392 8569689 : if (!sa) {
1393 0 : ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1394 : "replmd_add: attribute '%s' not defined in schema\n",
1395 : e->name);
1396 0 : talloc_free(ac);
1397 0 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
1398 : }
1399 :
1400 8569689 : if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1401 : /* if the attribute is not replicated (0x00000001)
1402 : * or constructed (0x00000004) it has no metadata
1403 : */
1404 152268 : i++;
1405 152268 : continue;
1406 : }
1407 :
1408 8417421 : if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1409 7045 : if (extended_dn == NULL) {
1410 6141 : ret = replmd_add_make_extended_dn(req,
1411 : guid_blob,
1412 : &extended_dn);
1413 6141 : if (ret != LDB_SUCCESS) {
1414 0 : talloc_free(ac);
1415 0 : return ret;
1416 : }
1417 : }
1418 :
1419 : /*
1420 : * Prepare the context for the backlinks and
1421 : * create metadata for the forward links. The
1422 : * backlinks are created in
1423 : * replmd_op_callback() after the successful
1424 : * ADD of the object.
1425 : */
1426 7045 : ret = replmd_add_fix_la(module, msg->elements,
1427 : replmd_private, e,
1428 : ac, now,
1429 : extended_dn,
1430 : sa, req);
1431 7045 : if (ret != LDB_SUCCESS) {
1432 3 : talloc_free(ac);
1433 3 : return ret;
1434 : }
1435 : /* linked attributes are not stored in
1436 : replPropertyMetaData in FL above w2k */
1437 7042 : i++;
1438 7042 : continue;
1439 : }
1440 :
1441 8410376 : m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1442 8410376 : m->version = 1;
1443 8410376 : if (m->attid == DRSUAPI_ATTID_isDeleted) {
1444 487 : const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1445 86 : const char* rdn;
1446 :
1447 487 : if (rdn_val == NULL) {
1448 0 : ldb_oom(ldb);
1449 0 : talloc_free(ac);
1450 0 : return LDB_ERR_OPERATIONS_ERROR;
1451 : }
1452 :
1453 487 : rdn = (const char*)rdn_val->data;
1454 487 : if (strcmp(rdn, "Deleted Objects") == 0) {
1455 : /*
1456 : * Set the originating_change_time to 29/12/9999 at 23:59:59
1457 : * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1458 : */
1459 484 : m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1460 : } else {
1461 3 : m->originating_change_time = now;
1462 : }
1463 : } else {
1464 8409889 : m->originating_change_time = now;
1465 : }
1466 8410376 : m->originating_invocation_id = ac->our_invocation_id;
1467 8410376 : m->originating_usn = ac->seq_num;
1468 8410376 : m->local_usn = ac->seq_num;
1469 8410376 : ni++;
1470 :
1471 8410376 : if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1472 8224974 : i++;
1473 8224974 : continue;
1474 : }
1475 :
1476 185402 : e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1477 :
1478 185402 : if (e->num_values != 0) {
1479 42386 : i++;
1480 42386 : continue;
1481 : }
1482 :
1483 143016 : ldb_msg_remove_element(msg, e);
1484 : }
1485 :
1486 : /* fix meta data count */
1487 542511 : nmd.ctr.ctr1.count = ni;
1488 :
1489 : /*
1490 : * sort meta data array
1491 : */
1492 542511 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1493 542511 : if (ret != LDB_SUCCESS) {
1494 0 : ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1495 0 : talloc_free(ac);
1496 0 : return ret;
1497 : }
1498 :
1499 : /* generated NDR encoded values */
1500 542511 : ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1501 : &nmd,
1502 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1503 542511 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1504 0 : ldb_oom(ldb);
1505 0 : talloc_free(ac);
1506 0 : return LDB_ERR_OPERATIONS_ERROR;
1507 : }
1508 :
1509 : /*
1510 : * add the autogenerated values
1511 : */
1512 542511 : ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1513 542511 : if (ret != LDB_SUCCESS) {
1514 0 : ldb_oom(ldb);
1515 0 : talloc_free(ac);
1516 0 : return ret;
1517 : }
1518 542511 : ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1519 542511 : if (ret != LDB_SUCCESS) {
1520 0 : ldb_oom(ldb);
1521 0 : talloc_free(ac);
1522 0 : return ret;
1523 : }
1524 542511 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1525 542511 : if (ret != LDB_SUCCESS) {
1526 0 : ldb_oom(ldb);
1527 0 : talloc_free(ac);
1528 0 : return ret;
1529 : }
1530 542511 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1531 542511 : if (ret != LDB_SUCCESS) {
1532 0 : ldb_oom(ldb);
1533 0 : talloc_free(ac);
1534 0 : return ret;
1535 : }
1536 542511 : ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1537 542511 : if (ret != LDB_SUCCESS) {
1538 0 : ldb_oom(ldb);
1539 0 : talloc_free(ac);
1540 0 : return ret;
1541 : }
1542 :
1543 : /*
1544 : * sort the attributes by attid before storing the object
1545 : */
1546 542511 : replmd_ldb_message_sort(msg, ac->schema);
1547 :
1548 : /*
1549 : * Assert that we do have an objectClass
1550 : */
1551 542511 : objectclass_el = ldb_msg_find_element(msg, "objectClass");
1552 542511 : if (objectclass_el == NULL) {
1553 0 : ldb_asprintf_errstring(ldb, __location__
1554 : ": objectClass missing on %s\n",
1555 : ldb_dn_get_linearized(msg->dn));
1556 0 : talloc_free(ac);
1557 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
1558 : }
1559 542511 : is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1560 : REPL_URGENT_ON_CREATE);
1561 :
1562 542511 : ac->is_urgent = is_urgent;
1563 542511 : ret = ldb_build_add_req(&down_req, ldb, ac,
1564 : msg,
1565 : req->controls,
1566 : ac, replmd_op_callback,
1567 : req);
1568 :
1569 542511 : LDB_REQ_SET_LOCATION(down_req);
1570 542511 : if (ret != LDB_SUCCESS) {
1571 0 : talloc_free(ac);
1572 0 : return ret;
1573 : }
1574 :
1575 : /* current partition control is needed by "replmd_op_callback" */
1576 542511 : if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1577 542511 : ret = ldb_request_add_control(down_req,
1578 : DSDB_CONTROL_CURRENT_PARTITION_OID,
1579 : false, NULL);
1580 542511 : if (ret != LDB_SUCCESS) {
1581 0 : talloc_free(ac);
1582 0 : return ret;
1583 : }
1584 : }
1585 :
1586 542511 : if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1587 15157 : ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1588 15157 : if (ret != LDB_SUCCESS) {
1589 0 : talloc_free(ac);
1590 0 : return ret;
1591 : }
1592 : }
1593 :
1594 : /* mark the relax control done */
1595 542511 : if (control) {
1596 271837 : control->critical = 0;
1597 : }
1598 : /* go on with the call chain */
1599 542511 : return ldb_next_request(module, down_req);
1600 : }
1601 :
1602 :
1603 : /*
1604 : * update the replPropertyMetaData for one element
1605 : */
1606 1962503 : static int replmd_update_rpmd_element(struct ldb_context *ldb,
1607 : struct ldb_message *msg,
1608 : struct ldb_message_element *el,
1609 : struct ldb_message_element *old_el,
1610 : struct replPropertyMetaDataBlob *omd,
1611 : const struct dsdb_schema *schema,
1612 : uint64_t *seq_num,
1613 : const struct GUID *our_invocation_id,
1614 : NTTIME now,
1615 : bool is_schema_nc,
1616 : bool is_forced_rodc,
1617 : struct ldb_request *req)
1618 : {
1619 21989 : uint32_t i;
1620 21989 : const struct dsdb_attribute *a;
1621 21989 : struct replPropertyMetaData1 *md1;
1622 1962503 : bool may_skip = false;
1623 21989 : uint32_t attid;
1624 :
1625 1962503 : a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1626 1962503 : if (a == NULL) {
1627 0 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1628 : /* allow this to make it possible for dbcheck
1629 : to remove bad attributes */
1630 0 : return LDB_SUCCESS;
1631 : }
1632 :
1633 0 : DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1634 : el->name));
1635 0 : return LDB_ERR_OPERATIONS_ERROR;
1636 : }
1637 :
1638 1962503 : attid = dsdb_attribute_get_attid(a, is_schema_nc);
1639 :
1640 1962503 : if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1641 288484 : return LDB_SUCCESS;
1642 : }
1643 :
1644 : /*
1645 : * if the attribute's value haven't changed, and this isn't
1646 : * just a delete of everything then return LDB_SUCCESS Unless
1647 : * we have the provision control or if the attribute is
1648 : * interSiteTopologyGenerator as this page explain:
1649 : * http://support.microsoft.com/kb/224815 this attribute is
1650 : * periodically written by the DC responsible for the intersite
1651 : * generation in a given site
1652 : *
1653 : * Unchanged could be deleting or replacing an already-gone
1654 : * thing with an unconstrained delete/empty replace or a
1655 : * replace with the same value, but not an add with the same
1656 : * value because that could be about adding a duplicate (which
1657 : * is for someone else to error out on).
1658 : */
1659 1670756 : if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1660 185622 : if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1661 160754 : may_skip = true;
1662 : }
1663 1485134 : } else if (old_el == NULL && el->num_values == 0) {
1664 36436 : if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1665 35869 : may_skip = true;
1666 242 : } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1667 241 : may_skip = true;
1668 : }
1669 1480398 : } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1670 31700 : ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1671 : /*
1672 : * We intentionally skip the version bump when attempting to
1673 : * vanish links.
1674 : *
1675 : * The control is set by dbcheck and expunge-tombstones which
1676 : * both attempt to be non-replicating. Otherwise, making an
1677 : * alteration to the replication state would trigger a
1678 : * broadcast of all expunged objects.
1679 : */
1680 30422 : may_skip = true;
1681 : }
1682 :
1683 1670756 : if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1684 101509 : may_skip = false;
1685 101509 : el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1686 : }
1687 :
1688 1670756 : if (may_skip) {
1689 383652 : if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1690 191826 : !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1691 : /*
1692 : * allow this to make it possible for dbcheck
1693 : * to rebuild broken metadata
1694 : */
1695 191554 : return LDB_SUCCESS;
1696 : }
1697 : }
1698 :
1699 20914474 : for (i=0; i<omd->ctr.ctr1.count; i++) {
1700 : /*
1701 : * First check if we find it under the msDS-IntID,
1702 : * then check if we find it under the OID and
1703 : * prefixMap ID.
1704 : *
1705 : * This allows the administrator to simply re-write
1706 : * the attributes and so restore replication, which is
1707 : * likely what they will try to do.
1708 : */
1709 20234895 : if (attid == omd->ctr.ctr1.array[i].attid) {
1710 784758 : break;
1711 : }
1712 :
1713 19435536 : if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1714 2 : break;
1715 : }
1716 : }
1717 :
1718 1478940 : if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1719 : /* linked attributes are not stored in
1720 : replPropertyMetaData in FL above w2k, but we do
1721 : raise the seqnum for the object */
1722 40585 : if (*seq_num == 0 &&
1723 12207 : ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1724 0 : return LDB_ERR_OPERATIONS_ERROR;
1725 : }
1726 28378 : return LDB_SUCCESS;
1727 : }
1728 :
1729 1450562 : if (i == omd->ctr.ctr1.count) {
1730 : /* we need to add a new one */
1731 651201 : omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1732 : struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1733 651201 : if (omd->ctr.ctr1.array == NULL) {
1734 0 : ldb_oom(ldb);
1735 0 : return LDB_ERR_OPERATIONS_ERROR;
1736 : }
1737 651201 : omd->ctr.ctr1.count++;
1738 651201 : ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1739 : }
1740 :
1741 : /* Get a new sequence number from the backend. We only do this
1742 : * if we have a change that requires a new
1743 : * replPropertyMetaData element
1744 : */
1745 1450562 : if (*seq_num == 0) {
1746 239940 : int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1747 239940 : if (ret != LDB_SUCCESS) {
1748 0 : return LDB_ERR_OPERATIONS_ERROR;
1749 : }
1750 : }
1751 :
1752 1450562 : md1 = &omd->ctr.ctr1.array[i];
1753 1450562 : md1->version++;
1754 1450562 : md1->attid = attid;
1755 :
1756 1450562 : if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1757 73075 : const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1758 141 : const char* rdn;
1759 :
1760 73075 : if (rdn_val == NULL) {
1761 0 : ldb_oom(ldb);
1762 0 : return LDB_ERR_OPERATIONS_ERROR;
1763 : }
1764 :
1765 73075 : rdn = (const char*)rdn_val->data;
1766 73075 : if (strcmp(rdn, "Deleted Objects") == 0) {
1767 : /*
1768 : * Set the originating_change_time to 29/12/9999 at 23:59:59
1769 : * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1770 : */
1771 8 : md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1772 : } else {
1773 73067 : md1->originating_change_time = now;
1774 : }
1775 : } else {
1776 1377487 : md1->originating_change_time = now;
1777 : }
1778 1450562 : md1->originating_invocation_id = *our_invocation_id;
1779 1450562 : md1->originating_usn = *seq_num;
1780 1450562 : md1->local_usn = *seq_num;
1781 :
1782 1450562 : if (is_forced_rodc) {
1783 : /* Force version to 0 to be overridden later via replication */
1784 8 : md1->version = 0;
1785 : }
1786 :
1787 1432231 : return LDB_SUCCESS;
1788 : }
1789 :
1790 : /*
1791 : * Bump the replPropertyMetaData version on an attribute, and if it
1792 : * has changed (or forced by leaving rdn_old NULL), update the value
1793 : * in the entry.
1794 : *
1795 : * This is important, as calling a modify operation may not change the
1796 : * version number if the values appear unchanged, but a rename between
1797 : * parents bumps this value.
1798 : *
1799 : */
1800 388820 : static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1801 : struct ldb_message *msg,
1802 : const struct ldb_val *rdn_new,
1803 : const struct ldb_val *rdn_old,
1804 : struct replPropertyMetaDataBlob *omd,
1805 : struct replmd_replicated_request *ar,
1806 : NTTIME now,
1807 : bool is_schema_nc,
1808 : bool is_forced_rodc)
1809 : {
1810 388820 : const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1811 0 : const struct dsdb_attribute *rdn_attr =
1812 388820 : dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1813 388820 : const char *attr_name = rdn_attr != NULL ?
1814 388820 : rdn_attr->lDAPDisplayName :
1815 : rdn_name;
1816 388820 : struct ldb_message_element new_el = {
1817 : .flags = LDB_FLAG_MOD_REPLACE,
1818 : .name = attr_name,
1819 : .num_values = 1,
1820 : .values = discard_const_p(struct ldb_val, rdn_new)
1821 : };
1822 388820 : struct ldb_message_element old_el = {
1823 : .flags = LDB_FLAG_MOD_REPLACE,
1824 : .name = attr_name,
1825 388820 : .num_values = rdn_old ? 1 : 0,
1826 : .values = discard_const_p(struct ldb_val, rdn_old)
1827 : };
1828 :
1829 388820 : if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1830 388797 : int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1831 388797 : if (ret != LDB_SUCCESS) {
1832 0 : return ldb_oom(ldb);
1833 : }
1834 : }
1835 :
1836 388820 : return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1837 : omd, ar->schema, &ar->seq_num,
1838 388820 : &ar->our_invocation_id,
1839 : now, is_schema_nc, is_forced_rodc,
1840 : ar->req);
1841 :
1842 : }
1843 :
1844 18 : static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1845 : {
1846 18 : uint32_t count = omd.ctr.ctr1.count;
1847 18 : uint64_t max = 0;
1848 0 : uint32_t i;
1849 310 : for (i=0; i < count; i++) {
1850 292 : struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1851 292 : if (max < m.local_usn) {
1852 68 : max = m.local_usn;
1853 : }
1854 : }
1855 18 : return max;
1856 : }
1857 :
1858 : /*
1859 : * update the replPropertyMetaData object each time we modify an
1860 : * object. This is needed for DRS replication, as the merge on the
1861 : * client is based on this object
1862 : */
1863 547412 : static int replmd_update_rpmd(struct ldb_module *module,
1864 : const struct dsdb_schema *schema,
1865 : struct ldb_request *req,
1866 : const char * const *rename_attrs,
1867 : struct ldb_message *msg, uint64_t *seq_num,
1868 : time_t t, bool is_schema_nc,
1869 : bool *is_urgent, bool *rodc)
1870 547412 : {
1871 23391 : const struct ldb_val *omd_value;
1872 23391 : enum ndr_err_code ndr_err;
1873 23391 : struct replPropertyMetaDataBlob omd;
1874 23391 : unsigned int i;
1875 23391 : NTTIME now;
1876 23391 : const struct GUID *our_invocation_id;
1877 23391 : int ret;
1878 547412 : const char * const *attrs = NULL;
1879 547412 : const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1880 23391 : struct ldb_result *res;
1881 23391 : struct ldb_context *ldb;
1882 23391 : struct ldb_message_element *objectclass_el;
1883 23391 : enum urgent_situation situation;
1884 23391 : bool rmd_is_provided;
1885 547412 : bool rmd_is_just_resorted = false;
1886 547412 : const char *not_rename_attrs[4 + msg->num_elements];
1887 547412 : bool is_forced_rodc = false;
1888 :
1889 547412 : if (rename_attrs) {
1890 1486 : attrs = rename_attrs;
1891 : } else {
1892 2150318 : for (i = 0; i < msg->num_elements; i++) {
1893 1604397 : not_rename_attrs[i] = msg->elements[i].name;
1894 : }
1895 545921 : not_rename_attrs[i] = "replPropertyMetaData";
1896 545921 : not_rename_attrs[i+1] = "objectClass";
1897 545921 : not_rename_attrs[i+2] = "instanceType";
1898 545921 : not_rename_attrs[i+3] = NULL;
1899 545921 : attrs = not_rename_attrs;
1900 : }
1901 :
1902 547412 : ldb = ldb_module_get_ctx(module);
1903 :
1904 547412 : ret = samdb_rodc(ldb, rodc);
1905 547412 : if (ret != LDB_SUCCESS) {
1906 0 : DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1907 0 : *rodc = false;
1908 : }
1909 :
1910 558870 : if (*rodc &&
1911 11458 : ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1912 527 : is_forced_rodc = true;
1913 : }
1914 :
1915 547412 : our_invocation_id = samdb_ntds_invocation_id(ldb);
1916 547412 : if (!our_invocation_id) {
1917 : /* this happens during an initial vampire while
1918 : updating the schema */
1919 0 : DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1920 0 : return LDB_SUCCESS;
1921 : }
1922 :
1923 547412 : unix_to_nt_time(&now, t);
1924 :
1925 547412 : if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1926 33667 : rmd_is_provided = true;
1927 33667 : if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1928 33648 : rmd_is_just_resorted = true;
1929 : }
1930 : } else {
1931 500452 : rmd_is_provided = false;
1932 : }
1933 :
1934 : /* if isDeleted is present and is TRUE, then we consider we are deleting,
1935 : * otherwise we consider we are updating */
1936 547412 : if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1937 72677 : situation = REPL_URGENT_ON_DELETE;
1938 474594 : } else if (rename_attrs) {
1939 1486 : situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1940 : } else {
1941 473103 : situation = REPL_URGENT_ON_UPDATE;
1942 : }
1943 :
1944 547412 : if (rmd_is_provided) {
1945 : /* In this case the change_replmetadata control was supplied */
1946 : /* We check that it's the only attribute that is provided
1947 : * (it's a rare case so it's better to keep the code simpler)
1948 : * We also check that the highest local_usn is bigger or the same as
1949 : * uSNChanged. */
1950 10098 : uint64_t db_seq;
1951 33667 : if( msg->num_elements != 1 ||
1952 33666 : strncmp(msg->elements[0].name,
1953 : "replPropertyMetaData", 20) ) {
1954 1 : DEBUG(0,(__location__ ": changereplmetada control called without "\
1955 : "a specified replPropertyMetaData attribute or with others\n"));
1956 1 : return LDB_ERR_OPERATIONS_ERROR;
1957 : }
1958 33666 : if (situation != REPL_URGENT_ON_UPDATE) {
1959 0 : DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1960 0 : return LDB_ERR_OPERATIONS_ERROR;
1961 : }
1962 33666 : omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1963 33666 : if (!omd_value) {
1964 0 : DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1965 : ldb_dn_get_linearized(msg->dn)));
1966 0 : return LDB_ERR_OPERATIONS_ERROR;
1967 : }
1968 33666 : ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1969 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1970 33666 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1971 0 : DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1972 : ldb_dn_get_linearized(msg->dn)));
1973 0 : return LDB_ERR_OPERATIONS_ERROR;
1974 : }
1975 :
1976 33666 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1977 : DSDB_FLAG_NEXT_MODULE |
1978 : DSDB_SEARCH_SHOW_RECYCLED |
1979 : DSDB_SEARCH_SHOW_EXTENDED_DN |
1980 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1981 : DSDB_SEARCH_REVEAL_INTERNALS, req);
1982 :
1983 33666 : if (ret != LDB_SUCCESS) {
1984 0 : return ret;
1985 : }
1986 :
1987 33666 : if (rmd_is_just_resorted == false) {
1988 18 : *seq_num = find_max_local_usn(omd);
1989 :
1990 18 : db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1991 :
1992 : /*
1993 : * The test here now allows for a new
1994 : * replPropertyMetaData with no change, if was
1995 : * just dbcheck re-sorting the values.
1996 : */
1997 18 : if (*seq_num <= db_seq) {
1998 2 : DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1999 : " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
2000 : (long long)*seq_num, (long long)db_seq));
2001 2 : return LDB_ERR_OPERATIONS_ERROR;
2002 : }
2003 : }
2004 :
2005 : } else {
2006 : /* search for the existing replPropertyMetaDataBlob. We need
2007 : * to use REVEAL and ask for DNs in storage format to support
2008 : * the check for values being the same in
2009 : * replmd_update_rpmd_element()
2010 : */
2011 513745 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
2012 : DSDB_FLAG_NEXT_MODULE |
2013 : DSDB_SEARCH_SHOW_RECYCLED |
2014 : DSDB_SEARCH_SHOW_EXTENDED_DN |
2015 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2016 : DSDB_SEARCH_REVEAL_INTERNALS, req);
2017 513745 : if (ret != LDB_SUCCESS) {
2018 28 : return ret;
2019 : }
2020 :
2021 513717 : omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
2022 513717 : if (!omd_value) {
2023 0 : DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
2024 : ldb_dn_get_linearized(msg->dn)));
2025 0 : return LDB_ERR_OPERATIONS_ERROR;
2026 : }
2027 :
2028 513717 : ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
2029 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2030 513717 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2031 0 : DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
2032 : ldb_dn_get_linearized(msg->dn)));
2033 0 : return LDB_ERR_OPERATIONS_ERROR;
2034 : }
2035 :
2036 513717 : if (omd.version != 1) {
2037 0 : DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
2038 : omd.version, ldb_dn_get_linearized(msg->dn)));
2039 0 : return LDB_ERR_OPERATIONS_ERROR;
2040 : }
2041 :
2042 2087400 : for (i=0; i<msg->num_elements;) {
2043 1573683 : struct ldb_message_element *el = &msg->elements[i];
2044 21989 : struct ldb_message_element *old_el;
2045 :
2046 1573683 : old_el = ldb_msg_find_element(res->msgs[0], el->name);
2047 1573683 : ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
2048 : &omd, schema, seq_num,
2049 : our_invocation_id,
2050 : now, is_schema_nc,
2051 : is_forced_rodc,
2052 : req);
2053 1573683 : if (ret != LDB_SUCCESS) {
2054 0 : return ret;
2055 : }
2056 :
2057 1573683 : if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
2058 657206 : *is_urgent = replmd_check_urgent_attribute(el);
2059 : }
2060 :
2061 1573683 : if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
2062 1573683 : i++;
2063 1573683 : continue;
2064 : }
2065 :
2066 0 : el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
2067 :
2068 0 : if (el->num_values != 0) {
2069 0 : i++;
2070 0 : continue;
2071 : }
2072 :
2073 0 : ldb_msg_remove_element(msg, el);
2074 : }
2075 : }
2076 :
2077 : /*
2078 : * Assert that we have an objectClass attribute - this is major
2079 : * corruption if we don't have this!
2080 : */
2081 547381 : objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
2082 547381 : if (objectclass_el != NULL) {
2083 : /*
2084 : * Now check if this objectClass means we need to do urgent replication
2085 : */
2086 547381 : if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
2087 : situation)) {
2088 54804 : *is_urgent = true;
2089 : }
2090 0 : } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
2091 0 : ldb_asprintf_errstring(ldb, __location__
2092 : ": objectClass missing on %s\n",
2093 : ldb_dn_get_linearized(msg->dn));
2094 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2095 : }
2096 :
2097 : /*
2098 : * replmd_update_rpmd_element has done an update if the
2099 : * seq_num is set
2100 : */
2101 547381 : if (*seq_num != 0 || rmd_is_just_resorted == true) {
2102 21937 : struct ldb_val *md_value;
2103 21937 : struct ldb_message_element *el;
2104 :
2105 : /*if we are RODC and this is a DRSR update then its ok*/
2106 285811 : if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
2107 285795 : && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
2108 252140 : && !is_forced_rodc) {
2109 11839 : unsigned instanceType;
2110 :
2111 252132 : if (*rodc) {
2112 11 : ldb_set_errstring(ldb, "RODC modify is forbidden!");
2113 11 : return LDB_ERR_REFERRAL;
2114 : }
2115 :
2116 252121 : instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
2117 252121 : if (!(instanceType & INSTANCE_TYPE_WRITE)) {
2118 0 : return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
2119 : "cannot change replicated attribute on partial replica");
2120 : }
2121 : }
2122 :
2123 285800 : md_value = talloc(msg, struct ldb_val);
2124 285800 : if (md_value == NULL) {
2125 0 : ldb_oom(ldb);
2126 0 : return LDB_ERR_OPERATIONS_ERROR;
2127 : }
2128 :
2129 285800 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2130 285800 : if (ret != LDB_SUCCESS) {
2131 0 : ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2132 0 : return ret;
2133 : }
2134 :
2135 285800 : ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2136 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2137 285800 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2138 0 : DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2139 : ldb_dn_get_linearized(msg->dn)));
2140 0 : return LDB_ERR_OPERATIONS_ERROR;
2141 : }
2142 :
2143 285800 : ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2144 285800 : if (ret != LDB_SUCCESS) {
2145 0 : DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2146 : ldb_dn_get_linearized(msg->dn)));
2147 0 : return ret;
2148 : }
2149 :
2150 285800 : el->num_values = 1;
2151 285800 : el->values = md_value;
2152 : }
2153 :
2154 523979 : return LDB_SUCCESS;
2155 : }
2156 :
2157 8067605 : static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2158 : {
2159 8067605 : int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2160 8067605 : if (ret == 0) {
2161 2517086 : return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2162 2517086 : &pdn2->dsdb_dn->extra_part);
2163 : }
2164 5547460 : return ret;
2165 : }
2166 :
2167 : /*
2168 : get a series of message element values as an array of DNs and GUIDs
2169 : the result is sorted by GUID
2170 : */
2171 65364 : static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2172 : struct ldb_message_element *el, struct parsed_dn **pdn,
2173 : const char *ldap_oid, struct ldb_request *parent)
2174 : {
2175 1105 : unsigned int i;
2176 65364 : bool values_are_sorted = true;
2177 65364 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2178 :
2179 65364 : if (el == NULL) {
2180 554 : *pdn = NULL;
2181 554 : return LDB_SUCCESS;
2182 : }
2183 :
2184 64810 : (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2185 64810 : if (!*pdn) {
2186 0 : ldb_module_oom(module);
2187 0 : return LDB_ERR_OPERATIONS_ERROR;
2188 : }
2189 :
2190 133641 : for (i=0; i<el->num_values; i++) {
2191 68834 : struct ldb_val *v = &el->values[i];
2192 2012 : NTSTATUS status;
2193 2012 : struct ldb_dn *dn;
2194 2012 : struct parsed_dn *p;
2195 :
2196 68834 : p = &(*pdn)[i];
2197 :
2198 68834 : p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2199 68834 : if (p->dsdb_dn == NULL) {
2200 1108 : return LDB_ERR_INVALID_DN_SYNTAX;
2201 : }
2202 :
2203 68834 : dn = p->dsdb_dn->dn;
2204 :
2205 68834 : status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2206 68834 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2207 68790 : unlikely(GUID_all_zero(&p->guid))) {
2208 : /* we got a DN without a GUID - go find the GUID */
2209 24319 : int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2210 24319 : if (ret != LDB_SUCCESS) {
2211 3 : char *dn_str = NULL;
2212 3 : dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2213 : (dn), 1);
2214 3 : ldb_asprintf_errstring(ldb,
2215 : "Unable to find GUID for DN %s\n",
2216 : dn_str);
2217 3 : if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2218 3 : LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2219 3 : ldb_attr_cmp(el->name, "member") == 0) {
2220 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
2221 : }
2222 0 : return ret;
2223 : }
2224 24316 : ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2225 24316 : if (ret != LDB_SUCCESS) {
2226 0 : return ret;
2227 : }
2228 44515 : } else if (!NT_STATUS_IS_OK(status)) {
2229 0 : return LDB_ERR_OPERATIONS_ERROR;
2230 : }
2231 68831 : if (i > 0 && values_are_sorted) {
2232 4336 : int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2233 4336 : if (cmp < 0) {
2234 1608 : values_are_sorted = false;
2235 : }
2236 : }
2237 : /* keep a pointer to the original ldb_val */
2238 68831 : p->v = v;
2239 : }
2240 64807 : if (! values_are_sorted) {
2241 1608 : TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2242 : }
2243 63702 : return LDB_SUCCESS;
2244 : }
2245 :
2246 : /*
2247 : * Get a series of trusted message element values. The result is sorted by
2248 : * GUID, even though the GUIDs might not be known. That works because we trust
2249 : * the database to give us the elements like that if the
2250 : * replmd_private->sorted_links flag is set.
2251 : *
2252 : * We also ensure that the links are in the Functional Level 2003
2253 : * linked attributes format.
2254 : */
2255 92154 : static int get_parsed_dns_trusted_fallback(struct ldb_module *module,
2256 : struct replmd_private *replmd_private,
2257 : TALLOC_CTX *mem_ctx,
2258 : struct ldb_message_element *el,
2259 : struct parsed_dn **pdn,
2260 : const char *ldap_oid,
2261 : struct ldb_request *parent)
2262 : {
2263 254 : int ret;
2264 92154 : if (el == NULL) {
2265 3297 : *pdn = NULL;
2266 3297 : return LDB_SUCCESS;
2267 : }
2268 :
2269 88857 : if (!replmd_private->sorted_links) {
2270 : /* We need to sort the list. This is the slow old path we want
2271 : to avoid.
2272 : */
2273 122 : ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2274 : parent);
2275 122 : if (ret != LDB_SUCCESS) {
2276 0 : return ret;
2277 : }
2278 : } else {
2279 88735 : ret = get_parsed_dns_trusted(mem_ctx, el, pdn);
2280 88735 : if (ret != LDB_SUCCESS) {
2281 0 : ldb_module_oom(module);
2282 0 : return LDB_ERR_OPERATIONS_ERROR;
2283 : }
2284 : }
2285 :
2286 : /*
2287 : * This upgrades links to FL2003 style, and sorts the result
2288 : * if that was needed.
2289 : *
2290 : * TODO: Add a database feature that asserts we have no FL2000
2291 : * style links to avoid this check or add a feature that
2292 : * uses a similar check to find sorted/unsorted links
2293 : * for an on-the-fly upgrade.
2294 : */
2295 :
2296 88857 : ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2297 : *pdn, el->num_values,
2298 : el,
2299 : ldap_oid);
2300 88857 : if (ret != LDB_SUCCESS) {
2301 0 : return ret;
2302 : }
2303 :
2304 88635 : return LDB_SUCCESS;
2305 : }
2306 :
2307 : /*
2308 : Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2309 : otherwise an error code. For compatibility the error code differs depending
2310 : on whether or not the attribute is "member".
2311 :
2312 : As always, the parsed_dn list is assumed to be sorted.
2313 : */
2314 7963 : static int check_parsed_dn_duplicates(struct ldb_module *module,
2315 : struct ldb_message_element *el,
2316 : struct parsed_dn *pdn)
2317 : {
2318 827 : unsigned int i;
2319 7963 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2320 :
2321 16148 : for (i = 1; i < el->num_values; i++) {
2322 7362 : struct parsed_dn *p = &pdn[i];
2323 7362 : if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2324 4 : ldb_asprintf_errstring(ldb,
2325 : "Linked attribute %s has "
2326 : "multiple identical values",
2327 : el->name);
2328 4 : if (ldb_attr_cmp(el->name, "member") == 0) {
2329 4 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
2330 : } else {
2331 0 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2332 : }
2333 : }
2334 : }
2335 7132 : return LDB_SUCCESS;
2336 : }
2337 :
2338 : /*
2339 : build a new extended DN, including all meta data fields
2340 :
2341 : RMD_FLAGS = DSDB_RMD_FLAG_* bits
2342 : RMD_ADDTIME = originating_add_time
2343 : RMD_INVOCID = originating_invocation_id
2344 : RMD_CHANGETIME = originating_change_time
2345 : RMD_ORIGINATING_USN = originating_usn
2346 : RMD_LOCAL_USN = local_usn
2347 : RMD_VERSION = version
2348 : */
2349 40153 : static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2350 : struct dsdb_dn *dsdb_dn,
2351 : const struct GUID *invocation_id,
2352 : uint64_t local_usn, NTTIME nttime)
2353 : {
2354 40020 : return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2355 : local_usn, local_usn, nttime,
2356 : RMD_VERSION_INITIAL, false);
2357 : }
2358 :
2359 : static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2360 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2361 : uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2362 : bool deleted);
2363 :
2364 : /*
2365 : check if any links need upgrading from w2k format
2366 : */
2367 89774 : static int replmd_check_upgrade_links(struct ldb_context *ldb,
2368 : struct parsed_dn *dns, uint32_t count,
2369 : struct ldb_message_element *el,
2370 : const char *ldap_oid)
2371 : {
2372 222 : uint32_t i;
2373 89774 : const struct GUID *invocation_id = NULL;
2374 2269214 : for (i=0; i<count; i++) {
2375 224 : NTSTATUS status;
2376 224 : uint32_t version;
2377 224 : int ret;
2378 2240022 : if (dns[i].dsdb_dn == NULL) {
2379 2239532 : ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2380 : ldap_oid);
2381 2239532 : if (ret != LDB_SUCCESS) {
2382 60582 : return LDB_ERR_INVALID_DN_SYNTAX;
2383 : }
2384 : }
2385 :
2386 2240022 : status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2387 : &version, "RMD_VERSION");
2388 2240022 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2389 : /*
2390 : * We optimistically assume they are all the same; if
2391 : * the first one is fixed, they are all fixed.
2392 : *
2393 : * If the first one was *not* fixed and we find a
2394 : * later one that is, that is an occasion to shout
2395 : * with DEBUG(0).
2396 : */
2397 60582 : if (i == 0) {
2398 60400 : return LDB_SUCCESS;
2399 : }
2400 0 : DEBUG(0, ("Mixed w2k and fixed format "
2401 : "linked attributes\n"));
2402 0 : continue;
2403 : }
2404 :
2405 2179440 : if (invocation_id == NULL) {
2406 25200 : invocation_id = samdb_ntds_invocation_id(ldb);
2407 25200 : if (invocation_id == NULL) {
2408 0 : return LDB_ERR_OPERATIONS_ERROR;
2409 : }
2410 : }
2411 :
2412 :
2413 : /* it's an old one that needs upgrading */
2414 2179440 : ret = replmd_update_la_val(el->values, dns[i].v,
2415 2179398 : dns[i].dsdb_dn, dns[i].dsdb_dn,
2416 : invocation_id, 1, 1, 0, false);
2417 2179440 : if (ret != LDB_SUCCESS) {
2418 0 : return ret;
2419 : }
2420 : }
2421 :
2422 : /*
2423 : * This sort() is critical for the operation of
2424 : * get_parsed_dns_trusted_fallback() because callers of this function
2425 : * expect a sorted list, and FL2000 style links are not
2426 : * sorted. In particular, as well as the upgrade case,
2427 : * get_parsed_dns_trusted_fallback() is called from
2428 : * replmd_delete_remove_link() even in FL2000 mode
2429 : *
2430 : * We do not normally pay the cost of the qsort() due to the
2431 : * early return in the RMD_VERSION found case.
2432 : */
2433 29192 : TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2434 29152 : return LDB_SUCCESS;
2435 : }
2436 :
2437 : /*
2438 : Sets the value for a linked attribute, including all meta data fields
2439 :
2440 : see replmd_build_la_val for value names
2441 : */
2442 2231820 : static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2443 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2444 : uint64_t usn, uint64_t local_usn, NTTIME nttime,
2445 : uint32_t version, bool deleted)
2446 : {
2447 2231820 : struct ldb_dn *dn = dsdb_dn->dn;
2448 1818 : const char *tstring, *usn_string, *flags_string;
2449 1818 : struct ldb_val tval;
2450 1818 : struct ldb_val iid;
2451 1818 : struct ldb_val usnv, local_usnv;
2452 1818 : struct ldb_val vers, flagsv;
2453 2231820 : const struct ldb_val *old_addtime = NULL;
2454 1818 : NTSTATUS status;
2455 1818 : int ret;
2456 1818 : const char *dnstring;
2457 1818 : char *vstring;
2458 2231820 : uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2459 :
2460 2231820 : tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2461 2231820 : if (!tstring) {
2462 0 : return LDB_ERR_OPERATIONS_ERROR;
2463 : }
2464 2231820 : tval = data_blob_string_const(tstring);
2465 :
2466 2231820 : usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2467 2231820 : if (!usn_string) {
2468 0 : return LDB_ERR_OPERATIONS_ERROR;
2469 : }
2470 2231820 : usnv = data_blob_string_const(usn_string);
2471 :
2472 2231820 : usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2473 2231820 : if (!usn_string) {
2474 0 : return LDB_ERR_OPERATIONS_ERROR;
2475 : }
2476 2231820 : local_usnv = data_blob_string_const(usn_string);
2477 :
2478 2231820 : status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2479 2231820 : if (!NT_STATUS_IS_OK(status)) {
2480 0 : return LDB_ERR_OPERATIONS_ERROR;
2481 : }
2482 :
2483 2231820 : flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2484 2231820 : if (!flags_string) {
2485 0 : return LDB_ERR_OPERATIONS_ERROR;
2486 : }
2487 2231820 : flagsv = data_blob_string_const(flags_string);
2488 :
2489 2231820 : ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2490 2231820 : if (ret != LDB_SUCCESS) return ret;
2491 :
2492 : /* get the ADDTIME from the original */
2493 2231820 : if (old_dsdb_dn != NULL) {
2494 2182799 : old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2495 : "RMD_ADDTIME");
2496 : }
2497 2230060 : if (old_addtime == NULL) {
2498 2226659 : old_addtime = &tval;
2499 : }
2500 4413001 : if (dsdb_dn != old_dsdb_dn ||
2501 2181181 : ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2502 2230079 : ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2503 2230079 : if (ret != LDB_SUCCESS) return ret;
2504 : }
2505 :
2506 : /* use our invocation id */
2507 2231820 : ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2508 2231820 : if (ret != LDB_SUCCESS) return ret;
2509 :
2510 : /* changetime is the current time */
2511 2231820 : ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2512 2231820 : if (ret != LDB_SUCCESS) return ret;
2513 :
2514 : /* update the USN */
2515 2231820 : ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2516 2231820 : if (ret != LDB_SUCCESS) return ret;
2517 :
2518 2231820 : ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2519 2231820 : if (ret != LDB_SUCCESS) return ret;
2520 :
2521 2231820 : vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2522 2231820 : vers = data_blob_string_const(vstring);
2523 2231820 : ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2524 2231820 : if (ret != LDB_SUCCESS) return ret;
2525 :
2526 2231820 : dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2527 2231820 : if (dnstring == NULL) {
2528 0 : return LDB_ERR_OPERATIONS_ERROR;
2529 : }
2530 2231820 : *v = data_blob_string_const(dnstring);
2531 :
2532 2231820 : return LDB_SUCCESS;
2533 : }
2534 :
2535 : /**
2536 : * Updates the value for a linked attribute, including all meta data fields
2537 : */
2538 2182753 : static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2539 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2540 : uint64_t usn, uint64_t local_usn, NTTIME nttime,
2541 : bool deleted)
2542 : {
2543 58 : uint32_t old_version;
2544 2182753 : uint32_t version = RMD_VERSION_INITIAL;
2545 58 : NTSTATUS status;
2546 :
2547 : /*
2548 : * We're updating the linked attribute locally, so increase the version
2549 : * by 1 so that other DCs will see the change when it gets replicated out
2550 : */
2551 2182753 : status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2552 : "RMD_VERSION");
2553 :
2554 2182753 : if (NT_STATUS_IS_OK(status)) {
2555 3313 : version = old_version + 1;
2556 : }
2557 :
2558 2182753 : return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2559 : usn, local_usn, nttime, version, deleted);
2560 : }
2561 :
2562 : /*
2563 : handle adding a linked attribute
2564 : */
2565 26185 : static int replmd_modify_la_add(struct ldb_module *module,
2566 : struct replmd_private *replmd_private,
2567 : struct replmd_replicated_request *ac,
2568 : struct ldb_message *msg,
2569 : struct ldb_message_element *el,
2570 : struct ldb_message_element *old_el,
2571 : const struct dsdb_attribute *schema_attr,
2572 : time_t t,
2573 : struct ldb_dn *msg_dn,
2574 : struct ldb_request *parent)
2575 : {
2576 123 : unsigned int i, j;
2577 123 : struct parsed_dn *dns, *old_dns;
2578 26185 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
2579 123 : int ret;
2580 26185 : struct ldb_val *new_values = NULL;
2581 26185 : unsigned old_num_values = old_el ? old_el->num_values : 0;
2582 26185 : unsigned num_values = 0;
2583 123 : unsigned max_num_values;
2584 26185 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2585 123 : NTTIME now;
2586 26185 : unix_to_nt_time(&now, t);
2587 :
2588 : /* get the DNs to be added, fully parsed.
2589 : *
2590 : * We need full parsing because they came off the wire and we don't
2591 : * trust them, besides which we need their details to know where to put
2592 : * them.
2593 : */
2594 26308 : ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2595 26185 : schema_attr->syntax->ldap_oid, parent);
2596 26185 : if (ret != LDB_SUCCESS) {
2597 0 : talloc_free(tmp_ctx);
2598 0 : return ret;
2599 : }
2600 :
2601 : /* get the existing DNs, lazily parsed */
2602 26308 : ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2603 : tmp_ctx, old_el, &old_dns,
2604 26185 : schema_attr->syntax->ldap_oid,
2605 : parent);
2606 :
2607 26185 : if (ret != LDB_SUCCESS) {
2608 0 : talloc_free(tmp_ctx);
2609 0 : return ret;
2610 : }
2611 :
2612 26185 : max_num_values = old_num_values + el->num_values;
2613 26185 : if (max_num_values < old_num_values) {
2614 0 : DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2615 : "old values: %u, new values: %u, sum: %u\n",
2616 : old_num_values, el->num_values, max_num_values));
2617 0 : talloc_free(tmp_ctx);
2618 0 : return LDB_ERR_OPERATIONS_ERROR;
2619 : }
2620 :
2621 26185 : new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2622 :
2623 26185 : if (new_values == NULL) {
2624 0 : ldb_module_oom(module);
2625 0 : talloc_free(tmp_ctx);
2626 0 : return LDB_ERR_OPERATIONS_ERROR;
2627 : }
2628 :
2629 : /*
2630 : * For each new value, find where it would go in the list. If there is
2631 : * a matching GUID there, we update the existing value; otherwise we
2632 : * put it in place.
2633 : */
2634 26062 : j = 0;
2635 52635 : for (i = 0; i < el->num_values; i++) {
2636 139 : struct parsed_dn *exact;
2637 139 : struct parsed_dn *next;
2638 139 : unsigned offset;
2639 26604 : int err = parsed_dn_find(ldb, old_dns, old_num_values,
2640 26465 : &dns[i].guid,
2641 26326 : dns[i].dsdb_dn->dn,
2642 26465 : dns[i].dsdb_dn->extra_part, 0,
2643 : &exact, &next,
2644 26465 : schema_attr->syntax->ldap_oid,
2645 : true);
2646 26465 : if (err != LDB_SUCCESS) {
2647 0 : talloc_free(tmp_ctx);
2648 15 : return err;
2649 : }
2650 :
2651 26465 : if (ac->fix_link_sid) {
2652 2 : char *fixed_dnstring = NULL;
2653 2 : struct dom_sid tmp_sid = { 0, };
2654 2 : DATA_BLOB sid_blob = data_blob_null;
2655 0 : enum ndr_err_code ndr_err;
2656 0 : NTSTATUS status;
2657 0 : int num;
2658 :
2659 2 : if (exact == NULL) {
2660 0 : talloc_free(tmp_ctx);
2661 0 : return ldb_operr(ldb);
2662 : }
2663 :
2664 2 : if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2665 0 : talloc_free(tmp_ctx);
2666 0 : return ldb_operr(ldb);
2667 : }
2668 :
2669 : /*
2670 : * Only "<GUID=...><SID=...>" is allowed.
2671 : *
2672 : * We get the GUID to just to find the old
2673 : * value and the SID in order to add it
2674 : * to the found value.
2675 : */
2676 :
2677 2 : num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2678 2 : if (num != 0) {
2679 0 : talloc_free(tmp_ctx);
2680 0 : return ldb_operr(ldb);
2681 : }
2682 :
2683 2 : num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2684 2 : if (num != 2) {
2685 0 : talloc_free(tmp_ctx);
2686 0 : return ldb_operr(ldb);
2687 : }
2688 :
2689 2 : status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2690 : &tmp_sid, "SID");
2691 2 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2692 : /* this is what we expect */
2693 0 : } else if (NT_STATUS_IS_OK(status)) {
2694 0 : struct GUID_txt_buf guid_str;
2695 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2696 : "i[%u] SID NOT MISSING... Attribute %s already "
2697 : "exists for target GUID %s, SID %s, DN: %s",
2698 : i, el->name,
2699 0 : GUID_buf_string(&exact->guid,
2700 : &guid_str),
2701 : dom_sid_string(tmp_ctx, &tmp_sid),
2702 : dsdb_dn_get_extended_linearized(tmp_ctx,
2703 0 : exact->dsdb_dn, 1));
2704 0 : talloc_free(tmp_ctx);
2705 0 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2706 : } else {
2707 0 : talloc_free(tmp_ctx);
2708 0 : return ldb_operr(ldb);
2709 : }
2710 :
2711 2 : status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2712 : &tmp_sid, "SID");
2713 2 : if (!NT_STATUS_IS_OK(status)) {
2714 0 : struct GUID_txt_buf guid_str;
2715 0 : ldb_asprintf_errstring(ldb,
2716 : "NO SID PROVIDED... Attribute %s already "
2717 : "exists for target GUID %s",
2718 : el->name,
2719 0 : GUID_buf_string(&exact->guid,
2720 : &guid_str));
2721 0 : talloc_free(tmp_ctx);
2722 0 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2723 : }
2724 :
2725 2 : ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2726 : (ndr_push_flags_fn_t)ndr_push_dom_sid);
2727 2 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2728 0 : talloc_free(tmp_ctx);
2729 0 : return ldb_operr(ldb);
2730 : }
2731 :
2732 2 : ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2733 2 : data_blob_free(&sid_blob);
2734 2 : if (ret != LDB_SUCCESS) {
2735 0 : talloc_free(tmp_ctx);
2736 0 : return ret;
2737 : }
2738 :
2739 2 : fixed_dnstring = dsdb_dn_get_extended_linearized(
2740 2 : new_values, exact->dsdb_dn, 1);
2741 2 : if (fixed_dnstring == NULL) {
2742 0 : talloc_free(tmp_ctx);
2743 0 : return ldb_operr(ldb);
2744 : }
2745 :
2746 : /*
2747 : * We just replace the existing value...
2748 : */
2749 2 : *exact->v = data_blob_string_const(fixed_dnstring);
2750 :
2751 2 : continue;
2752 : }
2753 :
2754 26463 : if (exact != NULL) {
2755 : /*
2756 : * We are trying to add one that exists, which is only
2757 : * allowed if it was previously deleted.
2758 : *
2759 : * When we do undelete a link we change it in place.
2760 : * It will be copied across into the right spot in due
2761 : * course.
2762 : */
2763 6 : uint32_t rmd_flags;
2764 777 : rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2765 :
2766 777 : if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2767 0 : struct GUID_txt_buf guid_str;
2768 15 : ldb_asprintf_errstring(ldb,
2769 : "Attribute %s already "
2770 : "exists for target GUID %s",
2771 : el->name,
2772 15 : GUID_buf_string(&exact->guid,
2773 : &guid_str));
2774 15 : talloc_free(tmp_ctx);
2775 : /* error codes for 'member' need to be
2776 : special cased */
2777 15 : if (ldb_attr_cmp(el->name, "member") == 0) {
2778 11 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
2779 : } else {
2780 4 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2781 : }
2782 : }
2783 :
2784 768 : ret = replmd_update_la_val(new_values, exact->v,
2785 756 : dns[i].dsdb_dn,
2786 762 : exact->dsdb_dn,
2787 762 : &ac->our_invocation_id,
2788 : ac->seq_num, ac->seq_num,
2789 : now, false);
2790 762 : if (ret != LDB_SUCCESS) {
2791 0 : talloc_free(tmp_ctx);
2792 0 : return ret;
2793 : }
2794 :
2795 762 : ret = replmd_add_backlink(module, replmd_private,
2796 : ac->schema,
2797 : msg_dn,
2798 756 : &dns[i].guid,
2799 : true,
2800 : schema_attr,
2801 : parent);
2802 762 : if (ret != LDB_SUCCESS) {
2803 0 : talloc_free(tmp_ctx);
2804 0 : return ret;
2805 : }
2806 762 : continue;
2807 : }
2808 : /*
2809 : * Here we don't have an exact match.
2810 : *
2811 : * If next is NULL, this one goes beyond the end of the
2812 : * existing list, so we need to add all of those ones first.
2813 : *
2814 : * If next is not NULL, we need to add all the ones before
2815 : * next.
2816 : */
2817 25686 : if (next == NULL) {
2818 6020 : offset = old_num_values;
2819 : } else {
2820 : /* next should have been parsed, but let's make sure */
2821 19716 : if (next->dsdb_dn == NULL) {
2822 0 : ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2823 0 : schema_attr->syntax->ldap_oid);
2824 0 : if (ret != LDB_SUCCESS) {
2825 0 : talloc_free(tmp_ctx);
2826 0 : return ret;
2827 : }
2828 : }
2829 19716 : offset = MIN(next - old_dns, old_num_values);
2830 : }
2831 :
2832 : /* put all the old ones before next on the list */
2833 1252092 : for (; j < offset; j++) {
2834 1226406 : new_values[num_values] = *old_dns[j].v;
2835 1226406 : num_values++;
2836 : }
2837 :
2838 25686 : ret = replmd_add_backlink(module, replmd_private,
2839 : ac->schema, msg_dn,
2840 25553 : &dns[i].guid,
2841 : true, schema_attr,
2842 : parent);
2843 25686 : if (ret != LDB_SUCCESS) {
2844 0 : talloc_free(tmp_ctx);
2845 0 : return ret;
2846 : }
2847 : /* Make the new linked attribute ldb_val. */
2848 25819 : ret = replmd_build_la_val(new_values, &new_values[num_values],
2849 25686 : dns[i].dsdb_dn, &ac->our_invocation_id,
2850 : ac->seq_num, now);
2851 25686 : if (ret != LDB_SUCCESS) {
2852 0 : talloc_free(tmp_ctx);
2853 0 : return ret;
2854 : }
2855 25686 : num_values++;
2856 25686 : if (ret != LDB_SUCCESS) {
2857 0 : talloc_free(tmp_ctx);
2858 0 : return ret;
2859 : }
2860 : }
2861 : /* copy the rest of the old ones (if any) */
2862 1252415 : for (; j < old_num_values; j++) {
2863 1226245 : new_values[num_values] = *old_dns[j].v;
2864 1226245 : num_values++;
2865 : }
2866 :
2867 26170 : talloc_steal(msg->elements, new_values);
2868 26170 : if (old_el != NULL) {
2869 22873 : talloc_steal(msg->elements, old_el->values);
2870 : }
2871 26170 : el->values = new_values;
2872 26170 : el->num_values = num_values;
2873 :
2874 26170 : talloc_free(tmp_ctx);
2875 :
2876 : /* we now tell the backend to replace all existing values
2877 : with the one we have constructed */
2878 26170 : el->flags = LDB_FLAG_MOD_REPLACE;
2879 :
2880 26170 : return LDB_SUCCESS;
2881 : }
2882 :
2883 :
2884 : /*
2885 : handle deleting all active linked attributes
2886 : */
2887 30420 : static int replmd_modify_la_delete(struct ldb_module *module,
2888 : struct replmd_private *replmd_private,
2889 : struct replmd_replicated_request *ac,
2890 : struct ldb_message *msg,
2891 : struct ldb_message_element *el,
2892 : struct ldb_message_element *old_el,
2893 : const struct dsdb_attribute *schema_attr,
2894 : time_t t,
2895 : struct ldb_dn *msg_dn,
2896 : struct ldb_request *parent)
2897 : {
2898 91 : unsigned int i;
2899 91 : struct parsed_dn *dns, *old_dns;
2900 30420 : TALLOC_CTX *tmp_ctx = NULL;
2901 91 : int ret;
2902 30420 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2903 30420 : struct ldb_control *vanish_links_ctrl = NULL;
2904 30420 : bool vanish_links = false;
2905 30420 : unsigned int num_to_delete = el->num_values;
2906 91 : uint32_t rmd_flags;
2907 91 : NTTIME now;
2908 :
2909 30420 : unix_to_nt_time(&now, t);
2910 :
2911 30420 : if (old_el == NULL || old_el->num_values == 0) {
2912 : /* there is nothing to delete... */
2913 243 : if (num_to_delete == 0) {
2914 : /* and we're deleting nothing, so that's OK */
2915 240 : return LDB_SUCCESS;
2916 : }
2917 3 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
2918 : }
2919 :
2920 30177 : tmp_ctx = talloc_new(msg);
2921 30177 : if (tmp_ctx == NULL) {
2922 0 : return LDB_ERR_OPERATIONS_ERROR;
2923 : }
2924 :
2925 30268 : ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2926 30177 : schema_attr->syntax->ldap_oid, parent);
2927 30177 : if (ret != LDB_SUCCESS) {
2928 3 : talloc_free(tmp_ctx);
2929 3 : return ret;
2930 : }
2931 :
2932 30265 : ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2933 : tmp_ctx, old_el, &old_dns,
2934 30174 : schema_attr->syntax->ldap_oid,
2935 : parent);
2936 :
2937 30174 : if (ret != LDB_SUCCESS) {
2938 0 : talloc_free(tmp_ctx);
2939 0 : return ret;
2940 : }
2941 :
2942 30174 : vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2943 30174 : if (vanish_links_ctrl) {
2944 28904 : vanish_links = true;
2945 28904 : vanish_links_ctrl->critical = false;
2946 : }
2947 :
2948 : /* we empty out el->values here to avoid damage if we return early. */
2949 30174 : el->num_values = 0;
2950 30174 : el->values = NULL;
2951 :
2952 : /*
2953 : * If vanish links is set, we are actually removing members of
2954 : * old_el->values; otherwise we are just marking them deleted.
2955 : *
2956 : * There is a special case when no values are given: we remove them
2957 : * all. When we have the vanish_links control we just have to remove
2958 : * the backlinks and change our element to replace the existing values
2959 : * with the empty list.
2960 : */
2961 :
2962 30174 : if (num_to_delete == 0) {
2963 15936 : for (i = 0; i < old_el->num_values; i++) {
2964 10732 : struct parsed_dn *p = &old_dns[i];
2965 10732 : if (p->dsdb_dn == NULL) {
2966 5506 : ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2967 5506 : schema_attr->syntax->ldap_oid);
2968 5506 : if (ret != LDB_SUCCESS) {
2969 0 : return ret;
2970 : }
2971 : }
2972 10732 : ret = replmd_add_backlink(module, replmd_private,
2973 : ac->schema, msg_dn, &p->guid,
2974 : false, schema_attr,
2975 : parent);
2976 10732 : if (ret != LDB_SUCCESS) {
2977 0 : talloc_free(tmp_ctx);
2978 0 : return ret;
2979 : }
2980 10732 : if (vanish_links) {
2981 10314 : continue;
2982 : }
2983 :
2984 418 : rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2985 418 : if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2986 165 : continue;
2987 : }
2988 :
2989 253 : ret = replmd_update_la_val(old_el->values, p->v,
2990 : p->dsdb_dn, p->dsdb_dn,
2991 253 : &ac->our_invocation_id,
2992 : ac->seq_num, ac->seq_num,
2993 : now, true);
2994 253 : if (ret != LDB_SUCCESS) {
2995 0 : talloc_free(tmp_ctx);
2996 0 : return ret;
2997 : }
2998 : }
2999 :
3000 5204 : if (vanish_links) {
3001 4925 : el->flags = LDB_FLAG_MOD_REPLACE;
3002 4925 : talloc_free(tmp_ctx);
3003 4925 : return LDB_SUCCESS;
3004 : }
3005 : }
3006 :
3007 :
3008 50438 : for (i = 0; i < num_to_delete; i++) {
3009 25200 : struct parsed_dn *p = &dns[i];
3010 25200 : struct parsed_dn *exact = NULL;
3011 25200 : struct parsed_dn *next = NULL;
3012 25278 : ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
3013 25200 : &p->guid,
3014 : NULL,
3015 25200 : p->dsdb_dn->extra_part, 0,
3016 : &exact, &next,
3017 25200 : schema_attr->syntax->ldap_oid,
3018 : true);
3019 25200 : if (ret != LDB_SUCCESS) {
3020 0 : talloc_free(tmp_ctx);
3021 11 : return ret;
3022 : }
3023 25200 : if (exact == NULL) {
3024 0 : struct GUID_txt_buf buf;
3025 6 : ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
3026 : "exist for target GUID %s",
3027 : el->name,
3028 6 : GUID_buf_string(&p->guid, &buf));
3029 6 : if (ldb_attr_cmp(el->name, "member") == 0) {
3030 6 : talloc_free(tmp_ctx);
3031 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
3032 : } else {
3033 0 : talloc_free(tmp_ctx);
3034 0 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
3035 : }
3036 : }
3037 :
3038 25194 : if (vanish_links) {
3039 23979 : if (CHECK_DEBUGLVL(5)) {
3040 0 : rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
3041 0 : if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3042 0 : struct GUID_txt_buf buf;
3043 0 : const char *guid_str = \
3044 0 : GUID_buf_string(&p->guid, &buf);
3045 0 : DEBUG(5, ("Deleting deleted linked "
3046 : "attribute %s to %s, because "
3047 : "vanish_links control is set\n",
3048 : el->name, guid_str));
3049 : }
3050 : }
3051 :
3052 : /* remove the backlink */
3053 23979 : ret = replmd_add_backlink(module,
3054 : replmd_private,
3055 : ac->schema,
3056 : msg_dn,
3057 : &p->guid,
3058 : false, schema_attr,
3059 : parent);
3060 23979 : if (ret != LDB_SUCCESS) {
3061 0 : talloc_free(tmp_ctx);
3062 0 : return ret;
3063 : }
3064 :
3065 : /* We flag the deletion and tidy it up later. */
3066 23979 : exact->v = NULL;
3067 23979 : continue;
3068 : }
3069 :
3070 1215 : rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
3071 :
3072 1215 : if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
3073 0 : struct GUID_txt_buf buf;
3074 5 : const char *guid_str = GUID_buf_string(&p->guid, &buf);
3075 5 : ldb_asprintf_errstring(ldb, "Attribute %s already "
3076 : "deleted for target GUID %s",
3077 : el->name, guid_str);
3078 5 : if (ldb_attr_cmp(el->name, "member") == 0) {
3079 5 : talloc_free(tmp_ctx);
3080 5 : return LDB_ERR_UNWILLING_TO_PERFORM;
3081 : } else {
3082 0 : talloc_free(tmp_ctx);
3083 0 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
3084 : }
3085 : }
3086 :
3087 1220 : ret = replmd_update_la_val(old_el->values, exact->v,
3088 1210 : exact->dsdb_dn, exact->dsdb_dn,
3089 1210 : &ac->our_invocation_id,
3090 : ac->seq_num, ac->seq_num,
3091 : now, true);
3092 1210 : if (ret != LDB_SUCCESS) {
3093 0 : talloc_free(tmp_ctx);
3094 0 : return ret;
3095 : }
3096 1210 : ret = replmd_add_backlink(module, replmd_private,
3097 : ac->schema, msg_dn,
3098 : &p->guid,
3099 : false, schema_attr,
3100 : parent);
3101 1210 : if (ret != LDB_SUCCESS) {
3102 0 : talloc_free(tmp_ctx);
3103 0 : return ret;
3104 : }
3105 : }
3106 :
3107 25238 : if (vanish_links) {
3108 23979 : unsigned j = 0;
3109 23979 : struct ldb_val *tmp_vals = NULL;
3110 :
3111 23979 : tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
3112 : old_el->num_values);
3113 23979 : if (tmp_vals == NULL) {
3114 0 : talloc_free(tmp_ctx);
3115 0 : return ldb_module_oom(module);
3116 : }
3117 2320065 : for (i = 0; i < old_el->num_values; i++) {
3118 2296086 : if (old_dns[i].v == NULL) {
3119 23979 : continue;
3120 : }
3121 2272107 : tmp_vals[j] = *old_dns[i].v;
3122 2272107 : j++;
3123 : }
3124 2296086 : for (i = 0; i < j; i++) {
3125 2272107 : old_el->values[i] = tmp_vals[i];
3126 : }
3127 23979 : old_el->num_values = j;
3128 : }
3129 :
3130 25238 : el->values = talloc_steal(msg->elements, old_el->values);
3131 25238 : el->num_values = old_el->num_values;
3132 :
3133 25238 : talloc_free(tmp_ctx);
3134 :
3135 : /* we now tell the backend to replace all existing values
3136 : with the one we have constructed */
3137 25238 : el->flags = LDB_FLAG_MOD_REPLACE;
3138 :
3139 25238 : return LDB_SUCCESS;
3140 : }
3141 :
3142 : /*
3143 : handle replacing a linked attribute
3144 : */
3145 978 : static int replmd_modify_la_replace(struct ldb_module *module,
3146 : struct replmd_private *replmd_private,
3147 : struct replmd_replicated_request *ac,
3148 : struct ldb_message *msg,
3149 : struct ldb_message_element *el,
3150 : struct ldb_message_element *old_el,
3151 : const struct dsdb_attribute *schema_attr,
3152 : time_t t,
3153 : struct ldb_dn *msg_dn,
3154 : struct ldb_request *parent)
3155 : {
3156 0 : unsigned int i, old_i, new_i;
3157 0 : struct parsed_dn *dns, *old_dns;
3158 978 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
3159 0 : int ret;
3160 978 : struct ldb_context *ldb = ldb_module_get_ctx(module);
3161 978 : struct ldb_val *new_values = NULL;
3162 978 : const char *ldap_oid = schema_attr->syntax->ldap_oid;
3163 0 : unsigned int old_num_values;
3164 0 : unsigned int repl_num_values;
3165 0 : unsigned int max_num_values;
3166 0 : NTTIME now;
3167 :
3168 978 : unix_to_nt_time(&now, t);
3169 :
3170 : /*
3171 : * The replace operation is unlike the replace and delete cases in that
3172 : * we need to look at every existing link to see whether it is being
3173 : * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3174 : *
3175 : * As we are trying to combine two sorted lists, the algorithm we use
3176 : * is akin to the merge phase of a merge sort. We interleave the two
3177 : * lists, doing different things depending on which side the current
3178 : * item came from.
3179 : *
3180 : * There are three main cases, with some sub-cases.
3181 : *
3182 : * - a DN is in the old list but not the new one. It needs to be
3183 : * marked as deleted (but left in the list).
3184 : * - maybe it is already deleted, and we have less to do.
3185 : *
3186 : * - a DN is in both lists. The old data gets replaced by the new,
3187 : * and the list doesn't grow. The old link may have been marked as
3188 : * deleted, in which case we undelete it.
3189 : *
3190 : * - a DN is in the new list only. We add it in the right place.
3191 : */
3192 :
3193 978 : old_num_values = old_el ? old_el->num_values : 0;
3194 978 : repl_num_values = el->num_values;
3195 978 : max_num_values = old_num_values + repl_num_values;
3196 :
3197 978 : if (max_num_values == 0) {
3198 : /* There is nothing to do! */
3199 59 : return LDB_SUCCESS;
3200 : }
3201 :
3202 : /*
3203 : * At the successful end of these functions el->values is
3204 : * overwritten with new_values. However get_parsed_dns()
3205 : * points p->v at the supplied el and it effectively gets used
3206 : * as a working area by replmd_build_la_val(). So we must
3207 : * duplicate it because our caller only called
3208 : * ldb_msg_copy_shallow().
3209 : */
3210 :
3211 919 : el->values = talloc_memdup(tmp_ctx,
3212 : el->values,
3213 : sizeof(el->values[0]) * el->num_values);
3214 919 : if (el->values == NULL) {
3215 0 : ldb_module_oom(module);
3216 0 : talloc_free(tmp_ctx);
3217 0 : return LDB_ERR_OPERATIONS_ERROR;
3218 : }
3219 :
3220 919 : ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3221 919 : if (ret != LDB_SUCCESS) {
3222 0 : talloc_free(tmp_ctx);
3223 0 : return ret;
3224 : }
3225 :
3226 919 : ret = check_parsed_dn_duplicates(module, el, dns);
3227 919 : if (ret != LDB_SUCCESS) {
3228 2 : talloc_free(tmp_ctx);
3229 2 : return ret;
3230 : }
3231 :
3232 917 : ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3233 : ldap_oid, parent);
3234 917 : if (ret != LDB_SUCCESS) {
3235 0 : talloc_free(tmp_ctx);
3236 0 : return ret;
3237 : }
3238 :
3239 917 : ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3240 : old_el, ldap_oid);
3241 917 : if (ret != LDB_SUCCESS) {
3242 0 : talloc_free(tmp_ctx);
3243 0 : return ret;
3244 : }
3245 :
3246 917 : new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3247 917 : if (new_values == NULL) {
3248 0 : ldb_module_oom(module);
3249 0 : talloc_free(tmp_ctx);
3250 0 : return LDB_ERR_OPERATIONS_ERROR;
3251 : }
3252 :
3253 917 : old_i = 0;
3254 917 : new_i = 0;
3255 3173 : for (i = 0; i < max_num_values; i++) {
3256 0 : int cmp;
3257 0 : struct parsed_dn *old_p, *new_p;
3258 2557 : if (old_i < old_num_values && new_i < repl_num_values) {
3259 1308 : old_p = &old_dns[old_i];
3260 1308 : new_p = &dns[new_i];
3261 1308 : cmp = parsed_dn_compare(old_p, new_p);
3262 1249 : } else if (old_i < old_num_values) {
3263 : /* the new list is empty, read the old list */
3264 330 : old_p = &old_dns[old_i];
3265 330 : new_p = NULL;
3266 330 : cmp = -1;
3267 919 : } else if (new_i < repl_num_values) {
3268 : /* the old list is empty, read new list */
3269 618 : old_p = NULL;
3270 618 : new_p = &dns[new_i];
3271 618 : cmp = 1;
3272 : } else {
3273 301 : break;
3274 : }
3275 :
3276 2256 : if (cmp < 0) {
3277 : /*
3278 : * An old ones that come before the next replacement
3279 : * (if any). We mark it as deleted and add it to the
3280 : * final list.
3281 : */
3282 704 : uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3283 704 : if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3284 270 : ret = replmd_update_la_val(new_values, old_p->v,
3285 : old_p->dsdb_dn,
3286 : old_p->dsdb_dn,
3287 270 : &ac->our_invocation_id,
3288 : ac->seq_num, ac->seq_num,
3289 : now, true);
3290 270 : if (ret != LDB_SUCCESS) {
3291 0 : talloc_free(tmp_ctx);
3292 0 : return ret;
3293 : }
3294 :
3295 270 : ret = replmd_add_backlink(module, replmd_private,
3296 : ac->schema,
3297 : msg_dn,
3298 : &old_p->guid, false,
3299 : schema_attr,
3300 : parent);
3301 270 : if (ret != LDB_SUCCESS) {
3302 0 : talloc_free(tmp_ctx);
3303 0 : return ret;
3304 : }
3305 : }
3306 704 : new_values[i] = *old_p->v;
3307 704 : old_i++;
3308 1552 : } else if (cmp == 0) {
3309 : /*
3310 : * We are overwriting one. If it was previously
3311 : * deleted, we need to add a backlink.
3312 : *
3313 : * Note that if any RMD_FLAGs in an extended new DN
3314 : * will be ignored.
3315 : */
3316 0 : uint32_t rmd_flags;
3317 :
3318 810 : ret = replmd_update_la_val(new_values, old_p->v,
3319 : new_p->dsdb_dn,
3320 : old_p->dsdb_dn,
3321 810 : &ac->our_invocation_id,
3322 : ac->seq_num, ac->seq_num,
3323 : now, false);
3324 810 : if (ret != LDB_SUCCESS) {
3325 0 : talloc_free(tmp_ctx);
3326 0 : return ret;
3327 : }
3328 :
3329 810 : rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3330 810 : if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3331 108 : ret = replmd_add_backlink(module, replmd_private,
3332 : ac->schema,
3333 : msg_dn,
3334 : &new_p->guid, true,
3335 : schema_attr,
3336 : parent);
3337 108 : if (ret != LDB_SUCCESS) {
3338 0 : talloc_free(tmp_ctx);
3339 0 : return ret;
3340 : }
3341 : }
3342 :
3343 810 : new_values[i] = *old_p->v;
3344 810 : old_i++;
3345 810 : new_i++;
3346 : } else {
3347 : /*
3348 : * Replacements that don't match an existing one. We
3349 : * just add them to the final list.
3350 : */
3351 742 : ret = replmd_build_la_val(new_values,
3352 : new_p->v,
3353 : new_p->dsdb_dn,
3354 742 : &ac->our_invocation_id,
3355 : ac->seq_num, now);
3356 742 : if (ret != LDB_SUCCESS) {
3357 0 : talloc_free(tmp_ctx);
3358 0 : return ret;
3359 : }
3360 742 : ret = replmd_add_backlink(module, replmd_private,
3361 : ac->schema,
3362 : msg_dn,
3363 : &new_p->guid, true,
3364 : schema_attr,
3365 : parent);
3366 742 : if (ret != LDB_SUCCESS) {
3367 0 : talloc_free(tmp_ctx);
3368 0 : return ret;
3369 : }
3370 742 : new_values[i] = *new_p->v;
3371 742 : new_i++;
3372 : }
3373 : }
3374 917 : if (old_el != NULL) {
3375 363 : talloc_steal(msg->elements, old_el->values);
3376 : }
3377 917 : el->values = talloc_steal(msg->elements, new_values);
3378 917 : el->num_values = i;
3379 917 : talloc_free(tmp_ctx);
3380 :
3381 917 : el->flags = LDB_FLAG_MOD_REPLACE;
3382 :
3383 917 : return LDB_SUCCESS;
3384 : }
3385 :
3386 :
3387 : /*
3388 : handle linked attributes in modify requests
3389 : */
3390 545881 : static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3391 : struct replmd_private *replmd_private,
3392 : struct replmd_replicated_request *ac,
3393 : struct ldb_message *msg,
3394 : time_t t,
3395 : struct ldb_request *parent)
3396 : {
3397 23386 : struct ldb_result *res;
3398 23386 : unsigned int i;
3399 23386 : int ret;
3400 545881 : struct ldb_context *ldb = ldb_module_get_ctx(module);
3401 23386 : struct ldb_message *old_msg;
3402 :
3403 545881 : if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3404 : /*
3405 : * Nothing special is required for modifying or vanishing links
3406 : * in fl2000 since they are just strings in a multi-valued
3407 : * attribute.
3408 : */
3409 94796 : struct ldb_control *ctrl = ldb_request_get_control(parent,
3410 : DSDB_CONTROL_REPLMD_VANISH_LINKS);
3411 94796 : if (ctrl) {
3412 1474 : ctrl->critical = false;
3413 : }
3414 94796 : return LDB_SUCCESS;
3415 : }
3416 :
3417 : /*
3418 : * TODO:
3419 : *
3420 : * We should restrict this to the intersection of the list of
3421 : * linked attributes in the schema and the list of attributes
3422 : * being modified.
3423 : *
3424 : * This will help performance a little, as otherwise we have
3425 : * to allocate the entire object value-by-value.
3426 : */
3427 451085 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3428 : DSDB_FLAG_NEXT_MODULE |
3429 : DSDB_SEARCH_SHOW_RECYCLED |
3430 : DSDB_SEARCH_REVEAL_INTERNALS |
3431 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3432 : parent);
3433 451085 : if (ret != LDB_SUCCESS) {
3434 0 : return ret;
3435 : }
3436 :
3437 451085 : old_msg = res->msgs[0];
3438 :
3439 2142037 : for (i=0; i<msg->num_elements; i++) {
3440 1690988 : struct ldb_message_element *el = &msg->elements[i];
3441 54009 : struct ldb_message_element *old_el, *new_el;
3442 1690988 : unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3443 54009 : const struct dsdb_attribute *schema_attr
3444 1690988 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3445 1690988 : if (!schema_attr) {
3446 0 : ldb_asprintf_errstring(ldb,
3447 : "%s: attribute %s is not a valid attribute in schema",
3448 : __FUNCTION__, el->name);
3449 36 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
3450 : }
3451 1690988 : if (schema_attr->linkID == 0) {
3452 1633405 : continue;
3453 : }
3454 57603 : if ((schema_attr->linkID & 1) == 1) {
3455 2 : struct ldb_control *ctrl;
3456 :
3457 20 : ctrl = ldb_request_get_control(parent,
3458 : DSDB_CONTROL_REPLMD_VANISH_LINKS);
3459 20 : if (ctrl != NULL) {
3460 7 : ctrl->critical = false;
3461 7 : continue;
3462 : }
3463 13 : ctrl = ldb_request_get_control(parent,
3464 : DSDB_CONTROL_DBCHECK);
3465 13 : if (ctrl != NULL) {
3466 13 : continue;
3467 : }
3468 :
3469 : /* Odd is for the target. Illegal to modify */
3470 0 : ldb_asprintf_errstring(ldb,
3471 : "attribute %s must not be modified directly, it is a linked attribute", el->name);
3472 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3473 : }
3474 57583 : old_el = ldb_msg_find_element(old_msg, el->name);
3475 57583 : switch (mod_type) {
3476 978 : case LDB_FLAG_MOD_REPLACE:
3477 978 : ret = replmd_modify_la_replace(module, replmd_private,
3478 : ac, msg, el, old_el,
3479 : schema_attr, t,
3480 : old_msg->dn,
3481 : parent);
3482 978 : break;
3483 30420 : case LDB_FLAG_MOD_DELETE:
3484 30420 : ret = replmd_modify_la_delete(module, replmd_private,
3485 : ac, msg, el, old_el,
3486 : schema_attr, t,
3487 : old_msg->dn,
3488 : parent);
3489 30420 : break;
3490 26185 : case LDB_FLAG_MOD_ADD:
3491 26185 : ret = replmd_modify_la_add(module, replmd_private,
3492 : ac, msg, el, old_el,
3493 : schema_attr, t,
3494 : old_msg->dn,
3495 : parent);
3496 26185 : break;
3497 0 : default:
3498 0 : ldb_asprintf_errstring(ldb,
3499 : "invalid flags 0x%x for %s linked attribute",
3500 : el->flags, el->name);
3501 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3502 : }
3503 57583 : if (ret != LDB_SUCCESS) {
3504 34 : return ret;
3505 : }
3506 57549 : ret = dsdb_check_single_valued_link(schema_attr, el);
3507 57549 : if (ret != LDB_SUCCESS) {
3508 2 : ldb_asprintf_errstring(ldb,
3509 : "Attribute %s is single valued but more than one value has been supplied",
3510 : el->name);
3511 : /* Return codes as found on Windows 2012r2 */
3512 2 : if (mod_type == LDB_FLAG_MOD_REPLACE) {
3513 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
3514 : } else {
3515 1 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3516 : }
3517 : } else {
3518 57547 : el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3519 : }
3520 :
3521 57547 : if (old_el) {
3522 53397 : ldb_msg_remove_attr(old_msg, el->name);
3523 : }
3524 57547 : ret = ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3525 57547 : if (ret != LDB_SUCCESS) {
3526 0 : return ret;
3527 : }
3528 57547 : new_el->num_values = el->num_values;
3529 57547 : new_el->values = talloc_steal(msg->elements, el->values);
3530 :
3531 : /* TODO: this relies a bit too heavily on the exact
3532 : behaviour of ldb_msg_find_element and
3533 : ldb_msg_remove_element */
3534 57547 : old_el = ldb_msg_find_element(msg, el->name);
3535 57547 : if (old_el != el) {
3536 15937 : ldb_msg_remove_element(msg, old_el);
3537 15937 : i--;
3538 : }
3539 : }
3540 :
3541 451049 : talloc_free(res);
3542 451049 : return ret;
3543 : }
3544 :
3545 :
3546 11 : static int send_rodc_referral(struct ldb_request *req,
3547 : struct ldb_context *ldb,
3548 : struct ldb_dn *dn)
3549 : {
3550 11 : char *referral = NULL;
3551 11 : struct loadparm_context *lp_ctx = NULL;
3552 11 : struct ldb_dn *fsmo_role_dn = NULL;
3553 11 : struct ldb_dn *role_owner_dn = NULL;
3554 11 : const char *domain = NULL;
3555 0 : WERROR werr;
3556 :
3557 11 : lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3558 : struct loadparm_context);
3559 :
3560 11 : werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3561 : &fsmo_role_dn, &role_owner_dn);
3562 :
3563 11 : if (W_ERROR_IS_OK(werr)) {
3564 11 : struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3565 11 : if (server_dn != NULL) {
3566 11 : ldb_dn_remove_child_components(server_dn, 1);
3567 11 : domain = samdb_dn_to_dnshostname(ldb, req,
3568 : server_dn);
3569 : }
3570 : }
3571 :
3572 11 : if (domain == NULL) {
3573 0 : domain = lpcfg_dnsdomain(lp_ctx);
3574 : }
3575 :
3576 11 : referral = talloc_asprintf(req, "ldap://%s/%s",
3577 : domain,
3578 : ldb_dn_get_linearized(dn));
3579 11 : if (referral == NULL) {
3580 0 : ldb_oom(ldb);
3581 0 : return LDB_ERR_OPERATIONS_ERROR;
3582 : }
3583 :
3584 11 : return ldb_module_send_referral(req, referral);
3585 : }
3586 :
3587 :
3588 903412 : static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3589 : {
3590 27824 : struct ldb_context *ldb;
3591 27824 : struct replmd_replicated_request *ac;
3592 27824 : struct ldb_request *down_req;
3593 27824 : struct ldb_message *msg;
3594 903412 : time_t t = time(NULL);
3595 27824 : int ret;
3596 903412 : bool is_urgent = false, rodc = false;
3597 903412 : bool is_schema_nc = false;
3598 27824 : unsigned int functional_level;
3599 903412 : const struct ldb_message_element *guid_el = NULL;
3600 27824 : struct ldb_control *sd_propagation_control;
3601 903412 : struct ldb_control *fix_links_control = NULL;
3602 903412 : struct ldb_control *fix_dn_name_control = NULL;
3603 903412 : struct ldb_control *fix_dn_sid_control = NULL;
3604 27824 : struct replmd_private *replmd_private =
3605 903412 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3606 :
3607 : /* do not manipulate our control entries */
3608 903412 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
3609 715 : return ldb_next_request(module, req);
3610 : }
3611 :
3612 902697 : sd_propagation_control = ldb_request_get_control(req,
3613 : DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3614 902697 : if (sd_propagation_control != NULL) {
3615 263905 : if (req->op.mod.message->num_elements != 1) {
3616 0 : return ldb_module_operr(module);
3617 : }
3618 263905 : ret = strcmp(req->op.mod.message->elements[0].name,
3619 : "nTSecurityDescriptor");
3620 263905 : if (ret != 0) {
3621 0 : return ldb_module_operr(module);
3622 : }
3623 :
3624 263905 : return ldb_next_request(module, req);
3625 : }
3626 :
3627 638792 : ldb = ldb_module_get_ctx(module);
3628 :
3629 638792 : fix_links_control = ldb_request_get_control(req,
3630 : DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3631 638792 : if (fix_links_control != NULL) {
3632 2 : struct dsdb_schema *schema = NULL;
3633 2 : const struct dsdb_attribute *sa = NULL;
3634 :
3635 2 : if (req->op.mod.message->num_elements != 1) {
3636 0 : return ldb_module_operr(module);
3637 : }
3638 :
3639 2 : if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_REPLACE) {
3640 0 : return ldb_module_operr(module);
3641 : }
3642 :
3643 2 : schema = dsdb_get_schema(ldb, req);
3644 2 : if (schema == NULL) {
3645 0 : return ldb_module_operr(module);
3646 : }
3647 :
3648 2 : sa = dsdb_attribute_by_lDAPDisplayName(schema,
3649 2 : req->op.mod.message->elements[0].name);
3650 2 : if (sa == NULL) {
3651 0 : return ldb_module_operr(module);
3652 : }
3653 :
3654 2 : if (sa->linkID == 0) {
3655 0 : return ldb_module_operr(module);
3656 : }
3657 :
3658 2 : fix_links_control->critical = false;
3659 2 : return ldb_next_request(module, req);
3660 : }
3661 :
3662 638790 : fix_dn_name_control = ldb_request_get_control(req,
3663 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3664 638790 : if (fix_dn_name_control != NULL) {
3665 92867 : struct dsdb_schema *schema = NULL;
3666 92867 : const struct dsdb_attribute *sa = NULL;
3667 :
3668 92867 : if (req->op.mod.message->num_elements != 2) {
3669 0 : return ldb_module_operr(module);
3670 : }
3671 :
3672 92867 : if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_DELETE) {
3673 0 : return ldb_module_operr(module);
3674 : }
3675 :
3676 92867 : if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[1].flags) != LDB_FLAG_MOD_ADD) {
3677 0 : return ldb_module_operr(module);
3678 : }
3679 :
3680 92867 : if (req->op.mod.message->elements[0].num_values != 1) {
3681 0 : return ldb_module_operr(module);
3682 : }
3683 :
3684 92867 : if (req->op.mod.message->elements[1].num_values != 1) {
3685 0 : return ldb_module_operr(module);
3686 : }
3687 :
3688 92867 : schema = dsdb_get_schema(ldb, req);
3689 92867 : if (schema == NULL) {
3690 0 : return ldb_module_operr(module);
3691 : }
3692 :
3693 92867 : if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3694 : req->op.mod.message->elements[1].name) != 0) {
3695 0 : return ldb_module_operr(module);
3696 : }
3697 :
3698 92867 : sa = dsdb_attribute_by_lDAPDisplayName(schema,
3699 92858 : req->op.mod.message->elements[0].name);
3700 92867 : if (sa == NULL) {
3701 0 : return ldb_module_operr(module);
3702 : }
3703 :
3704 92867 : if (sa->dn_format == DSDB_INVALID_DN) {
3705 0 : return ldb_module_operr(module);
3706 : }
3707 :
3708 92867 : if (sa->linkID != 0) {
3709 0 : return ldb_module_operr(module);
3710 : }
3711 :
3712 : /*
3713 : * If we are run from dbcheck and we are not updating
3714 : * a link (as these would need to be sorted and so
3715 : * can't go via such a simple update, then do not
3716 : * trigger replicated updates and a new USN from this
3717 : * change, it wasn't a real change, just a new
3718 : * (correct) string DN
3719 : */
3720 :
3721 92867 : fix_dn_name_control->critical = false;
3722 92867 : return ldb_next_request(module, req);
3723 : }
3724 :
3725 545923 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3726 :
3727 545923 : guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3728 545923 : if (guid_el != NULL) {
3729 0 : ldb_set_errstring(ldb,
3730 : "replmd_modify: it's not allowed to change the objectGUID!");
3731 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
3732 : }
3733 :
3734 545923 : ac = replmd_ctx_init(module, req);
3735 545923 : if (ac == NULL) {
3736 0 : return ldb_module_oom(module);
3737 : }
3738 :
3739 545923 : functional_level = dsdb_functional_level(ldb);
3740 :
3741 : /* we have to copy the message as the caller might have it as a const */
3742 545923 : msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3743 545923 : if (msg == NULL) {
3744 0 : ldb_oom(ldb);
3745 0 : talloc_free(ac);
3746 0 : return LDB_ERR_OPERATIONS_ERROR;
3747 : }
3748 :
3749 545923 : fix_dn_sid_control = ldb_request_get_control(req,
3750 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3751 545923 : if (fix_dn_sid_control != NULL) {
3752 2 : const struct dsdb_attribute *sa = NULL;
3753 :
3754 2 : if (msg->num_elements != 1) {
3755 0 : talloc_free(ac);
3756 0 : return ldb_module_operr(module);
3757 : }
3758 :
3759 2 : if (LDB_FLAG_MOD_TYPE(msg->elements[0].flags) != LDB_FLAG_MOD_ADD) {
3760 0 : talloc_free(ac);
3761 0 : return ldb_module_operr(module);
3762 : }
3763 :
3764 2 : if (msg->elements[0].num_values != 1) {
3765 0 : talloc_free(ac);
3766 0 : return ldb_module_operr(module);
3767 : }
3768 :
3769 2 : sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3770 2 : msg->elements[0].name);
3771 2 : if (sa == NULL) {
3772 0 : talloc_free(ac);
3773 0 : return ldb_module_operr(module);
3774 : }
3775 :
3776 2 : if (sa->dn_format != DSDB_NORMAL_DN) {
3777 0 : talloc_free(ac);
3778 0 : return ldb_module_operr(module);
3779 : }
3780 :
3781 2 : fix_dn_sid_control->critical = false;
3782 2 : ac->fix_link_sid = true;
3783 :
3784 2 : goto handle_linked_attribs;
3785 : }
3786 :
3787 545921 : ldb_msg_remove_attr(msg, "whenChanged");
3788 545921 : ldb_msg_remove_attr(msg, "uSNChanged");
3789 :
3790 545921 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3791 :
3792 545921 : ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3793 : msg, &ac->seq_num, t, is_schema_nc,
3794 : &is_urgent, &rodc);
3795 545921 : if (rodc && (ret == LDB_ERR_REFERRAL)) {
3796 11 : ret = send_rodc_referral(req, ldb, msg->dn);
3797 11 : talloc_free(ac);
3798 11 : return ret;
3799 :
3800 : }
3801 :
3802 545910 : if (ret != LDB_SUCCESS) {
3803 31 : talloc_free(ac);
3804 31 : return ret;
3805 : }
3806 :
3807 545879 : handle_linked_attribs:
3808 545881 : ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3809 : ac, msg, t, req);
3810 545881 : if (ret != LDB_SUCCESS) {
3811 36 : talloc_free(ac);
3812 36 : return ret;
3813 : }
3814 :
3815 : /* TODO:
3816 : * - replace the old object with the newly constructed one
3817 : */
3818 :
3819 545845 : ac->is_urgent = is_urgent;
3820 :
3821 545845 : ret = ldb_build_mod_req(&down_req, ldb, ac,
3822 : msg,
3823 : req->controls,
3824 : ac, replmd_op_callback,
3825 : req);
3826 545845 : LDB_REQ_SET_LOCATION(down_req);
3827 545845 : if (ret != LDB_SUCCESS) {
3828 0 : talloc_free(ac);
3829 0 : return ret;
3830 : }
3831 :
3832 : /* current partition control is needed by "replmd_op_callback" */
3833 545845 : if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3834 545845 : ret = ldb_request_add_control(down_req,
3835 : DSDB_CONTROL_CURRENT_PARTITION_OID,
3836 : false, NULL);
3837 545845 : if (ret != LDB_SUCCESS) {
3838 0 : talloc_free(ac);
3839 0 : return ret;
3840 : }
3841 : }
3842 :
3843 : /* If we are in functional level 2000, then
3844 : * replmd_modify_handle_linked_attribs will have done
3845 : * nothing */
3846 545845 : if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3847 94796 : ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3848 94796 : if (ret != LDB_SUCCESS) {
3849 0 : talloc_free(ac);
3850 0 : return ret;
3851 : }
3852 : }
3853 :
3854 545845 : talloc_steal(down_req, msg);
3855 :
3856 : /* we only change whenChanged and uSNChanged if the seq_num
3857 : has changed */
3858 545845 : if (ac->seq_num != 0) {
3859 250625 : ret = add_time_element(msg, "whenChanged", t);
3860 250625 : if (ret != LDB_SUCCESS) {
3861 0 : talloc_free(ac);
3862 0 : ldb_operr(ldb);
3863 0 : return ret;
3864 : }
3865 :
3866 250625 : ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3867 250625 : if (ret != LDB_SUCCESS) {
3868 0 : talloc_free(ac);
3869 0 : ldb_operr(ldb);
3870 0 : return ret;
3871 : }
3872 : }
3873 :
3874 : /* go on with the call chain */
3875 545845 : return ldb_next_request(module, down_req);
3876 : }
3877 :
3878 : static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3879 :
3880 : /*
3881 : handle a rename request
3882 :
3883 : On a rename we need to do an extra ldb_modify which sets the
3884 : whenChanged and uSNChanged attributes. We do this in a callback after the success.
3885 : */
3886 1496 : static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3887 : {
3888 5 : struct ldb_context *ldb;
3889 1496 : struct ldb_control *fix_dn_name_control = NULL;
3890 5 : struct replmd_replicated_request *ac;
3891 5 : int ret;
3892 5 : struct ldb_request *down_req;
3893 :
3894 : /* do not manipulate our control entries */
3895 1496 : if (ldb_dn_is_special(req->op.rename.olddn)) {
3896 0 : return ldb_next_request(module, req);
3897 : }
3898 :
3899 1496 : fix_dn_name_control = ldb_request_get_control(req,
3900 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3901 1496 : if (fix_dn_name_control != NULL) {
3902 1 : return ldb_next_request(module, req);
3903 : }
3904 :
3905 1495 : ldb = ldb_module_get_ctx(module);
3906 :
3907 1495 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3908 :
3909 1495 : ac = replmd_ctx_init(module, req);
3910 1495 : if (ac == NULL) {
3911 0 : return ldb_module_oom(module);
3912 : }
3913 :
3914 1495 : ret = ldb_build_rename_req(&down_req, ldb, ac,
3915 1490 : ac->req->op.rename.olddn,
3916 1490 : ac->req->op.rename.newdn,
3917 1490 : ac->req->controls,
3918 : ac, replmd_rename_callback,
3919 : ac->req);
3920 1495 : LDB_REQ_SET_LOCATION(down_req);
3921 1495 : if (ret != LDB_SUCCESS) {
3922 0 : talloc_free(ac);
3923 0 : return ret;
3924 : }
3925 :
3926 : /* go on with the call chain */
3927 1495 : return ldb_next_request(module, down_req);
3928 : }
3929 :
3930 : /* After the rename is completed, update the whenchanged etc */
3931 1495 : static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3932 : {
3933 5 : struct ldb_context *ldb;
3934 5 : struct ldb_request *down_req;
3935 5 : struct ldb_message *msg;
3936 5 : const struct dsdb_attribute *rdn_attr;
3937 5 : const char *rdn_name;
3938 5 : const struct ldb_val *rdn_val;
3939 1495 : const char *attrs[5] = { NULL, };
3940 1495 : time_t t = time(NULL);
3941 5 : int ret;
3942 1495 : bool is_urgent = false, rodc = false;
3943 5 : bool is_schema_nc;
3944 5 : struct replmd_replicated_request *ac =
3945 1495 : talloc_get_type(req->context, struct replmd_replicated_request);
3946 5 : struct replmd_private *replmd_private =
3947 1495 : talloc_get_type(ldb_module_get_private(ac->module),
3948 : struct replmd_private);
3949 :
3950 1495 : ldb = ldb_module_get_ctx(ac->module);
3951 :
3952 1495 : if (ares->error != LDB_SUCCESS) {
3953 4 : return ldb_module_done(ac->req, ares->controls,
3954 : ares->response, ares->error);
3955 : }
3956 :
3957 1491 : if (ares->type != LDB_REPLY_DONE) {
3958 0 : ldb_set_errstring(ldb,
3959 : "invalid reply type in repl_meta_data rename callback");
3960 0 : talloc_free(ares);
3961 0 : return ldb_module_done(ac->req, NULL, NULL,
3962 : LDB_ERR_OPERATIONS_ERROR);
3963 : }
3964 :
3965 : /* TODO:
3966 : * - replace the old object with the newly constructed one
3967 : */
3968 :
3969 1491 : msg = ldb_msg_new(ac);
3970 1491 : if (msg == NULL) {
3971 0 : ldb_oom(ldb);
3972 0 : return LDB_ERR_OPERATIONS_ERROR;
3973 : }
3974 :
3975 1491 : msg->dn = ac->req->op.rename.newdn;
3976 :
3977 1491 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3978 :
3979 1491 : rdn_name = ldb_dn_get_rdn_name(msg->dn);
3980 1491 : if (rdn_name == NULL) {
3981 0 : talloc_free(ares);
3982 0 : return ldb_module_done(ac->req, NULL, NULL,
3983 : ldb_operr(ldb));
3984 : }
3985 :
3986 : /* normalize the rdn attribute name */
3987 1491 : rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3988 1491 : if (rdn_attr == NULL) {
3989 0 : talloc_free(ares);
3990 0 : return ldb_module_done(ac->req, NULL, NULL,
3991 : ldb_operr(ldb));
3992 : }
3993 1491 : rdn_name = rdn_attr->lDAPDisplayName;
3994 :
3995 1491 : rdn_val = ldb_dn_get_rdn_val(msg->dn);
3996 1491 : if (rdn_val == NULL) {
3997 0 : talloc_free(ares);
3998 0 : return ldb_module_done(ac->req, NULL, NULL,
3999 : ldb_operr(ldb));
4000 : }
4001 :
4002 1491 : if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
4003 0 : talloc_free(ares);
4004 0 : return ldb_module_done(ac->req, NULL, NULL,
4005 : ldb_oom(ldb));
4006 : }
4007 1491 : if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
4008 0 : talloc_free(ares);
4009 0 : return ldb_module_done(ac->req, NULL, NULL,
4010 : ldb_oom(ldb));
4011 : }
4012 :
4013 : /*
4014 : * here we let replmd_update_rpmd() only search for
4015 : * the existing "replPropertyMetaData" and rdn_name attributes.
4016 : *
4017 : * We do not want the existing "name" attribute as
4018 : * the "name" attribute needs to get the version
4019 : * updated on rename even if the rdn value hasn't changed.
4020 : *
4021 : * This is the diff of the meta data, for a moved user
4022 : * on a w2k8r2 server:
4023 : *
4024 : * # record 1
4025 : * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
4026 : * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
4027 : * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
4028 : * version : 0x00000001 (1)
4029 : * reserved : 0x00000000 (0)
4030 : * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
4031 : * local_usn : 0x00000000000037a5 (14245)
4032 : * array: struct replPropertyMetaData1
4033 : * attid : DRSUAPI_ATTID_name (0x90001)
4034 : * - version : 0x00000001 (1)
4035 : * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
4036 : * + version : 0x00000002 (2)
4037 : * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
4038 : * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
4039 : * - originating_usn : 0x00000000000037a5 (14245)
4040 : * - local_usn : 0x00000000000037a5 (14245)
4041 : * + originating_usn : 0x0000000000003834 (14388)
4042 : * + local_usn : 0x0000000000003834 (14388)
4043 : * array: struct replPropertyMetaData1
4044 : * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
4045 : * version : 0x00000004 (4)
4046 : */
4047 1491 : attrs[0] = "replPropertyMetaData";
4048 1491 : attrs[1] = "objectClass";
4049 1491 : attrs[2] = "instanceType";
4050 1491 : attrs[3] = rdn_name;
4051 1491 : attrs[4] = NULL;
4052 :
4053 1491 : ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
4054 : msg, &ac->seq_num, t,
4055 : is_schema_nc, &is_urgent, &rodc);
4056 1491 : if (rodc && (ret == LDB_ERR_REFERRAL)) {
4057 0 : ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
4058 0 : talloc_free(ares);
4059 0 : return ldb_module_done(req, NULL, NULL, ret);
4060 : }
4061 :
4062 1491 : if (ret != LDB_SUCCESS) {
4063 0 : talloc_free(ares);
4064 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
4065 : }
4066 :
4067 1491 : if (ac->seq_num == 0) {
4068 0 : talloc_free(ares);
4069 0 : return ldb_module_done(ac->req, NULL, NULL,
4070 : ldb_error(ldb, ret,
4071 : "internal error seq_num == 0"));
4072 : }
4073 1491 : ac->is_urgent = is_urgent;
4074 :
4075 1491 : ret = ldb_build_mod_req(&down_req, ldb, ac,
4076 : msg,
4077 : req->controls,
4078 : ac, replmd_op_callback,
4079 : req);
4080 1491 : LDB_REQ_SET_LOCATION(down_req);
4081 1491 : if (ret != LDB_SUCCESS) {
4082 0 : talloc_free(ac);
4083 0 : return ret;
4084 : }
4085 :
4086 : /* current partition control is needed by "replmd_op_callback" */
4087 1491 : if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
4088 1491 : ret = ldb_request_add_control(down_req,
4089 : DSDB_CONTROL_CURRENT_PARTITION_OID,
4090 : false, NULL);
4091 1491 : if (ret != LDB_SUCCESS) {
4092 0 : talloc_free(ac);
4093 0 : return ret;
4094 : }
4095 : }
4096 :
4097 1491 : talloc_steal(down_req, msg);
4098 :
4099 1491 : ret = add_time_element(msg, "whenChanged", t);
4100 1491 : if (ret != LDB_SUCCESS) {
4101 0 : talloc_free(ac);
4102 0 : ldb_operr(ldb);
4103 0 : return ret;
4104 : }
4105 :
4106 1491 : ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
4107 1491 : if (ret != LDB_SUCCESS) {
4108 0 : talloc_free(ac);
4109 0 : ldb_operr(ldb);
4110 0 : return ret;
4111 : }
4112 :
4113 : /* go on with the call chain - do the modify after the rename */
4114 1491 : return ldb_next_request(ac->module, down_req);
4115 : }
4116 :
4117 : /*
4118 : * remove links from objects that point at this object when an object
4119 : * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
4120 : * RemoveObj which states that link removal due to the object being
4121 : * deleted is NOT an originating update - they just go away!
4122 : *
4123 : */
4124 9661 : static int replmd_delete_remove_link(struct ldb_module *module,
4125 : const struct dsdb_schema *schema,
4126 : struct replmd_private *replmd_private,
4127 : struct ldb_dn *dn,
4128 : struct GUID *guid,
4129 : struct ldb_message_element *el,
4130 : const struct dsdb_attribute *sa,
4131 : struct ldb_request *parent,
4132 : bool *caller_should_vanish)
4133 : {
4134 40 : unsigned int i;
4135 9661 : TALLOC_CTX *tmp_ctx = talloc_new(module);
4136 9661 : struct ldb_context *ldb = ldb_module_get_ctx(module);
4137 :
4138 34905 : for (i=0; i<el->num_values; i++) {
4139 41 : struct dsdb_dn *dsdb_dn;
4140 41 : int ret;
4141 41 : struct ldb_message *msg;
4142 41 : const struct dsdb_attribute *target_attr;
4143 41 : struct ldb_message_element *el2;
4144 41 : const char *dn_str;
4145 41 : struct ldb_val dn_val;
4146 25204 : uint32_t dsdb_flags = 0;
4147 25204 : const char *attrs[] = { NULL, NULL };
4148 41 : struct ldb_result *link_res;
4149 41 : struct ldb_message *link_msg;
4150 41 : struct ldb_message_element *link_el;
4151 41 : struct parsed_dn *link_dns;
4152 25204 : struct parsed_dn *p = NULL, *unused = NULL;
4153 :
4154 25204 : if (dsdb_dn_is_deleted_val(&el->values[i])) {
4155 45 : continue;
4156 : }
4157 :
4158 25204 : dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4159 25204 : if (!dsdb_dn) {
4160 0 : talloc_free(tmp_ctx);
4161 0 : return LDB_ERR_OPERATIONS_ERROR;
4162 : }
4163 :
4164 : /* remove the link */
4165 25204 : msg = ldb_msg_new(tmp_ctx);
4166 25204 : if (!msg) {
4167 0 : ldb_module_oom(module);
4168 0 : talloc_free(tmp_ctx);
4169 0 : return LDB_ERR_OPERATIONS_ERROR;
4170 : }
4171 :
4172 25204 : msg->dn = dsdb_dn->dn;
4173 :
4174 25204 : target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4175 25204 : if (target_attr == NULL) {
4176 0 : continue;
4177 : }
4178 25204 : attrs[0] = target_attr->lDAPDisplayName;
4179 :
4180 25204 : ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4181 : LDB_FLAG_MOD_DELETE, &el2);
4182 25204 : if (ret != LDB_SUCCESS) {
4183 0 : ldb_module_oom(module);
4184 0 : talloc_free(tmp_ctx);
4185 0 : return LDB_ERR_OPERATIONS_ERROR;
4186 : }
4187 :
4188 25204 : ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4189 : msg->dn, attrs,
4190 : DSDB_FLAG_NEXT_MODULE |
4191 : DSDB_SEARCH_SHOW_EXTENDED_DN |
4192 : DSDB_SEARCH_SHOW_RECYCLED,
4193 : parent);
4194 :
4195 25204 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4196 2 : DBG_WARNING("Failed to find forward link object %s "
4197 : "to remove backlink %s on %s\n",
4198 : ldb_dn_get_linearized(msg->dn),
4199 : sa->lDAPDisplayName,
4200 : ldb_dn_get_linearized(dn));
4201 2 : *caller_should_vanish = true;
4202 2 : continue;
4203 : }
4204 :
4205 25202 : if (ret != LDB_SUCCESS) {
4206 0 : talloc_free(tmp_ctx);
4207 0 : return ret;
4208 : }
4209 :
4210 25202 : link_msg = link_res->msgs[0];
4211 25243 : link_el = ldb_msg_find_element(link_msg,
4212 25202 : target_attr->lDAPDisplayName);
4213 25202 : if (link_el == NULL) {
4214 2 : DBG_WARNING("Failed to find forward link on %s "
4215 : "as %s to remove backlink %s on %s\n",
4216 : ldb_dn_get_linearized(msg->dn),
4217 : target_attr->lDAPDisplayName,
4218 : sa->lDAPDisplayName,
4219 : ldb_dn_get_linearized(dn));
4220 2 : *caller_should_vanish = true;
4221 2 : continue;
4222 : }
4223 :
4224 : /*
4225 : * This call 'upgrades' the links in link_dns, but we
4226 : * do not commit the result back into the database, so
4227 : * this is safe to call in FL2000 or on databases that
4228 : * have been run at that level in the past.
4229 : */
4230 25240 : ret = get_parsed_dns_trusted_fallback(module, replmd_private,
4231 : tmp_ctx,
4232 : link_el, &link_dns,
4233 25200 : target_attr->syntax->ldap_oid,
4234 : parent);
4235 25200 : if (ret != LDB_SUCCESS) {
4236 0 : talloc_free(tmp_ctx);
4237 0 : return ret;
4238 : }
4239 :
4240 25240 : ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4241 : guid, dn,
4242 : data_blob_null, 0,
4243 : &p, &unused,
4244 25200 : target_attr->syntax->ldap_oid, false);
4245 25200 : if (ret != LDB_SUCCESS) {
4246 0 : talloc_free(tmp_ctx);
4247 0 : return ret;
4248 : }
4249 :
4250 25200 : if (p == NULL) {
4251 3 : DBG_WARNING("Failed to find forward link on %s "
4252 : "as %s to remove backlink %s on %s\n",
4253 : ldb_dn_get_linearized(msg->dn),
4254 : target_attr->lDAPDisplayName,
4255 : sa->lDAPDisplayName,
4256 : ldb_dn_get_linearized(dn));
4257 3 : *caller_should_vanish = true;
4258 3 : continue;
4259 : }
4260 :
4261 : /*
4262 : * If we find a backlink to ourself, we will delete
4263 : * the forward link before we get to process that
4264 : * properly, so just let the caller process this via
4265 : * the forward link.
4266 : *
4267 : * We do this once we are sure we have the forward
4268 : * link (to ourself) in case something is very wrong
4269 : * and they are out of sync.
4270 : */
4271 25197 : if (ldb_dn_compare(dsdb_dn->dn, dn) == 0) {
4272 38 : continue;
4273 : }
4274 :
4275 : /* This needs to get the Binary DN, by first searching */
4276 25198 : dn_str = dsdb_dn_get_linearized(tmp_ctx,
4277 25159 : p->dsdb_dn);
4278 :
4279 25159 : dn_val = data_blob_string_const(dn_str);
4280 25159 : el2->values = &dn_val;
4281 25159 : el2->num_values = 1;
4282 :
4283 : /*
4284 : * Ensure that we tell the modification to vanish any linked
4285 : * attributes (not simply mark them as isDeleted = TRUE)
4286 : */
4287 25159 : dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4288 :
4289 25159 : ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4290 25159 : if (ret != LDB_SUCCESS) {
4291 0 : talloc_free(tmp_ctx);
4292 0 : return ret;
4293 : }
4294 : }
4295 9661 : talloc_free(tmp_ctx);
4296 9661 : return LDB_SUCCESS;
4297 : }
4298 :
4299 :
4300 : /*
4301 : handle update of replication meta data for deletion of objects
4302 :
4303 : This also handles the mapping of delete to a rename operation
4304 : to allow deletes to be replicated.
4305 :
4306 : It also handles the incoming deleted objects, to ensure they are
4307 : fully deleted here. In that case re_delete is true, and we do not
4308 : use this as a signal to change the deleted state, just reinforce it.
4309 :
4310 : */
4311 224109 : static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4312 : {
4313 224109 : int ret = LDB_ERR_OTHER;
4314 148 : bool retb, disallow_move_on_delete;
4315 224109 : struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4316 148 : const char *rdn_name;
4317 148 : const struct ldb_val *rdn_value, *new_rdn_value;
4318 148 : struct GUID guid;
4319 224109 : struct ldb_context *ldb = ldb_module_get_ctx(module);
4320 148 : const struct dsdb_schema *schema;
4321 148 : struct ldb_message *msg, *old_msg;
4322 148 : struct ldb_message_element *el;
4323 148 : TALLOC_CTX *tmp_ctx;
4324 148 : struct ldb_result *res, *parent_res;
4325 148 : static const char * const preserved_attrs[] = {
4326 : /*
4327 : * This list MUST be kept in case-insensitive sorted order,
4328 : * as we use it in a binary search with ldb_attr_cmp().
4329 : *
4330 : * We get this hard-coded list from
4331 : * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4332 : */
4333 : "attributeID",
4334 : "attributeSyntax",
4335 : "distinguishedName",
4336 : "dNReferenceUpdate",
4337 : "dNSHostName",
4338 : "flatName",
4339 : "governsID",
4340 : "groupType",
4341 : "instanceType",
4342 : "isDeleted",
4343 : "isRecycled",
4344 : "lastKnownParent",
4345 : "lDAPDisplayName",
4346 : "legacyExchangeDN",
4347 : "mS-DS-CreatorSID",
4348 : "msDS-LastKnownRDN",
4349 : "msDS-PortLDAP",
4350 : "mSMQOwnerID",
4351 : "name",
4352 : "nCName",
4353 : "nTSecurityDescriptor",
4354 : "objectClass",
4355 : "objectGUID",
4356 : "objectSid",
4357 : "oMSyntax",
4358 : "proxiedObjectName",
4359 : "replPropertyMetaData",
4360 : "sAMAccountName",
4361 : "securityIdentifier",
4362 : "sIDHistory",
4363 : "subClassOf",
4364 : "systemFlags",
4365 : "trustAttributes",
4366 : "trustDirection",
4367 : "trustPartner",
4368 : "trustType",
4369 : "userAccountControl",
4370 : "uSNChanged",
4371 : "uSNCreated",
4372 : "whenChanged",
4373 : "whenCreated",
4374 : /*
4375 : * DO NOT JUST APPEND TO THIS LIST.
4376 : *
4377 : * In case you missed the note at the top, this list is kept
4378 : * in case-insensitive sorted order. In the unlikely event you
4379 : * need to add an attribute, please add it in the RIGHT PLACE.
4380 : */
4381 : };
4382 148 : static const char * const all_attrs[] = {
4383 : DSDB_SECRET_ATTRIBUTES,
4384 : "*",
4385 : NULL
4386 : };
4387 148 : static const struct ldb_val true_val = {
4388 : .data = discard_const_p(uint8_t, "TRUE"),
4389 : .length = 4
4390 : };
4391 :
4392 148 : unsigned int i;
4393 224109 : uint32_t dsdb_flags = 0;
4394 148 : struct replmd_private *replmd_private;
4395 148 : enum deletion_state deletion_state, next_deletion_state;
4396 :
4397 224109 : if (ldb_dn_is_special(req->op.del.dn)) {
4398 1 : return ldb_next_request(module, req);
4399 : }
4400 :
4401 : /*
4402 : * We have to allow dbcheck to remove an object that
4403 : * is beyond repair, and to do so totally. This could
4404 : * mean we we can get a partial object from the other
4405 : * DC, causing havoc, so dbcheck suggests
4406 : * re-replication first. dbcheck sets both DBCHECK
4407 : * and RELAX in this situation.
4408 : */
4409 224108 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4410 25 : && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4411 : /* really, really remove it */
4412 3 : return ldb_next_request(module, req);
4413 : }
4414 :
4415 224105 : tmp_ctx = talloc_new(ldb);
4416 224105 : if (!tmp_ctx) {
4417 0 : ldb_oom(ldb);
4418 0 : return LDB_ERR_OPERATIONS_ERROR;
4419 : }
4420 :
4421 224105 : schema = dsdb_get_schema(ldb, tmp_ctx);
4422 224105 : if (!schema) {
4423 0 : talloc_free(tmp_ctx);
4424 0 : return LDB_ERR_OPERATIONS_ERROR;
4425 : }
4426 :
4427 224105 : old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4428 :
4429 : /* we need the complete msg off disk, so we can work out which
4430 : attributes need to be removed */
4431 224105 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4432 : DSDB_FLAG_NEXT_MODULE |
4433 : DSDB_SEARCH_SHOW_RECYCLED |
4434 : DSDB_SEARCH_REVEAL_INTERNALS |
4435 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4436 224105 : if (ret != LDB_SUCCESS) {
4437 0 : ldb_asprintf_errstring(ldb_module_get_ctx(module),
4438 : "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4439 : re_delete ? "re-delete" : "delete",
4440 : ldb_dn_get_linearized(old_dn),
4441 : ldb_errstring(ldb_module_get_ctx(module)));
4442 0 : talloc_free(tmp_ctx);
4443 0 : return ret;
4444 : }
4445 224105 : old_msg = res->msgs[0];
4446 :
4447 224105 : replmd_deletion_state(module, old_msg,
4448 : &deletion_state,
4449 : &next_deletion_state);
4450 :
4451 : /* This supports us noticing an incoming isDeleted and acting on it */
4452 224105 : if (re_delete) {
4453 151290 : SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4454 151290 : next_deletion_state = deletion_state;
4455 : }
4456 :
4457 224105 : if (next_deletion_state == OBJECT_REMOVED) {
4458 : /*
4459 : * We have to prevent objects being deleted, even if
4460 : * the administrator really wants them gone, as
4461 : * without the tombstone, we can get a partial object
4462 : * from the other DC, causing havoc.
4463 : *
4464 : * The only other valid case is when the 180 day
4465 : * timeout has expired, when relax is specified.
4466 : */
4467 13 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4468 : /* it is already deleted - really remove it this time */
4469 13 : talloc_free(tmp_ctx);
4470 13 : return ldb_next_request(module, req);
4471 : }
4472 :
4473 0 : ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4474 : "This check is to prevent corruption of the replicated state.",
4475 : ldb_dn_get_linearized(old_msg->dn));
4476 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4477 : }
4478 :
4479 224092 : rdn_name = ldb_dn_get_rdn_name(old_dn);
4480 224092 : rdn_value = ldb_dn_get_rdn_val(old_dn);
4481 224092 : if ((rdn_name == NULL) || (rdn_value == NULL)) {
4482 0 : talloc_free(tmp_ctx);
4483 0 : return ldb_operr(ldb);
4484 : }
4485 :
4486 224092 : msg = ldb_msg_new(tmp_ctx);
4487 224092 : if (msg == NULL) {
4488 0 : ldb_module_oom(module);
4489 0 : talloc_free(tmp_ctx);
4490 0 : return LDB_ERR_OPERATIONS_ERROR;
4491 : }
4492 :
4493 224092 : msg->dn = old_dn;
4494 :
4495 : /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4496 224231 : disallow_move_on_delete =
4497 224092 : (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4498 224092 : & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4499 :
4500 : /* work out where we will be renaming this object to */
4501 224092 : if (!disallow_move_on_delete) {
4502 97 : struct ldb_dn *deleted_objects_dn;
4503 221148 : ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4504 : &deleted_objects_dn);
4505 :
4506 : /*
4507 : * We should not move objects if we can't find the
4508 : * deleted objects DN. Not moving (or otherwise
4509 : * harming) the Deleted Objects DN itself is handled
4510 : * in the caller.
4511 : */
4512 221148 : if (re_delete && (ret != LDB_SUCCESS)) {
4513 0 : new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4514 0 : if (new_dn == NULL) {
4515 0 : ldb_module_oom(module);
4516 0 : talloc_free(tmp_ctx);
4517 0 : return LDB_ERR_OPERATIONS_ERROR;
4518 : }
4519 221148 : } else if (ret != LDB_SUCCESS) {
4520 : /* this is probably an attempted delete on a partition
4521 : * that doesn't allow delete operations, such as the
4522 : * schema partition */
4523 0 : ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4524 : ldb_dn_get_linearized(old_dn));
4525 0 : talloc_free(tmp_ctx);
4526 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4527 : } else {
4528 221148 : new_dn = deleted_objects_dn;
4529 : }
4530 : } else {
4531 2944 : new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4532 2944 : if (new_dn == NULL) {
4533 0 : ldb_module_oom(module);
4534 0 : talloc_free(tmp_ctx);
4535 0 : return LDB_ERR_OPERATIONS_ERROR;
4536 : }
4537 : }
4538 :
4539 : /* get the objects GUID from the search we just did */
4540 224092 : guid = samdb_result_guid(old_msg, "objectGUID");
4541 :
4542 224092 : if (deletion_state == OBJECT_NOT_DELETED) {
4543 139 : struct ldb_message_element *is_deleted_el;
4544 :
4545 72941 : ret = replmd_make_deleted_child_dn(tmp_ctx,
4546 : ldb,
4547 : new_dn,
4548 : rdn_name, rdn_value,
4549 : guid);
4550 :
4551 72802 : if (ret != LDB_SUCCESS) {
4552 0 : talloc_free(tmp_ctx);
4553 0 : return ret;
4554 : }
4555 :
4556 72802 : ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4557 : &is_deleted_el);
4558 72802 : if (ret != LDB_SUCCESS) {
4559 0 : ldb_asprintf_errstring(ldb, __location__
4560 : ": Failed to add isDeleted string to the msg");
4561 0 : talloc_free(tmp_ctx);
4562 0 : return ret;
4563 : }
4564 72802 : is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4565 : } else {
4566 : /*
4567 : * No matter what has happened with other renames etc, try again to
4568 : * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4569 : */
4570 :
4571 151290 : struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4572 151290 : retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4573 151290 : if (!retb) {
4574 0 : ldb_asprintf_errstring(ldb, __location__
4575 : ": Unable to add a prepare rdn of %s",
4576 : ldb_dn_get_linearized(rdn));
4577 0 : talloc_free(tmp_ctx);
4578 0 : return LDB_ERR_OPERATIONS_ERROR;
4579 : }
4580 151290 : SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4581 :
4582 151290 : retb = ldb_dn_add_child(new_dn, rdn);
4583 151290 : if (!retb) {
4584 0 : ldb_asprintf_errstring(ldb, __location__
4585 : ": Unable to add rdn %s to base dn: %s",
4586 : ldb_dn_get_linearized(rdn),
4587 : ldb_dn_get_linearized(new_dn));
4588 0 : talloc_free(tmp_ctx);
4589 0 : return LDB_ERR_OPERATIONS_ERROR;
4590 : }
4591 : }
4592 :
4593 : /*
4594 : now we need to modify the object in the following ways:
4595 :
4596 : - add isDeleted=TRUE
4597 : - update rDN and name, with new rDN
4598 : - remove linked attributes
4599 : - remove objectCategory and sAMAccountType
4600 : - remove attribs not on the preserved list
4601 : - preserved if in above list, or is rDN
4602 : - remove all linked attribs from this object
4603 : - remove all links from other objects to this object
4604 : (note we use the backlinks to do this, so we won't find one-way
4605 : links that still point to this object, or deactivated two-way
4606 : links, i.e. 'member' after the user has been removed from the
4607 : group)
4608 : - add lastKnownParent
4609 : - update replPropertyMetaData?
4610 :
4611 : see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4612 : */
4613 :
4614 224092 : if (deletion_state == OBJECT_NOT_DELETED) {
4615 72802 : struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4616 72802 : char *parent_dn_str = NULL;
4617 139 : struct ldb_message_element *p_el;
4618 :
4619 : /* we need the storage form of the parent GUID */
4620 72802 : ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4621 : parent_dn, NULL,
4622 : DSDB_FLAG_NEXT_MODULE |
4623 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4624 : DSDB_SEARCH_REVEAL_INTERNALS|
4625 : DSDB_SEARCH_SHOW_RECYCLED, req);
4626 72802 : if (ret != LDB_SUCCESS) {
4627 0 : ldb_asprintf_errstring(ldb_module_get_ctx(module),
4628 : "repmd_delete: Failed to %s %s, "
4629 : "because we failed to find it's parent (%s): %s",
4630 : re_delete ? "re-delete" : "delete",
4631 : ldb_dn_get_linearized(old_dn),
4632 : ldb_dn_get_linearized(parent_dn),
4633 : ldb_errstring(ldb_module_get_ctx(module)));
4634 0 : talloc_free(tmp_ctx);
4635 0 : return ret;
4636 : }
4637 :
4638 : /*
4639 : * Now we can use the DB version,
4640 : * it will have the extended DN info in it
4641 : */
4642 72802 : parent_dn = parent_res->msgs[0]->dn;
4643 72802 : parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4644 : parent_dn,
4645 : 1);
4646 72802 : if (parent_dn_str == NULL) {
4647 0 : talloc_free(tmp_ctx);
4648 0 : return ldb_module_oom(module);
4649 : }
4650 :
4651 72802 : ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4652 : parent_dn_str);
4653 72802 : if (ret != LDB_SUCCESS) {
4654 0 : ldb_asprintf_errstring(ldb, __location__
4655 : ": Failed to add lastKnownParent "
4656 : "string when deleting %s",
4657 : ldb_dn_get_linearized(old_dn));
4658 0 : talloc_free(tmp_ctx);
4659 0 : return ret;
4660 : }
4661 72802 : p_el = ldb_msg_find_element(msg,
4662 : "lastKnownParent");
4663 72802 : if (p_el == NULL) {
4664 0 : talloc_free(tmp_ctx);
4665 0 : return ldb_module_operr(module);
4666 : }
4667 72802 : p_el->flags = LDB_FLAG_MOD_REPLACE;
4668 :
4669 72802 : if (next_deletion_state == OBJECT_DELETED) {
4670 0 : ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4671 0 : if (ret != LDB_SUCCESS) {
4672 0 : ldb_asprintf_errstring(ldb, __location__
4673 : ": Failed to add msDS-LastKnownRDN "
4674 : "string when deleting %s",
4675 : ldb_dn_get_linearized(old_dn));
4676 0 : talloc_free(tmp_ctx);
4677 0 : return ret;
4678 : }
4679 0 : p_el = ldb_msg_find_element(msg,
4680 : "msDS-LastKnownRDN");
4681 0 : if (p_el == NULL) {
4682 0 : talloc_free(tmp_ctx);
4683 0 : return ldb_module_operr(module);
4684 : }
4685 0 : p_el->flags = LDB_FLAG_MOD_ADD;
4686 : }
4687 : }
4688 :
4689 224092 : switch (next_deletion_state) {
4690 :
4691 224092 : case OBJECT_RECYCLED:
4692 : case OBJECT_TOMBSTONE:
4693 :
4694 : /*
4695 : * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4696 : * describes what must be removed from a tombstone
4697 : * object
4698 : *
4699 : * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4700 : * describes what must be removed from a recycled
4701 : * object
4702 : *
4703 : */
4704 :
4705 : /*
4706 : * we also mark it as recycled, meaning this object can't be
4707 : * recovered (we are stripping its attributes).
4708 : * This is done only if we have this schema object of course ...
4709 : * This behavior is identical to the one of Windows 2008R2 which
4710 : * always set the isRecycled attribute, even if the recycle-bin is
4711 : * not activated and what ever the forest level is.
4712 : */
4713 224092 : if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4714 139 : struct ldb_message_element *is_recycled_el;
4715 :
4716 224092 : ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4717 : &is_recycled_el);
4718 224092 : if (ret != LDB_SUCCESS) {
4719 0 : DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4720 0 : ldb_module_oom(module);
4721 0 : talloc_free(tmp_ctx);
4722 0 : return ret;
4723 : }
4724 224092 : is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4725 : }
4726 :
4727 224092 : replmd_private = talloc_get_type(ldb_module_get_private(module),
4728 : struct replmd_private);
4729 : /* work out which of the old attributes we will be removing */
4730 3766919 : for (i=0; i<old_msg->num_elements; i++) {
4731 3806 : const struct dsdb_attribute *sa;
4732 3542688 : el = &old_msg->elements[i];
4733 3542688 : sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4734 3542688 : if (!sa) {
4735 0 : const char *old_dn_str
4736 0 : = ldb_dn_get_linearized(old_dn);
4737 :
4738 0 : ldb_asprintf_errstring(ldb,
4739 : __location__
4740 : ": Attribute %s "
4741 : "not found in schema "
4742 : "when deleting %s. "
4743 : "Existing record is invalid",
4744 0 : el->name,
4745 : old_dn_str);
4746 0 : talloc_free(tmp_ctx);
4747 0 : return LDB_ERR_OPERATIONS_ERROR;
4748 : }
4749 3542688 : if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4750 : /* don't remove the rDN */
4751 224092 : continue;
4752 : }
4753 :
4754 3318596 : if (sa->linkID & 1) {
4755 9661 : bool caller_should_vanish = false;
4756 : /*
4757 : * we have a backlink in this object
4758 : * that needs to be removed. We're not
4759 : * allowed to remove it directly
4760 : * however, so we instead setup a
4761 : * modify to delete the corresponding
4762 : * forward link
4763 : */
4764 9661 : ret = replmd_delete_remove_link(module, schema,
4765 : replmd_private,
4766 : old_dn, &guid,
4767 : el, sa, req,
4768 : &caller_should_vanish);
4769 9661 : if (ret != LDB_SUCCESS) {
4770 0 : const char *old_dn_str
4771 0 : = ldb_dn_get_linearized(old_dn);
4772 0 : ldb_asprintf_errstring(ldb,
4773 : __location__
4774 : ": Failed to remove backlink of "
4775 : "%s when deleting %s: %s",
4776 0 : el->name,
4777 : old_dn_str,
4778 : ldb_errstring(ldb));
4779 0 : talloc_free(tmp_ctx);
4780 0 : return LDB_ERR_OPERATIONS_ERROR;
4781 : }
4782 :
4783 9661 : if (caller_should_vanish == false) {
4784 : /*
4785 : * now we continue, which means we
4786 : * won't remove this backlink
4787 : * directly
4788 : */
4789 9656 : continue;
4790 : }
4791 :
4792 : /*
4793 : * Otherwise vanish the link, we are
4794 : * out of sync and the controlling
4795 : * object does not have the source
4796 : * link any more
4797 : */
4798 :
4799 5 : dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4800 :
4801 3308935 : } else if (sa->linkID == 0) {
4802 3303922 : const char * const *attr = NULL;
4803 3303922 : if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4804 2005834 : continue;
4805 : }
4806 6741807 : BINARY_ARRAY_SEARCH_V(preserved_attrs,
4807 : ARRAY_SIZE(preserved_attrs),
4808 : el->name,
4809 : ldb_attr_cmp,
4810 : attr);
4811 : /*
4812 : * If we are preserving, do not do the
4813 : * ldb_msg_add_empty() below, continue
4814 : * to the next element
4815 : */
4816 1298088 : if (attr != NULL) {
4817 764705 : continue;
4818 : }
4819 : } else {
4820 : /*
4821 : * Ensure that we tell the modification to vanish any linked
4822 : * attributes (not simply mark them as isDeleted = TRUE)
4823 : */
4824 5000 : dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4825 : }
4826 538401 : ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, NULL);
4827 538401 : if (ret != LDB_SUCCESS) {
4828 0 : talloc_free(tmp_ctx);
4829 0 : ldb_module_oom(module);
4830 0 : return ret;
4831 : }
4832 : }
4833 :
4834 223953 : break;
4835 :
4836 0 : case OBJECT_DELETED:
4837 : /*
4838 : * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4839 : * describes what must be removed from a deleted
4840 : * object
4841 : */
4842 :
4843 0 : ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4844 0 : if (ret != LDB_SUCCESS) {
4845 0 : talloc_free(tmp_ctx);
4846 0 : ldb_module_oom(module);
4847 0 : return ret;
4848 : }
4849 :
4850 0 : ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4851 0 : if (ret != LDB_SUCCESS) {
4852 0 : talloc_free(tmp_ctx);
4853 0 : ldb_module_oom(module);
4854 0 : return ret;
4855 : }
4856 :
4857 0 : break;
4858 :
4859 0 : default:
4860 0 : break;
4861 : }
4862 :
4863 224092 : if (deletion_state == OBJECT_NOT_DELETED) {
4864 139 : const struct dsdb_attribute *sa;
4865 :
4866 : /* work out what the new rdn value is, for updating the
4867 : rDN and name fields */
4868 72802 : new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4869 72802 : if (new_rdn_value == NULL) {
4870 0 : talloc_free(tmp_ctx);
4871 0 : return ldb_operr(ldb);
4872 : }
4873 :
4874 72802 : sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4875 72802 : if (!sa) {
4876 0 : talloc_free(tmp_ctx);
4877 0 : return LDB_ERR_OPERATIONS_ERROR;
4878 : }
4879 :
4880 72802 : ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4881 : &el);
4882 72802 : if (ret != LDB_SUCCESS) {
4883 0 : talloc_free(tmp_ctx);
4884 0 : return ret;
4885 : }
4886 72802 : el->flags = LDB_FLAG_MOD_REPLACE;
4887 :
4888 72802 : el = ldb_msg_find_element(old_msg, "name");
4889 72802 : if (el) {
4890 72802 : ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4891 72802 : if (ret != LDB_SUCCESS) {
4892 0 : talloc_free(tmp_ctx);
4893 0 : return ret;
4894 : }
4895 72802 : el->flags = LDB_FLAG_MOD_REPLACE;
4896 : }
4897 : }
4898 :
4899 : /*
4900 : * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4901 : *
4902 : */
4903 :
4904 : /*
4905 : * No matter what has happned with other renames, try again to
4906 : * get this to be under the deleted DN.
4907 : */
4908 224092 : if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4909 : /* now rename onto the new DN */
4910 72802 : ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4911 72802 : if (ret != LDB_SUCCESS){
4912 0 : DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4913 : ldb_dn_get_linearized(old_dn),
4914 : ldb_dn_get_linearized(new_dn),
4915 : ldb_errstring(ldb)));
4916 0 : talloc_free(tmp_ctx);
4917 0 : return ret;
4918 : }
4919 72802 : msg->dn = new_dn;
4920 : }
4921 :
4922 224092 : ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4923 224092 : if (ret != LDB_SUCCESS) {
4924 0 : char *s = NULL;
4925 : /*
4926 : * This should not fail, so be quite verbose in the
4927 : * error handling if it fails
4928 : */
4929 0 : if (strcmp(ldb_dn_get_linearized(old_dn),
4930 : ldb_dn_get_linearized(new_dn)) != 0) {
4931 0 : DBG_NOTICE("Failure to handle '%s' of object %s "
4932 : "after successful rename to %s. "
4933 : "Error during tombstone modification was: %s\n",
4934 : re_delete ? "re-delete" : "delete",
4935 : ldb_dn_get_linearized(new_dn),
4936 : ldb_dn_get_linearized(old_dn),
4937 : ldb_errstring(ldb));
4938 : } else {
4939 0 : DBG_NOTICE("Failure to handle '%s' of object %s. "
4940 : "Error during tombstone modification was: %s\n",
4941 : re_delete ? "re-delete" : "delete",
4942 : ldb_dn_get_linearized(new_dn),
4943 : ldb_errstring(ldb));
4944 : }
4945 0 : s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(module),
4946 : tmp_ctx,
4947 : LDB_CHANGETYPE_MODIFY,
4948 : msg);
4949 :
4950 0 : DBG_INFO("Failed tombstone modify%s was:\n%s\n",
4951 : (dsdb_flags & DSDB_REPLMD_VANISH_LINKS) ?
4952 : " with VANISH_LINKS" : "",
4953 : s);
4954 0 : ldb_asprintf_errstring(ldb,
4955 : "replmd_delete: Failed to modify"
4956 : " object %s in '%s' - %s",
4957 : ldb_dn_get_linearized(old_dn),
4958 : re_delete ? "re-delete" : "delete",
4959 : ldb_errstring(ldb));
4960 0 : talloc_free(tmp_ctx);
4961 0 : return ret;
4962 : }
4963 :
4964 224092 : talloc_free(tmp_ctx);
4965 :
4966 224092 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4967 : }
4968 :
4969 72819 : static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4970 : {
4971 72819 : return replmd_delete_internals(module, req, false);
4972 : }
4973 :
4974 :
4975 0 : static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4976 : {
4977 0 : return ret;
4978 : }
4979 :
4980 69 : static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4981 : {
4982 69 : int ret = LDB_ERR_OTHER;
4983 : /* TODO: do some error mapping */
4984 :
4985 : /* Let the caller know the full WERROR */
4986 69 : ar->objs->error = status;
4987 :
4988 69 : return ret;
4989 : }
4990 :
4991 :
4992 : static struct replPropertyMetaData1 *
4993 239976 : replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4994 : enum drsuapi_DsAttributeId attid)
4995 : {
4996 0 : uint32_t i;
4997 239976 : struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4998 :
4999 1515001 : for (i = 0; i < rpmd_ctr->count; i++) {
5000 1513926 : if (rpmd_ctr->array[i].attid == attid) {
5001 238901 : return &rpmd_ctr->array[i];
5002 : }
5003 : }
5004 1075 : return NULL;
5005 : }
5006 :
5007 :
5008 : /*
5009 : return true if an update is newer than an existing entry
5010 : see section 5.11 of MS-ADTS
5011 : */
5012 1428662 : static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
5013 : const struct GUID *update_invocation_id,
5014 : uint32_t current_version,
5015 : uint32_t update_version,
5016 : NTTIME current_change_time,
5017 : NTTIME update_change_time)
5018 : {
5019 1428662 : if (update_version != current_version) {
5020 36012 : return update_version > current_version;
5021 : }
5022 1392650 : if (update_change_time != current_change_time) {
5023 146 : return update_change_time > current_change_time;
5024 : }
5025 1392504 : return GUID_compare(update_invocation_id, current_invocation_id) > 0;
5026 : }
5027 :
5028 1424249 : static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
5029 : struct replPropertyMetaData1 *new_m)
5030 : {
5031 2848498 : return replmd_update_is_newer(&cur_m->originating_invocation_id,
5032 1424249 : &new_m->originating_invocation_id,
5033 : cur_m->version,
5034 : new_m->version,
5035 : cur_m->originating_change_time,
5036 : new_m->originating_change_time);
5037 : }
5038 :
5039 1425324 : static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
5040 : struct replPropertyMetaData1 *cur_m,
5041 : struct replPropertyMetaData1 *new_m)
5042 : {
5043 0 : bool cmp;
5044 :
5045 : /*
5046 : * If the new replPropertyMetaData entry for this attribute is
5047 : * not provided (this happens in the case where we look for
5048 : * ATTID_name, but the name was not changed), then the local
5049 : * state is clearly still current, as the remote
5050 : * server didn't send it due to being older the high watermark
5051 : * USN we sent.
5052 : */
5053 1425324 : if (new_m == NULL) {
5054 1075 : return false;
5055 : }
5056 :
5057 1424249 : if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
5058 : /*
5059 : * if we compare equal then do an
5060 : * update. This is used when a client
5061 : * asks for a FULL_SYNC, and can be
5062 : * used to recover a corrupt
5063 : * replica.
5064 : *
5065 : * This call is a bit tricky, what we
5066 : * are doing it turning the 'is_newer'
5067 : * call into a 'not is older' by
5068 : * swapping cur_m and new_m, and negating the
5069 : * outcome.
5070 : */
5071 798743 : cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
5072 798743 : cur_m);
5073 : } else {
5074 625506 : cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
5075 : new_m);
5076 : }
5077 1424249 : return cmp;
5078 : }
5079 :
5080 :
5081 : /*
5082 : form a DN for a deleted (DEL:) or conflict (CNF:) DN
5083 : */
5084 72846 : static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
5085 : struct ldb_context *ldb,
5086 : struct ldb_dn *dn,
5087 : const char *four_char_prefix,
5088 : const char *rdn_name,
5089 : const struct ldb_val *rdn_value,
5090 : struct GUID guid)
5091 : {
5092 139 : struct ldb_val deleted_child_rdn_val;
5093 139 : struct GUID_txt_buf guid_str;
5094 139 : int ret;
5095 139 : bool retb;
5096 :
5097 72846 : GUID_buf_string(&guid, &guid_str);
5098 :
5099 72846 : retb = ldb_dn_add_child_fmt(dn, "X=Y");
5100 72846 : if (!retb) {
5101 0 : ldb_asprintf_errstring(ldb, __location__
5102 : ": Unable to add a formatted child to dn: %s",
5103 : ldb_dn_get_linearized(dn));
5104 0 : return LDB_ERR_OPERATIONS_ERROR;
5105 : }
5106 :
5107 : /*
5108 : * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
5109 : * we should truncate this value to ensure the RDN is not more than 255 chars.
5110 : *
5111 : * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
5112 : *
5113 : * "Naming constraints are not enforced for replicated
5114 : * updates." so this is safe and we don't have to work out not
5115 : * splitting a UTF8 char right now.
5116 : */
5117 72846 : deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
5118 :
5119 : /*
5120 : * sizeof(guid_str.buf) will always be longer than
5121 : * strlen(guid_str.buf) but we allocate using this and
5122 : * waste the trailing bytes to avoid scaring folks
5123 : * with memcpy() using strlen() below
5124 : */
5125 :
5126 139 : deleted_child_rdn_val.data
5127 72846 : = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
5128 : uint8_t,
5129 : rdn_value->length + 5
5130 : + sizeof(guid_str.buf));
5131 72846 : if (!deleted_child_rdn_val.data) {
5132 0 : ldb_asprintf_errstring(ldb, __location__
5133 : ": Unable to add a formatted child to dn: %s",
5134 : ldb_dn_get_linearized(dn));
5135 0 : return LDB_ERR_OPERATIONS_ERROR;
5136 : }
5137 :
5138 72846 : deleted_child_rdn_val.length =
5139 72846 : rdn_value->length + 5
5140 72846 : + strlen(guid_str.buf);
5141 :
5142 72846 : SMB_ASSERT(deleted_child_rdn_val.length <
5143 : talloc_get_size(deleted_child_rdn_val.data));
5144 :
5145 : /*
5146 : * talloc won't allocate more than 256MB so we can't
5147 : * overflow but just to be sure
5148 : */
5149 72846 : if (deleted_child_rdn_val.length < rdn_value->length) {
5150 0 : return LDB_ERR_OPERATIONS_ERROR;
5151 : }
5152 :
5153 72846 : deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
5154 72846 : memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
5155 : four_char_prefix, 4);
5156 72846 : memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
5157 : guid_str.buf,
5158 : sizeof(guid_str.buf));
5159 :
5160 : /* Now set the value into the RDN, without parsing it */
5161 72846 : ret = ldb_dn_set_component(
5162 : dn,
5163 : 0,
5164 : rdn_name,
5165 : deleted_child_rdn_val);
5166 :
5167 72846 : return ret;
5168 : }
5169 :
5170 :
5171 : /*
5172 : form a conflict DN
5173 : */
5174 44 : static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
5175 : struct ldb_context *ldb,
5176 : struct ldb_dn *dn,
5177 : struct GUID *guid)
5178 : {
5179 0 : const struct ldb_val *rdn_val;
5180 0 : const char *rdn_name;
5181 0 : struct ldb_dn *new_dn;
5182 0 : int ret;
5183 :
5184 44 : rdn_val = ldb_dn_get_rdn_val(dn);
5185 44 : rdn_name = ldb_dn_get_rdn_name(dn);
5186 44 : if (!rdn_val || !rdn_name) {
5187 0 : return NULL;
5188 : }
5189 :
5190 44 : new_dn = ldb_dn_get_parent(mem_ctx, dn);
5191 44 : if (!new_dn) {
5192 0 : return NULL;
5193 : }
5194 :
5195 44 : ret = replmd_make_prefix_child_dn(mem_ctx,
5196 : ldb, new_dn,
5197 : "CNF:",
5198 : rdn_name,
5199 : rdn_val,
5200 : *guid);
5201 44 : if (ret != LDB_SUCCESS) {
5202 0 : return NULL;
5203 : }
5204 44 : return new_dn;
5205 : }
5206 :
5207 : /*
5208 : form a deleted DN
5209 : */
5210 72802 : static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
5211 : struct ldb_context *ldb,
5212 : struct ldb_dn *dn,
5213 : const char *rdn_name,
5214 : const struct ldb_val *rdn_value,
5215 : struct GUID guid)
5216 : {
5217 72802 : return replmd_make_prefix_child_dn(tmp_ctx,
5218 : ldb, dn,
5219 : "DEL:",
5220 : rdn_name,
5221 : rdn_value,
5222 : guid);
5223 : }
5224 :
5225 :
5226 : /*
5227 : perform a modify operation which sets the rDN and name attributes to
5228 : their current values. This has the effect of changing these
5229 : attributes to have been last updated by the current DC. This is
5230 : needed to ensure that renames performed as part of conflict
5231 : resolution are propagated to other DCs
5232 : */
5233 50 : static int replmd_name_modify(struct replmd_replicated_request *ar,
5234 : struct ldb_request *req, struct ldb_dn *dn)
5235 : {
5236 0 : struct ldb_message *msg;
5237 0 : const char *rdn_name;
5238 0 : const struct ldb_val *rdn_val;
5239 0 : const struct dsdb_attribute *rdn_attr;
5240 0 : int ret;
5241 :
5242 50 : msg = ldb_msg_new(req);
5243 50 : if (msg == NULL) {
5244 0 : goto failed;
5245 : }
5246 50 : msg->dn = dn;
5247 :
5248 50 : rdn_name = ldb_dn_get_rdn_name(dn);
5249 50 : if (rdn_name == NULL) {
5250 0 : goto failed;
5251 : }
5252 :
5253 : /* normalize the rdn attribute name */
5254 50 : rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5255 50 : if (rdn_attr == NULL) {
5256 0 : goto failed;
5257 : }
5258 50 : rdn_name = rdn_attr->lDAPDisplayName;
5259 :
5260 50 : rdn_val = ldb_dn_get_rdn_val(dn);
5261 50 : if (rdn_val == NULL) {
5262 0 : goto failed;
5263 : }
5264 :
5265 50 : if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
5266 0 : goto failed;
5267 : }
5268 50 : if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
5269 0 : goto failed;
5270 : }
5271 :
5272 : /*
5273 : * We have to mark this as a replicated update otherwise
5274 : * schema_data may reject a rename in the schema partition
5275 : */
5276 :
5277 50 : ret = dsdb_module_modify(ar->module, msg,
5278 : DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5279 : req);
5280 50 : if (ret != LDB_SUCCESS) {
5281 0 : DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s\n",
5282 : ldb_dn_get_linearized(dn),
5283 : ldb_errstring(ldb_module_get_ctx(ar->module))));
5284 0 : return ret;
5285 : }
5286 :
5287 50 : talloc_free(msg);
5288 :
5289 50 : return LDB_SUCCESS;
5290 :
5291 0 : failed:
5292 0 : talloc_free(msg);
5293 0 : DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'\n",
5294 : ldb_dn_get_linearized(dn)));
5295 0 : return LDB_ERR_OPERATIONS_ERROR;
5296 : }
5297 :
5298 :
5299 : /*
5300 : callback for conflict DN handling where we have renamed the incoming
5301 : record. After renaming it, we need to ensure the change of name and
5302 : rDN for the incoming record is seen as an originating update by this DC.
5303 :
5304 : This also handles updating lastKnownParent for entries sent to lostAndFound
5305 : */
5306 22 : static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5307 : {
5308 0 : struct replmd_replicated_request *ar =
5309 22 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
5310 22 : struct ldb_dn *conflict_dn = NULL;
5311 0 : int ret;
5312 :
5313 22 : if (ares->error != LDB_SUCCESS) {
5314 : /* call the normal callback for everything except success */
5315 0 : return replmd_op_callback(req, ares);
5316 : }
5317 :
5318 22 : switch (req->operation) {
5319 20 : case LDB_ADD:
5320 20 : conflict_dn = req->op.add.message->dn;
5321 20 : break;
5322 2 : case LDB_MODIFY:
5323 2 : conflict_dn = req->op.mod.message->dn;
5324 2 : break;
5325 0 : default:
5326 0 : smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5327 : }
5328 :
5329 : /* perform a modify of the rDN and name of the record */
5330 22 : ret = replmd_name_modify(ar, req, conflict_dn);
5331 22 : if (ret != LDB_SUCCESS) {
5332 0 : ares->error = ret;
5333 0 : return replmd_op_callback(req, ares);
5334 : }
5335 :
5336 22 : if (ar->objs->objects[ar->index_current].last_known_parent) {
5337 8 : struct ldb_message *msg = ldb_msg_new(req);
5338 8 : if (msg == NULL) {
5339 0 : ldb_module_oom(ar->module);
5340 0 : return LDB_ERR_OPERATIONS_ERROR;
5341 : }
5342 :
5343 8 : msg->dn = req->op.add.message->dn;
5344 :
5345 8 : ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5346 8 : ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5347 8 : if (ret != LDB_SUCCESS) {
5348 0 : DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5349 0 : ldb_module_oom(ar->module);
5350 0 : return ret;
5351 : }
5352 8 : msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5353 :
5354 8 : ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5355 8 : if (ret != LDB_SUCCESS) {
5356 0 : DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s\n",
5357 : ldb_dn_get_linearized(msg->dn),
5358 : ldb_errstring(ldb_module_get_ctx(ar->module))));
5359 0 : return ret;
5360 : }
5361 8 : TALLOC_FREE(msg);
5362 : }
5363 :
5364 22 : return replmd_op_callback(req, ares);
5365 : }
5366 :
5367 :
5368 :
5369 : /*
5370 : * A helper for replmd_op_possible_conflict_callback() and
5371 : * replmd_replicated_handle_rename()
5372 : */
5373 46 : static int incoming_dn_should_be_renamed(TALLOC_CTX *mem_ctx,
5374 : struct replmd_replicated_request *ar,
5375 : struct ldb_dn *conflict_dn,
5376 : struct ldb_result **res,
5377 : bool *rename_incoming_record)
5378 : {
5379 0 : int ret;
5380 0 : bool rodc;
5381 0 : enum ndr_err_code ndr_err;
5382 46 : const struct ldb_val *omd_value = NULL;
5383 46 : struct replPropertyMetaDataBlob omd, *rmd = NULL;
5384 46 : struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5385 46 : const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5386 46 : struct replPropertyMetaData1 *omd_name = NULL;
5387 46 : struct replPropertyMetaData1 *rmd_name = NULL;
5388 46 : struct ldb_message *msg = NULL;
5389 :
5390 46 : ret = samdb_rodc(ldb, &rodc);
5391 46 : if (ret != LDB_SUCCESS) {
5392 0 : ldb_asprintf_errstring(
5393 : ldb,
5394 : "Failed to determine if we are an RODC when attempting "
5395 : "to form conflict DN: %s",
5396 : ldb_errstring(ldb));
5397 0 : return LDB_ERR_OPERATIONS_ERROR;
5398 : }
5399 :
5400 46 : if (rodc) {
5401 : /*
5402 : * We are on an RODC, or were a GC for this
5403 : * partition, so we have to fail this until
5404 : * someone who owns the partition sorts it
5405 : * out
5406 : */
5407 2 : ldb_asprintf_errstring(
5408 : ldb,
5409 : "Conflict adding object '%s' from incoming replication "
5410 : "but we are read only for the partition. \n"
5411 : " - We must fail the operation until a master for this "
5412 : "partition resolves the conflict",
5413 : ldb_dn_get_linearized(conflict_dn));
5414 2 : return LDB_ERR_OPERATIONS_ERROR;
5415 : }
5416 :
5417 : /*
5418 : * first we need the replPropertyMetaData attribute from the
5419 : * old record
5420 : */
5421 44 : ret = dsdb_module_search_dn(ar->module, mem_ctx, res, conflict_dn,
5422 : attrs,
5423 : DSDB_FLAG_NEXT_MODULE |
5424 : DSDB_SEARCH_SHOW_DELETED |
5425 : DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5426 44 : if (ret != LDB_SUCCESS) {
5427 0 : DBG_ERR(__location__
5428 : ": Unable to find object for conflicting record '%s'\n",
5429 : ldb_dn_get_linearized(conflict_dn));
5430 0 : return LDB_ERR_OPERATIONS_ERROR;
5431 : }
5432 :
5433 44 : msg = (*res)->msgs[0];
5434 44 : omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
5435 44 : if (omd_value == NULL) {
5436 0 : DBG_ERR(__location__
5437 : ": Unable to find replPropertyMetaData for conflicting "
5438 : "record '%s'\n",
5439 : ldb_dn_get_linearized(conflict_dn));
5440 0 : return LDB_ERR_OPERATIONS_ERROR;
5441 : }
5442 :
5443 44 : ndr_err = ndr_pull_struct_blob(
5444 : omd_value, msg, &omd,
5445 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5446 44 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5447 0 : DBG_ERR(__location__
5448 : ": Failed to parse old replPropertyMetaData for %s\n",
5449 : ldb_dn_get_linearized(conflict_dn));
5450 0 : return LDB_ERR_OPERATIONS_ERROR;
5451 : }
5452 :
5453 44 : rmd = ar->objs->objects[ar->index_current].meta_data;
5454 :
5455 : /*
5456 : * we decide which is newer based on the RPMD on the name
5457 : * attribute. See [MS-DRSR] ResolveNameConflict.
5458 : *
5459 : * We expect omd_name to be present, as this is from a local
5460 : * search, but while rmd_name should have been given to us by
5461 : * the remote server, if it is missing we just prefer the
5462 : * local name in
5463 : * replmd_replPropertyMetaData1_new_should_be_taken()
5464 : */
5465 44 : rmd_name = replmd_replPropertyMetaData1_find_attid(rmd,
5466 : DRSUAPI_ATTID_name);
5467 44 : omd_name = replmd_replPropertyMetaData1_find_attid(&omd,
5468 : DRSUAPI_ATTID_name);
5469 44 : if (!omd_name) {
5470 0 : DBG_ERR(__location__
5471 : ": Failed to find name attribute in "
5472 : "local LDB replPropertyMetaData for %s\n",
5473 : ldb_dn_get_linearized(conflict_dn));
5474 0 : return LDB_ERR_OPERATIONS_ERROR;
5475 : }
5476 :
5477 : /*
5478 : * Should we preserve the current record, and so rename the
5479 : * incoming record to be a conflict?
5480 : */
5481 44 : *rename_incoming_record =
5482 88 : !replmd_replPropertyMetaData1_new_should_be_taken(
5483 44 : (ar->objs->dsdb_repl_flags &
5484 : DSDB_REPL_FLAG_PRIORITISE_INCOMING),
5485 44 : omd_name, rmd_name);
5486 :
5487 44 : return LDB_SUCCESS;
5488 : }
5489 :
5490 :
5491 : /*
5492 : callback for replmd_replicated_apply_add()
5493 : This copes with the creation of conflict records in the case where
5494 : the DN exists, but with a different objectGUID
5495 : */
5496 380532 : static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct ldb_reply *ares, int (*callback)(struct ldb_request *req, struct ldb_reply *ares))
5497 : {
5498 0 : struct ldb_dn *conflict_dn;
5499 0 : struct replmd_replicated_request *ar =
5500 380532 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
5501 0 : struct ldb_result *res;
5502 0 : int ret;
5503 0 : bool rename_incoming_record;
5504 0 : struct ldb_message *msg;
5505 380532 : struct ldb_request *down_req = NULL;
5506 :
5507 : /* call the normal callback for success */
5508 380532 : if (ares->error == LDB_SUCCESS) {
5509 380499 : return callback(req, ares);
5510 : }
5511 :
5512 : /*
5513 : * we have a conflict, and need to decide if we will keep the
5514 : * new record or the old record
5515 : */
5516 :
5517 33 : msg = ar->objs->objects[ar->index_current].msg;
5518 33 : conflict_dn = msg->dn;
5519 :
5520 : /* For failures other than conflicts, fail the whole operation here */
5521 33 : if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5522 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5523 : ldb_dn_get_linearized(conflict_dn),
5524 : ldb_errstring(ldb_module_get_ctx(ar->module)));
5525 :
5526 0 : return ldb_module_done(ar->req, NULL, NULL,
5527 : LDB_ERR_OPERATIONS_ERROR);
5528 : }
5529 :
5530 :
5531 33 : ret = incoming_dn_should_be_renamed(req, ar, conflict_dn, &res,
5532 : &rename_incoming_record);
5533 33 : if (ret != LDB_SUCCESS) {
5534 1 : goto failed;
5535 : }
5536 :
5537 32 : if (rename_incoming_record) {
5538 0 : struct GUID guid;
5539 0 : struct ldb_dn *new_dn;
5540 :
5541 14 : guid = samdb_result_guid(msg, "objectGUID");
5542 14 : if (GUID_all_zero(&guid)) {
5543 0 : DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5544 : ldb_dn_get_linearized(conflict_dn)));
5545 0 : goto failed;
5546 : }
5547 14 : new_dn = replmd_conflict_dn(req,
5548 : ldb_module_get_ctx(ar->module),
5549 : conflict_dn, &guid);
5550 14 : if (new_dn == NULL) {
5551 0 : DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5552 : ldb_dn_get_linearized(conflict_dn)));
5553 0 : goto failed;
5554 : }
5555 :
5556 14 : DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5557 : ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5558 :
5559 : /* re-submit the request, but with the new DN */
5560 14 : callback = replmd_op_name_modify_callback;
5561 14 : msg->dn = new_dn;
5562 : } else {
5563 : /* we are renaming the existing record */
5564 0 : struct GUID guid;
5565 0 : struct ldb_dn *new_dn;
5566 :
5567 18 : guid = samdb_result_guid(res->msgs[0], "objectGUID");
5568 18 : if (GUID_all_zero(&guid)) {
5569 0 : DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5570 : ldb_dn_get_linearized(conflict_dn)));
5571 0 : goto failed;
5572 : }
5573 :
5574 18 : new_dn = replmd_conflict_dn(req,
5575 : ldb_module_get_ctx(ar->module),
5576 : conflict_dn, &guid);
5577 18 : if (new_dn == NULL) {
5578 0 : DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5579 : ldb_dn_get_linearized(conflict_dn)));
5580 0 : goto failed;
5581 : }
5582 :
5583 18 : DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5584 : ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5585 :
5586 18 : ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5587 : DSDB_FLAG_OWN_MODULE, req);
5588 18 : if (ret != LDB_SUCCESS) {
5589 0 : DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5590 : ldb_dn_get_linearized(conflict_dn),
5591 : ldb_dn_get_linearized(new_dn),
5592 : ldb_errstring(ldb_module_get_ctx(ar->module))));
5593 0 : goto failed;
5594 : }
5595 :
5596 : /*
5597 : * now we need to ensure that the rename is seen as an
5598 : * originating update. We do that with a modify.
5599 : */
5600 18 : ret = replmd_name_modify(ar, req, new_dn);
5601 18 : if (ret != LDB_SUCCESS) {
5602 0 : goto failed;
5603 : }
5604 :
5605 18 : DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5606 : ldb_dn_get_linearized(req->op.add.message->dn)));
5607 : }
5608 :
5609 32 : ret = ldb_build_add_req(&down_req,
5610 : ldb_module_get_ctx(ar->module),
5611 : req,
5612 : msg,
5613 : ar->controls,
5614 : ar,
5615 : callback,
5616 : req);
5617 32 : if (ret != LDB_SUCCESS) {
5618 0 : goto failed;
5619 : }
5620 32 : LDB_REQ_SET_LOCATION(down_req);
5621 :
5622 : /* current partition control needed by "repmd_op_callback" */
5623 32 : ret = ldb_request_add_control(down_req,
5624 : DSDB_CONTROL_CURRENT_PARTITION_OID,
5625 : false, NULL);
5626 32 : if (ret != LDB_SUCCESS) {
5627 0 : return replmd_replicated_request_error(ar, ret);
5628 : }
5629 :
5630 32 : if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5631 : /* this tells the partition module to make it a
5632 : partial replica if creating an NC */
5633 0 : ret = ldb_request_add_control(down_req,
5634 : DSDB_CONTROL_PARTIAL_REPLICA,
5635 : false, NULL);
5636 0 : if (ret != LDB_SUCCESS) {
5637 0 : return replmd_replicated_request_error(ar, ret);
5638 : }
5639 : }
5640 :
5641 : /*
5642 : * Finally we re-run the add, otherwise the new record won't
5643 : * exist, as we are here because of that exact failure!
5644 : */
5645 32 : return ldb_next_request(ar->module, down_req);
5646 1 : failed:
5647 :
5648 : /* on failure make the caller get the error. This means
5649 : * replication will stop with an error, but there is not much
5650 : * else we can do.
5651 : */
5652 1 : if (ret == LDB_SUCCESS) {
5653 0 : ret = LDB_ERR_OPERATIONS_ERROR;
5654 : }
5655 1 : return ldb_module_done(ar->req, NULL, NULL,
5656 : ret);
5657 : }
5658 :
5659 : /*
5660 : callback for replmd_replicated_apply_add()
5661 : This copes with the creation of conflict records in the case where
5662 : the DN exists, but with a different objectGUID
5663 : */
5664 380532 : static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5665 : {
5666 0 : struct replmd_replicated_request *ar =
5667 380532 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
5668 :
5669 380532 : if (ar->objs->objects[ar->index_current].last_known_parent) {
5670 : /* This is like a conflict DN, where we put the object in LostAndFound
5671 : see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5672 8 : return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5673 : }
5674 :
5675 380524 : return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5676 : }
5677 :
5678 : /*
5679 : this is called when a new object comes in over DRS
5680 : */
5681 380532 : static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5682 : {
5683 0 : struct ldb_context *ldb;
5684 0 : struct ldb_request *change_req;
5685 0 : enum ndr_err_code ndr_err;
5686 0 : struct ldb_message *msg;
5687 0 : struct replPropertyMetaDataBlob *md;
5688 0 : struct ldb_val md_value;
5689 0 : unsigned int i;
5690 0 : int ret;
5691 380532 : bool remote_isDeleted = false;
5692 0 : bool is_schema_nc;
5693 0 : NTTIME now;
5694 380532 : time_t t = time(NULL);
5695 0 : const struct ldb_val *rdn_val;
5696 0 : struct replmd_private *replmd_private =
5697 380532 : talloc_get_type(ldb_module_get_private(ar->module),
5698 : struct replmd_private);
5699 380532 : unix_to_nt_time(&now, t);
5700 :
5701 380532 : ldb = ldb_module_get_ctx(ar->module);
5702 380532 : msg = ar->objs->objects[ar->index_current].msg;
5703 380532 : md = ar->objs->objects[ar->index_current].meta_data;
5704 380532 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5705 :
5706 380532 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5707 380532 : if (ret != LDB_SUCCESS) {
5708 0 : return replmd_replicated_request_error(ar, ret);
5709 : }
5710 :
5711 380532 : ret = dsdb_msg_add_guid(msg,
5712 380532 : &ar->objs->objects[ar->index_current].object_guid,
5713 : "objectGUID");
5714 380532 : if (ret != LDB_SUCCESS) {
5715 0 : return replmd_replicated_request_error(ar, ret);
5716 : }
5717 :
5718 380532 : ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5719 380532 : if (ret != LDB_SUCCESS) {
5720 0 : return replmd_replicated_request_error(ar, ret);
5721 : }
5722 :
5723 380532 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5724 380532 : if (ret != LDB_SUCCESS) {
5725 0 : return replmd_replicated_request_error(ar, ret);
5726 : }
5727 :
5728 380532 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5729 380532 : if (ret != LDB_SUCCESS) {
5730 0 : return replmd_replicated_request_error(ar, ret);
5731 : }
5732 :
5733 : /* remove any message elements that have zero values */
5734 7552569 : for (i=0; i<msg->num_elements; i++) {
5735 7172037 : struct ldb_message_element *el = &msg->elements[i];
5736 :
5737 7172037 : if (el->num_values == 0) {
5738 548403 : if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5739 0 : ldb_asprintf_errstring(ldb, __location__
5740 : ": empty objectClass sent on %s, aborting replication\n",
5741 : ldb_dn_get_linearized(msg->dn));
5742 0 : return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5743 : }
5744 :
5745 548403 : DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5746 : el->name));
5747 548403 : ldb_msg_remove_element(msg, &msg->elements[i]);
5748 548403 : i--;
5749 548403 : continue;
5750 : }
5751 : }
5752 :
5753 380532 : if (DEBUGLVL(8)) {
5754 0 : struct GUID_txt_buf guid_txt;
5755 :
5756 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
5757 : LDB_CHANGETYPE_ADD,
5758 : msg);
5759 0 : DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5760 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5761 : s));
5762 0 : talloc_free(s);
5763 380532 : } else if (DEBUGLVL(4)) {
5764 0 : struct GUID_txt_buf guid_txt;
5765 0 : DEBUG(4, ("DRS replication add DN of %s is %s\n",
5766 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5767 : ldb_dn_get_linearized(msg->dn)));
5768 : }
5769 380532 : remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5770 : "isDeleted", false);
5771 :
5772 : /*
5773 : * the meta data array is already sorted by the caller, except
5774 : * for the RDN, which needs to be added.
5775 : */
5776 :
5777 :
5778 380532 : rdn_val = ldb_dn_get_rdn_val(msg->dn);
5779 380532 : ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5780 : md, ar, now, is_schema_nc,
5781 : false);
5782 380532 : if (ret != LDB_SUCCESS) {
5783 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5784 0 : return replmd_replicated_request_error(ar, ret);
5785 : }
5786 :
5787 380532 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5788 380532 : if (ret != LDB_SUCCESS) {
5789 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5790 0 : return replmd_replicated_request_error(ar, ret);
5791 : }
5792 :
5793 6410973 : for (i=0; i < md->ctr.ctr1.count; i++) {
5794 6030441 : md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5795 : }
5796 380532 : ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5797 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5798 380532 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5799 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5800 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5801 : }
5802 380532 : ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5803 380532 : if (ret != LDB_SUCCESS) {
5804 0 : return replmd_replicated_request_error(ar, ret);
5805 : }
5806 :
5807 380532 : replmd_ldb_message_sort(msg, ar->schema);
5808 :
5809 380532 : if (!remote_isDeleted) {
5810 : /*
5811 : * Ensure any local ACL inheritance is applied from
5812 : * the parent object.
5813 : *
5814 : * This is needed because descriptor is above
5815 : * repl_meta_data in the module stack, so this will
5816 : * not be triggered 'naturally' by the flow of
5817 : * operations.
5818 : */
5819 870878 : ret = dsdb_module_schedule_sd_propagation(ar->module,
5820 290416 : ar->objs->partition_dn,
5821 290416 : ar->objs->objects[ar->index_current].object_guid,
5822 290416 : ar->objs->objects[ar->index_current].parent_guid ?
5823 290046 : *ar->objs->objects[ar->index_current].parent_guid :
5824 370 : GUID_zero(),
5825 : true);
5826 290416 : if (ret != LDB_SUCCESS) {
5827 0 : return replmd_replicated_request_error(ar, ret);
5828 : }
5829 : }
5830 :
5831 380532 : ar->isDeleted = remote_isDeleted;
5832 :
5833 380532 : ret = ldb_build_add_req(&change_req,
5834 : ldb,
5835 : ar,
5836 : msg,
5837 : ar->controls,
5838 : ar,
5839 : replmd_op_add_callback,
5840 : ar->req);
5841 380532 : LDB_REQ_SET_LOCATION(change_req);
5842 380532 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5843 :
5844 : /* current partition control needed by "repmd_op_callback" */
5845 380532 : ret = ldb_request_add_control(change_req,
5846 : DSDB_CONTROL_CURRENT_PARTITION_OID,
5847 : false, NULL);
5848 380532 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5849 :
5850 380532 : if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5851 : /* this tells the partition module to make it a
5852 : partial replica if creating an NC */
5853 9856 : ret = ldb_request_add_control(change_req,
5854 : DSDB_CONTROL_PARTIAL_REPLICA,
5855 : false, NULL);
5856 9856 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5857 : }
5858 :
5859 380532 : return ldb_next_request(ar->module, change_req);
5860 : }
5861 :
5862 1333693 : static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5863 : struct ldb_reply *ares)
5864 : {
5865 1333693 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
5866 : struct replmd_replicated_request);
5867 0 : int ret;
5868 :
5869 1333693 : if (!ares) {
5870 0 : return ldb_module_done(ar->req, NULL, NULL,
5871 : LDB_ERR_OPERATIONS_ERROR);
5872 : }
5873 :
5874 : /*
5875 : * The error NO_SUCH_OBJECT is not expected, unless the search
5876 : * base is the partition DN, and that case doesn't happen here
5877 : * because then we wouldn't get a parent_guid_value in any
5878 : * case.
5879 : */
5880 1333693 : if (ares->error != LDB_SUCCESS) {
5881 0 : return ldb_module_done(ar->req, ares->controls,
5882 : ares->response, ares->error);
5883 : }
5884 :
5885 1333693 : switch (ares->type) {
5886 451416 : case LDB_REPLY_ENTRY:
5887 : {
5888 451416 : struct ldb_message *parent_msg = ares->message;
5889 451416 : struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5890 451416 : struct ldb_dn *parent_dn = NULL;
5891 0 : int comp_num;
5892 :
5893 451416 : if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5894 334190 : && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5895 : /* Per MS-DRSR 4.1.10.6.10
5896 : * FindBestParentObject we need to move this
5897 : * new object under a deleted object to
5898 : * lost-and-found */
5899 0 : struct ldb_dn *nc_root;
5900 :
5901 8 : ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5902 8 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5903 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5904 : "No suitable NC root found for %s. "
5905 : "We need to move this object because parent object %s "
5906 : "is deleted, but this object is not.",
5907 : ldb_dn_get_linearized(msg->dn),
5908 : ldb_dn_get_linearized(parent_msg->dn));
5909 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5910 8 : } else if (ret != LDB_SUCCESS) {
5911 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5912 : "Unable to find NC root for %s: %s. "
5913 : "We need to move this object because parent object %s "
5914 : "is deleted, but this object is not.",
5915 : ldb_dn_get_linearized(msg->dn),
5916 : ldb_errstring(ldb_module_get_ctx(ar->module)),
5917 : ldb_dn_get_linearized(parent_msg->dn));
5918 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5919 : }
5920 :
5921 8 : ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5922 : nc_root,
5923 : DS_GUID_LOSTANDFOUND_CONTAINER,
5924 : &parent_dn);
5925 8 : if (ret != LDB_SUCCESS) {
5926 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5927 : "Unable to find LostAndFound Container for %s "
5928 : "in partition %s: %s. "
5929 : "We need to move this object because parent object %s "
5930 : "is deleted, but this object is not.",
5931 : ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5932 : ldb_errstring(ldb_module_get_ctx(ar->module)),
5933 : ldb_dn_get_linearized(parent_msg->dn));
5934 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5935 : }
5936 8 : ar->objs->objects[ar->index_current].last_known_parent
5937 8 : = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5938 :
5939 : } else {
5940 0 : parent_dn
5941 451408 : = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5942 :
5943 : }
5944 451416 : ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5945 :
5946 451416 : comp_num = ldb_dn_get_comp_num(msg->dn);
5947 451416 : if (comp_num > 1) {
5948 451416 : if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5949 0 : talloc_free(ares);
5950 0 : return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5951 : }
5952 : }
5953 451416 : if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5954 0 : talloc_free(ares);
5955 0 : return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5956 : }
5957 451416 : break;
5958 : }
5959 430831 : case LDB_REPLY_REFERRAL:
5960 : /* we ignore referrals */
5961 430831 : break;
5962 :
5963 451446 : case LDB_REPLY_DONE:
5964 :
5965 451446 : if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5966 0 : struct GUID_txt_buf str_buf;
5967 30 : if (ar->search_msg != NULL) {
5968 4 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5969 : "No parent with GUID %s found for object locally known as %s",
5970 4 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5971 4 : ldb_dn_get_linearized(ar->search_msg->dn));
5972 : } else {
5973 26 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5974 : "No parent with GUID %s found for object remotely known as %s",
5975 26 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5976 26 : ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5977 : }
5978 :
5979 : /*
5980 : * This error code is really important, as it
5981 : * is the flag back to the callers to retry
5982 : * this with DRSUAPI_DRS_GET_ANC, and so get
5983 : * the parent objects before the child
5984 : * objects
5985 : */
5986 30 : return ldb_module_done(ar->req, NULL, NULL,
5987 30 : replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5988 : }
5989 :
5990 451416 : if (ar->search_msg != NULL) {
5991 71254 : ret = replmd_replicated_apply_merge(ar);
5992 : } else {
5993 380162 : ret = replmd_replicated_apply_add(ar);
5994 : }
5995 451416 : if (ret != LDB_SUCCESS) {
5996 1 : return ldb_module_done(ar->req, NULL, NULL, ret);
5997 : }
5998 : }
5999 :
6000 1333662 : talloc_free(ares);
6001 1333662 : return LDB_SUCCESS;
6002 : }
6003 :
6004 : /*
6005 : * Look for the parent object, so we put the new object in the right
6006 : * place This is akin to NameObject in MS-DRSR - this routine and the
6007 : * callbacks find the right parent name, and correct name for this
6008 : * object
6009 : */
6010 :
6011 452049 : static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
6012 : {
6013 0 : struct ldb_context *ldb;
6014 0 : int ret;
6015 0 : char *tmp_str;
6016 0 : char *filter;
6017 0 : struct ldb_request *search_req;
6018 0 : static const char *attrs[] = {"isDeleted", NULL};
6019 0 : struct GUID_txt_buf guid_str_buf;
6020 :
6021 452049 : ldb = ldb_module_get_ctx(ar->module);
6022 :
6023 452049 : if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
6024 603 : if (ar->search_msg != NULL) {
6025 233 : return replmd_replicated_apply_merge(ar);
6026 : } else {
6027 370 : return replmd_replicated_apply_add(ar);
6028 : }
6029 : }
6030 :
6031 451446 : tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6032 : &guid_str_buf);
6033 :
6034 451446 : filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6035 451446 : if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6036 :
6037 451446 : ret = ldb_build_search_req(&search_req,
6038 : ldb,
6039 : ar,
6040 451446 : ar->objs->partition_dn,
6041 : LDB_SCOPE_SUBTREE,
6042 : filter,
6043 : attrs,
6044 : NULL,
6045 : ar,
6046 : replmd_replicated_apply_search_for_parent_callback,
6047 : ar->req);
6048 451446 : LDB_REQ_SET_LOCATION(search_req);
6049 :
6050 451446 : ret = dsdb_request_add_controls(search_req,
6051 : DSDB_SEARCH_SHOW_RECYCLED|
6052 : DSDB_SEARCH_SHOW_DELETED|
6053 : DSDB_SEARCH_SHOW_EXTENDED_DN);
6054 451446 : if (ret != LDB_SUCCESS) {
6055 0 : return ret;
6056 : }
6057 :
6058 451446 : return ldb_next_request(ar->module, search_req);
6059 : }
6060 :
6061 : /*
6062 : handle renames that come in over DRS replication
6063 : */
6064 8289 : static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
6065 : struct ldb_message *msg,
6066 : struct ldb_request *parent,
6067 : bool *renamed_to_conflict)
6068 : {
6069 0 : int ret;
6070 8289 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
6071 0 : struct ldb_result *res;
6072 0 : struct ldb_dn *conflict_dn;
6073 0 : bool rename_incoming_record;
6074 0 : struct ldb_dn *new_dn;
6075 0 : struct GUID guid;
6076 :
6077 8289 : DEBUG(4,("replmd_replicated_request rename %s => %s\n",
6078 : ldb_dn_get_linearized(ar->search_msg->dn),
6079 : ldb_dn_get_linearized(msg->dn)));
6080 :
6081 :
6082 8289 : ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6083 : DSDB_FLAG_NEXT_MODULE, ar->req);
6084 8289 : if (ret == LDB_SUCCESS) {
6085 8276 : talloc_free(tmp_ctx);
6086 8276 : return ret;
6087 : }
6088 :
6089 13 : if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
6090 0 : talloc_free(tmp_ctx);
6091 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
6092 0 : ldb_dn_get_linearized(ar->search_msg->dn),
6093 : ldb_dn_get_linearized(msg->dn),
6094 : ldb_errstring(ldb_module_get_ctx(ar->module)));
6095 0 : return ret;
6096 : }
6097 :
6098 13 : conflict_dn = msg->dn;
6099 :
6100 :
6101 13 : ret = incoming_dn_should_be_renamed(tmp_ctx, ar, conflict_dn, &res,
6102 : &rename_incoming_record);
6103 13 : if (ret != LDB_SUCCESS) {
6104 1 : goto failed;
6105 : }
6106 :
6107 12 : if (rename_incoming_record) {
6108 :
6109 2 : new_dn = replmd_conflict_dn(msg,
6110 : ldb_module_get_ctx(ar->module),
6111 : msg->dn,
6112 2 : &ar->objs->objects[ar->index_current].object_guid);
6113 2 : if (new_dn == NULL) {
6114 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6115 : "Failed to form conflict DN for %s\n",
6116 : ldb_dn_get_linearized(msg->dn));
6117 :
6118 0 : talloc_free(tmp_ctx);
6119 0 : return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6120 : }
6121 :
6122 2 : ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
6123 : DSDB_FLAG_NEXT_MODULE, ar->req);
6124 2 : if (ret != LDB_SUCCESS) {
6125 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6126 : "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
6127 : ldb_dn_get_linearized(conflict_dn),
6128 0 : ldb_dn_get_linearized(ar->search_msg->dn),
6129 : ldb_dn_get_linearized(new_dn),
6130 : ldb_errstring(ldb_module_get_ctx(ar->module)));
6131 0 : talloc_free(tmp_ctx);
6132 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6133 : }
6134 :
6135 2 : msg->dn = new_dn;
6136 2 : *renamed_to_conflict = true;
6137 2 : talloc_free(tmp_ctx);
6138 2 : return LDB_SUCCESS;
6139 : }
6140 :
6141 : /* we are renaming the existing record */
6142 :
6143 10 : guid = samdb_result_guid(res->msgs[0], "objectGUID");
6144 10 : if (GUID_all_zero(&guid)) {
6145 0 : DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
6146 : ldb_dn_get_linearized(conflict_dn)));
6147 0 : goto failed;
6148 : }
6149 :
6150 10 : new_dn = replmd_conflict_dn(tmp_ctx,
6151 : ldb_module_get_ctx(ar->module),
6152 : conflict_dn, &guid);
6153 10 : if (new_dn == NULL) {
6154 0 : DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
6155 : ldb_dn_get_linearized(conflict_dn)));
6156 0 : goto failed;
6157 : }
6158 :
6159 10 : DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
6160 : ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
6161 :
6162 10 : ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
6163 : DSDB_FLAG_OWN_MODULE, ar->req);
6164 10 : if (ret != LDB_SUCCESS) {
6165 0 : DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
6166 : ldb_dn_get_linearized(conflict_dn),
6167 : ldb_dn_get_linearized(new_dn),
6168 : ldb_errstring(ldb_module_get_ctx(ar->module))));
6169 0 : goto failed;
6170 : }
6171 :
6172 : /*
6173 : * now we need to ensure that the rename is seen as an
6174 : * originating update. We do that with a modify.
6175 : */
6176 10 : ret = replmd_name_modify(ar, ar->req, new_dn);
6177 10 : if (ret != LDB_SUCCESS) {
6178 0 : goto failed;
6179 : }
6180 :
6181 10 : DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
6182 : ldb_dn_get_linearized(ar->search_msg->dn),
6183 : ldb_dn_get_linearized(msg->dn)));
6184 :
6185 : /*
6186 : * With the other record out of the way, do the rename we had
6187 : * at the top again
6188 : */
6189 10 : ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6190 : DSDB_FLAG_NEXT_MODULE, ar->req);
6191 10 : if (ret != LDB_SUCCESS) {
6192 0 : DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6193 : ldb_dn_get_linearized(ar->search_msg->dn),
6194 : ldb_dn_get_linearized(msg->dn),
6195 : ldb_errstring(ldb_module_get_ctx(ar->module))));
6196 0 : goto failed;
6197 : }
6198 :
6199 10 : talloc_free(tmp_ctx);
6200 10 : return ret;
6201 1 : failed:
6202 : /*
6203 : * On failure make the caller get the error
6204 : * This means replication will stop with an error,
6205 : * but there is not much else we can do. In the
6206 : * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6207 : * needed.
6208 : */
6209 1 : if (ret == LDB_SUCCESS) {
6210 0 : ret = LDB_ERR_OPERATIONS_ERROR;
6211 : }
6212 :
6213 1 : talloc_free(tmp_ctx);
6214 1 : return ret;
6215 : }
6216 :
6217 :
6218 119940 : static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6219 : {
6220 0 : struct ldb_context *ldb;
6221 0 : struct ldb_request *change_req;
6222 0 : enum ndr_err_code ndr_err;
6223 0 : struct ldb_message *msg;
6224 0 : struct replPropertyMetaDataBlob *rmd;
6225 0 : struct replPropertyMetaDataBlob omd;
6226 0 : const struct ldb_val *omd_value;
6227 0 : struct replPropertyMetaDataBlob nmd;
6228 0 : struct ldb_val nmd_value;
6229 0 : struct GUID remote_parent_guid;
6230 0 : unsigned int i;
6231 119940 : uint32_t j,ni=0;
6232 119940 : unsigned int removed_attrs = 0;
6233 0 : int ret;
6234 119940 : int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6235 119940 : bool isDeleted = false;
6236 119940 : bool local_isDeleted = false;
6237 119940 : bool remote_isDeleted = false;
6238 119940 : bool take_remote_isDeleted = false;
6239 119940 : bool sd_updated = false;
6240 119940 : bool renamed = false;
6241 119940 : bool renamed_to_conflict = false;
6242 119940 : bool is_schema_nc = false;
6243 0 : NTSTATUS nt_status;
6244 0 : const struct ldb_val *old_rdn, *new_rdn;
6245 0 : struct replmd_private *replmd_private =
6246 119940 : talloc_get_type(ldb_module_get_private(ar->module),
6247 : struct replmd_private);
6248 0 : NTTIME now;
6249 119940 : time_t t = time(NULL);
6250 119940 : unix_to_nt_time(&now, t);
6251 :
6252 119940 : ldb = ldb_module_get_ctx(ar->module);
6253 119940 : msg = ar->objs->objects[ar->index_current].msg;
6254 :
6255 119940 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6256 :
6257 119940 : rmd = ar->objs->objects[ar->index_current].meta_data;
6258 119940 : ZERO_STRUCT(omd);
6259 119940 : omd.version = 1;
6260 :
6261 : /* find existing meta data */
6262 119940 : omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6263 119940 : if (omd_value) {
6264 119940 : ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6265 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6266 119940 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6267 0 : nt_status = ndr_map_error2ntstatus(ndr_err);
6268 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6269 : }
6270 :
6271 119940 : if (omd.version != 1) {
6272 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6273 : }
6274 : }
6275 :
6276 119940 : if (DEBUGLVL(8)) {
6277 0 : struct GUID_txt_buf guid_txt;
6278 :
6279 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
6280 : LDB_CHANGETYPE_MODIFY, msg);
6281 0 : DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6282 : "%s\n"
6283 : "%s\n",
6284 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6285 : s,
6286 : ndr_print_struct_string(s,
6287 : (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6288 : "existing replPropertyMetaData",
6289 : &omd),
6290 : ndr_print_struct_string(s,
6291 : (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6292 : "incoming replPropertyMetaData",
6293 : rmd)));
6294 0 : talloc_free(s);
6295 119940 : } else if (DEBUGLVL(4)) {
6296 0 : struct GUID_txt_buf guid_txt;
6297 :
6298 0 : DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6299 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6300 : &guid_txt),
6301 : ldb_dn_get_linearized(msg->dn)));
6302 : }
6303 :
6304 119940 : local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6305 : "isDeleted", false);
6306 119940 : remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6307 : "isDeleted", false);
6308 :
6309 : /*
6310 : * Fill in the remote_parent_guid with the GUID or an all-zero
6311 : * GUID.
6312 : */
6313 119940 : if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6314 119707 : remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6315 : } else {
6316 233 : remote_parent_guid = GUID_zero();
6317 : }
6318 :
6319 : /*
6320 : * To ensure we follow a complex rename chain around, we have
6321 : * to confirm that the DN is the same (mostly to confirm the
6322 : * RDN) and the parentGUID is the same.
6323 : *
6324 : * This ensures we keep things under the correct parent, which
6325 : * replmd_replicated_handle_rename() will do.
6326 : */
6327 :
6328 119940 : if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6329 111651 : && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6330 111651 : ret = LDB_SUCCESS;
6331 : } else {
6332 : /*
6333 : * handle renames, even just by case that come in over
6334 : * DRS. Changes in the parent DN don't hit us here,
6335 : * because the search for a parent will clean up those
6336 : * components.
6337 : *
6338 : * We also have already filtered out the case where
6339 : * the peer has an older name to what we have (see
6340 : * replmd_replicated_apply_search_callback())
6341 : */
6342 8289 : ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed_to_conflict);
6343 :
6344 : /*
6345 : * This looks strange, but we must set this after any
6346 : * rename, otherwise the SD propegation will not
6347 : * happen (which might matter if we have a new parent)
6348 : *
6349 : * The additional case of calling
6350 : * replmd_op_name_modify_callback (below) is
6351 : * controlled by renamed_to_conflict.
6352 : */
6353 8289 : renamed = true;
6354 : }
6355 :
6356 119940 : if (ret != LDB_SUCCESS) {
6357 2 : ldb_debug(ldb, LDB_DEBUG_FATAL,
6358 : "replmd_replicated_request rename %s => %s failed - %s\n",
6359 1 : ldb_dn_get_linearized(ar->search_msg->dn),
6360 : ldb_dn_get_linearized(msg->dn),
6361 : ldb_errstring(ldb));
6362 1 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6363 : }
6364 :
6365 119939 : if (renamed_to_conflict == true) {
6366 : /*
6367 : * Set the callback to one that will fix up the name
6368 : * metadata on the new conflict DN
6369 : */
6370 2 : callback = replmd_op_name_modify_callback;
6371 : }
6372 :
6373 119939 : ZERO_STRUCT(nmd);
6374 119939 : nmd.version = 1;
6375 119939 : nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6376 119939 : nmd.ctr.ctr1.array = talloc_array(ar,
6377 : struct replPropertyMetaData1,
6378 : nmd.ctr.ctr1.count);
6379 119939 : if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6380 :
6381 : /* first copy the old meta data */
6382 1615138 : for (i=0; i < omd.ctr.ctr1.count; i++) {
6383 1495199 : nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6384 1495199 : ni++;
6385 : }
6386 :
6387 119939 : ar->seq_num = 0;
6388 : /* now merge in the new meta data */
6389 1449964 : for (i=0; i < rmd->ctr.ctr1.count; i++) {
6390 1330025 : bool found = false;
6391 :
6392 11079585 : for (j=0; j < ni; j++) {
6393 0 : bool cmp;
6394 :
6395 11054896 : if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6396 9749560 : continue;
6397 : }
6398 :
6399 1305336 : cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6400 1305336 : ar->objs->dsdb_repl_flags,
6401 1305336 : &nmd.ctr.ctr1.array[j],
6402 1305336 : &rmd->ctr.ctr1.array[i]);
6403 1305336 : if (cmp) {
6404 : /* replace the entry */
6405 763385 : nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6406 763385 : if (ar->seq_num == 0) {
6407 64521 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6408 64521 : if (ret != LDB_SUCCESS) {
6409 0 : return replmd_replicated_request_error(ar, ret);
6410 : }
6411 : }
6412 763385 : nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6413 763385 : switch (nmd.ctr.ctr1.array[j].attid) {
6414 63232 : case DRSUAPI_ATTID_ntSecurityDescriptor:
6415 63232 : sd_updated = true;
6416 63232 : break;
6417 19852 : case DRSUAPI_ATTID_isDeleted:
6418 19852 : take_remote_isDeleted = true;
6419 19852 : break;
6420 680301 : default:
6421 680301 : break;
6422 : }
6423 763385 : found = true;
6424 763385 : break;
6425 : }
6426 :
6427 541951 : if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6428 485230 : DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6429 : msg->elements[i-removed_attrs].name,
6430 : ldb_dn_get_linearized(msg->dn),
6431 : GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6432 : }
6433 :
6434 : /* we don't want to apply this change so remove the attribute */
6435 541951 : ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6436 541951 : removed_attrs++;
6437 :
6438 541951 : found = true;
6439 541951 : break;
6440 : }
6441 :
6442 1330025 : if (found) continue;
6443 :
6444 24689 : nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6445 24689 : if (ar->seq_num == 0) {
6446 7850 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6447 7850 : if (ret != LDB_SUCCESS) {
6448 0 : return replmd_replicated_request_error(ar, ret);
6449 : }
6450 : }
6451 24689 : nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6452 24689 : switch (nmd.ctr.ctr1.array[ni].attid) {
6453 0 : case DRSUAPI_ATTID_ntSecurityDescriptor:
6454 0 : sd_updated = true;
6455 0 : break;
6456 7592 : case DRSUAPI_ATTID_isDeleted:
6457 7592 : take_remote_isDeleted = true;
6458 7592 : break;
6459 17097 : default:
6460 17097 : break;
6461 : }
6462 24689 : ni++;
6463 : }
6464 :
6465 : /*
6466 : * finally correct the size of the meta_data array
6467 : */
6468 119939 : nmd.ctr.ctr1.count = ni;
6469 :
6470 119939 : new_rdn = ldb_dn_get_rdn_val(msg->dn);
6471 119939 : old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6472 :
6473 119939 : if (renamed) {
6474 8288 : ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6475 : &nmd, ar, now, is_schema_nc,
6476 : false);
6477 8288 : if (ret != LDB_SUCCESS) {
6478 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6479 0 : return replmd_replicated_request_error(ar, ret);
6480 : }
6481 : }
6482 : /*
6483 : * sort the new meta data array
6484 : */
6485 119939 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6486 119939 : if (ret != LDB_SUCCESS) {
6487 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6488 0 : return ret;
6489 : }
6490 :
6491 : /*
6492 : * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6493 : * UpdateObject.
6494 : *
6495 : * This also controls SD propagation below
6496 : */
6497 119939 : if (take_remote_isDeleted) {
6498 27444 : isDeleted = remote_isDeleted;
6499 : } else {
6500 92495 : isDeleted = local_isDeleted;
6501 : }
6502 :
6503 119939 : ar->isDeleted = isDeleted;
6504 :
6505 : /*
6506 : * check if some replicated attributes left, otherwise skip the ldb_modify() call
6507 : */
6508 119939 : if (msg->num_elements == 0) {
6509 47568 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6510 : ar->index_current);
6511 :
6512 47568 : return replmd_replicated_apply_isDeleted(ar);
6513 : }
6514 :
6515 72371 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6516 : ar->index_current, msg->num_elements);
6517 :
6518 72371 : if (renamed) {
6519 : /*
6520 : * This is an new name for this object, so we must
6521 : * inherit from the parent
6522 : *
6523 : * This is needed because descriptor is above
6524 : * repl_meta_data in the module stack, so this will
6525 : * not be triggered 'naturally' by the flow of
6526 : * operations.
6527 : */
6528 24864 : ret = dsdb_module_schedule_sd_propagation(ar->module,
6529 8288 : ar->objs->partition_dn,
6530 8288 : ar->objs->objects[ar->index_current].object_guid,
6531 8288 : ar->objs->objects[ar->index_current].parent_guid ?
6532 8288 : *ar->objs->objects[ar->index_current].parent_guid :
6533 0 : GUID_zero(),
6534 : true);
6535 8288 : if (ret != LDB_SUCCESS) {
6536 0 : return ldb_operr(ldb);
6537 : }
6538 : }
6539 :
6540 72371 : if (sd_updated && !isDeleted) {
6541 : /*
6542 : * This is an existing object, so there is no need to
6543 : * inherit from the parent, but we must inherit any
6544 : * incoming changes to our child objects.
6545 : *
6546 : * This is needed because descriptor is above
6547 : * repl_meta_data in the module stack, so this will
6548 : * not be triggered 'naturally' by the flow of
6549 : * operations.
6550 : */
6551 131857 : ret = dsdb_module_schedule_sd_propagation(ar->module,
6552 44030 : ar->objs->partition_dn,
6553 44030 : ar->objs->objects[ar->index_current].object_guid,
6554 44030 : ar->objs->objects[ar->index_current].parent_guid ?
6555 43797 : *ar->objs->objects[ar->index_current].parent_guid :
6556 233 : GUID_zero(),
6557 : false);
6558 44030 : if (ret != LDB_SUCCESS) {
6559 0 : return ldb_operr(ldb);
6560 : }
6561 : }
6562 :
6563 : /* create the meta data value */
6564 72371 : ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6565 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6566 72371 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6567 0 : nt_status = ndr_map_error2ntstatus(ndr_err);
6568 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6569 : }
6570 :
6571 : /*
6572 : * when we know that we'll modify the record, add the whenChanged, uSNChanged
6573 : * and replPopertyMetaData attributes
6574 : */
6575 72371 : ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6576 72371 : if (ret != LDB_SUCCESS) {
6577 0 : return replmd_replicated_request_error(ar, ret);
6578 : }
6579 72371 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6580 72371 : if (ret != LDB_SUCCESS) {
6581 0 : return replmd_replicated_request_error(ar, ret);
6582 : }
6583 72371 : ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6584 72371 : if (ret != LDB_SUCCESS) {
6585 0 : return replmd_replicated_request_error(ar, ret);
6586 : }
6587 :
6588 72371 : replmd_ldb_message_sort(msg, ar->schema);
6589 :
6590 : /* we want to replace the old values */
6591 1085823 : for (i=0; i < msg->num_elements; i++) {
6592 1013452 : msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6593 1013452 : if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6594 63154 : if (msg->elements[i].num_values == 0) {
6595 0 : ldb_asprintf_errstring(ldb, __location__
6596 : ": objectClass removed on %s, aborting replication\n",
6597 : ldb_dn_get_linearized(msg->dn));
6598 0 : return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6599 : }
6600 : }
6601 : }
6602 :
6603 72371 : if (DEBUGLVL(8)) {
6604 0 : struct GUID_txt_buf guid_txt;
6605 :
6606 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
6607 : LDB_CHANGETYPE_MODIFY,
6608 : msg);
6609 0 : DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6610 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6611 : &guid_txt),
6612 : s));
6613 0 : talloc_free(s);
6614 72371 : } else if (DEBUGLVL(4)) {
6615 0 : struct GUID_txt_buf guid_txt;
6616 :
6617 0 : DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6618 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6619 : &guid_txt),
6620 : ldb_dn_get_linearized(msg->dn)));
6621 : }
6622 :
6623 72371 : ret = ldb_build_mod_req(&change_req,
6624 : ldb,
6625 : ar,
6626 : msg,
6627 : ar->controls,
6628 : ar,
6629 : callback,
6630 : ar->req);
6631 72371 : LDB_REQ_SET_LOCATION(change_req);
6632 72371 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6633 :
6634 : /* current partition control needed by "repmd_op_callback" */
6635 72371 : ret = ldb_request_add_control(change_req,
6636 : DSDB_CONTROL_CURRENT_PARTITION_OID,
6637 : false, NULL);
6638 72371 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6639 :
6640 72371 : return ldb_next_request(ar->module, change_req);
6641 : }
6642 :
6643 1180334 : static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6644 : struct ldb_reply *ares)
6645 : {
6646 1180334 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
6647 : struct replmd_replicated_request);
6648 0 : int ret;
6649 :
6650 1180334 : if (!ares) {
6651 0 : return ldb_module_done(ar->req, NULL, NULL,
6652 : LDB_ERR_OPERATIONS_ERROR);
6653 : }
6654 1180334 : if (ares->error != LDB_SUCCESS &&
6655 370 : ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6656 0 : return ldb_module_done(ar->req, ares->controls,
6657 : ares->response, ares->error);
6658 : }
6659 :
6660 1180334 : switch (ares->type) {
6661 119944 : case LDB_REPLY_ENTRY:
6662 119944 : ar->search_msg = talloc_steal(ar, ares->message);
6663 119944 : break;
6664 :
6665 559888 : case LDB_REPLY_REFERRAL:
6666 : /* we ignore referrals */
6667 559888 : break;
6668 :
6669 500502 : case LDB_REPLY_DONE:
6670 : {
6671 0 : struct replPropertyMetaData1 *md_remote;
6672 0 : struct replPropertyMetaData1 *md_local;
6673 :
6674 0 : struct replPropertyMetaDataBlob omd;
6675 0 : const struct ldb_val *omd_value;
6676 0 : struct replPropertyMetaDataBlob *rmd;
6677 0 : struct ldb_message *msg;
6678 0 : int instanceType;
6679 500502 : ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6680 500502 : ar->objs->objects[ar->index_current].last_known_parent = NULL;
6681 :
6682 : /*
6683 : * This is the ADD case, find the appropriate parent,
6684 : * as this object doesn't exist locally:
6685 : */
6686 500502 : if (ar->search_msg == NULL) {
6687 380558 : ret = replmd_replicated_apply_search_for_parent(ar);
6688 380558 : if (ret != LDB_SUCCESS) {
6689 380560 : return ldb_module_done(ar->req, NULL, NULL, ret);
6690 : }
6691 380558 : talloc_free(ares);
6692 380558 : return LDB_SUCCESS;
6693 : }
6694 :
6695 : /*
6696 : * Otherwise, in the MERGE case, work out if we are
6697 : * attempting a rename, and if so find the parent the
6698 : * newly renamed object wants to belong under (which
6699 : * may not be the parent in it's attached string DN
6700 : */
6701 119944 : rmd = ar->objs->objects[ar->index_current].meta_data;
6702 119944 : ZERO_STRUCT(omd);
6703 119944 : omd.version = 1;
6704 :
6705 : /* find existing meta data */
6706 119944 : omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6707 119944 : if (omd_value) {
6708 0 : enum ndr_err_code ndr_err;
6709 119944 : ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6710 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6711 119944 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6712 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6713 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6714 : }
6715 :
6716 119944 : if (omd.version != 1) {
6717 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6718 : }
6719 : }
6720 :
6721 119944 : ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6722 :
6723 119944 : instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6724 119944 : if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6725 119492 : && GUID_all_zero(&ar->local_parent_guid)) {
6726 0 : DEBUG(0, ("Refusing to replicate new version of %s "
6727 : "as local object has an all-zero parentGUID attribute, "
6728 : "despite not being an NC root\n",
6729 : ldb_dn_get_linearized(ar->search_msg->dn)));
6730 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6731 : }
6732 :
6733 : /*
6734 : * now we need to check for double renames. We could have a
6735 : * local rename pending which our replication partner hasn't
6736 : * received yet. We choose which one wins by looking at the
6737 : * attribute stamps on the two objects, the newer one wins.
6738 : *
6739 : * This also simply applies the correct algorithms for
6740 : * determining if a change was made to name at all, or
6741 : * if the object has just been renamed under the same
6742 : * parent.
6743 : */
6744 119944 : md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6745 119944 : md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6746 119944 : if (!md_local) {
6747 0 : DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6748 : ldb_dn_get_linearized(ar->search_msg->dn)));
6749 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6750 : }
6751 :
6752 : /*
6753 : * if there is no name attribute given then we have to assume the
6754 : * object we've received has the older name
6755 : */
6756 119944 : if (replmd_replPropertyMetaData1_new_should_be_taken(
6757 119944 : ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6758 : md_local, md_remote)) {
6759 0 : struct GUID_txt_buf p_guid_local;
6760 0 : struct GUID_txt_buf p_guid_remote;
6761 71491 : msg = ar->objs->objects[ar->index_current].msg;
6762 :
6763 : /* Merge on the existing object, with rename */
6764 :
6765 71491 : DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6766 : "as incoming object changing to %s under %s\n",
6767 : ldb_dn_get_linearized(ar->search_msg->dn),
6768 : GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6769 : ldb_dn_get_linearized(msg->dn),
6770 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6771 : &p_guid_remote)));
6772 71491 : ret = replmd_replicated_apply_search_for_parent(ar);
6773 : } else {
6774 0 : struct GUID_txt_buf p_guid_local;
6775 0 : struct GUID_txt_buf p_guid_remote;
6776 48453 : msg = ar->objs->objects[ar->index_current].msg;
6777 :
6778 : /*
6779 : * Merge on the existing object, force no
6780 : * rename (code below just to explain why in
6781 : * the DEBUG() logs)
6782 : */
6783 :
6784 48453 : if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6785 : ldb_dn_get_linearized(msg->dn)) == 0) {
6786 48366 : if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6787 48147 : GUID_equal(&ar->local_parent_guid,
6788 48147 : ar->objs->objects[ar->index_current].parent_guid)
6789 48147 : == false) {
6790 0 : DEBUG(4,(__location__ ": Keeping object %s at under %s "
6791 : "despite incoming object changing parent to %s\n",
6792 : ldb_dn_get_linearized(ar->search_msg->dn),
6793 : GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6794 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6795 : &p_guid_remote)));
6796 : }
6797 : } else {
6798 87 : DEBUG(4,(__location__ ": Keeping object %s at under %s "
6799 : " and rejecting older rename to %s under %s\n",
6800 : ldb_dn_get_linearized(ar->search_msg->dn),
6801 : GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6802 : ldb_dn_get_linearized(msg->dn),
6803 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6804 : &p_guid_remote)));
6805 : }
6806 : /*
6807 : * This assignment ensures that the strcmp()
6808 : * and GUID_equal() calls in
6809 : * replmd_replicated_apply_merge() avoids the
6810 : * rename call
6811 : */
6812 48453 : ar->objs->objects[ar->index_current].parent_guid =
6813 48453 : &ar->local_parent_guid;
6814 :
6815 48453 : msg->dn = ar->search_msg->dn;
6816 48453 : ret = replmd_replicated_apply_merge(ar);
6817 : }
6818 119944 : if (ret != LDB_SUCCESS) {
6819 2 : return ldb_module_done(ar->req, NULL, NULL, ret);
6820 : }
6821 : }
6822 : }
6823 :
6824 799774 : talloc_free(ares);
6825 799774 : return LDB_SUCCESS;
6826 : }
6827 :
6828 : /**
6829 : * Returns true if we can group together processing this link attribute,
6830 : * i.e. it has the same source-object and attribute ID as other links
6831 : * already in the group
6832 : */
6833 12870 : static bool la_entry_matches_group(struct la_entry *la_entry,
6834 : struct la_group *la_group)
6835 : {
6836 12870 : struct la_entry *prev = la_group->la_entries;
6837 :
6838 24123 : return (la_entry->la->attid == prev->la->attid &&
6839 11253 : GUID_equal(&la_entry->la->identifier->guid,
6840 11253 : &prev->la->identifier->guid));
6841 : }
6842 :
6843 : /**
6844 : * Creates a new la_entry to store replication info for a single
6845 : * linked attribute.
6846 : */
6847 : static struct la_entry *
6848 13650 : create_la_entry(struct replmd_private *replmd_private,
6849 : struct drsuapi_DsReplicaLinkedAttribute *la,
6850 : uint32_t dsdb_repl_flags)
6851 : {
6852 0 : struct la_entry *la_entry;
6853 :
6854 13650 : if (replmd_private->la_ctx == NULL) {
6855 369 : replmd_private->la_ctx = talloc_new(replmd_private);
6856 : }
6857 13650 : la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6858 13650 : if (la_entry == NULL) {
6859 0 : return NULL;
6860 : }
6861 13650 : la_entry->la = talloc(la_entry,
6862 : struct drsuapi_DsReplicaLinkedAttribute);
6863 13650 : if (la_entry->la == NULL) {
6864 0 : talloc_free(la_entry);
6865 0 : return NULL;
6866 : }
6867 13650 : *la_entry->la = *la;
6868 13650 : la_entry->dsdb_repl_flags = dsdb_repl_flags;
6869 :
6870 : /*
6871 : * we need to steal the non-scalars so they stay
6872 : * around until the end of the transaction
6873 : */
6874 13650 : talloc_steal(la_entry->la, la_entry->la->identifier);
6875 13650 : talloc_steal(la_entry->la, la_entry->la->value.blob);
6876 :
6877 13650 : return la_entry;
6878 : }
6879 :
6880 : /**
6881 : * Stores the linked attributes received in the replication chunk - these get
6882 : * applied at the end of the transaction. We also check that each linked
6883 : * attribute is valid, i.e. source and target objects are known.
6884 : */
6885 3897 : static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6886 : {
6887 3897 : int ret = LDB_SUCCESS;
6888 0 : uint32_t i;
6889 3897 : struct ldb_module *module = ar->module;
6890 0 : struct replmd_private *replmd_private =
6891 3897 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6892 3897 : struct la_group *la_group = NULL;
6893 0 : struct ldb_context *ldb;
6894 3897 : TALLOC_CTX *tmp_ctx = NULL;
6895 3897 : struct ldb_message *src_msg = NULL;
6896 3897 : const struct dsdb_attribute *attr = NULL;
6897 :
6898 3897 : ldb = ldb_module_get_ctx(module);
6899 :
6900 3897 : DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6901 :
6902 : /* save away the linked attributes for the end of the transaction */
6903 17509 : for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6904 0 : struct la_entry *la_entry;
6905 0 : bool new_srcobj;
6906 :
6907 : /* create an entry to store the received link attribute info */
6908 13650 : la_entry = create_la_entry(replmd_private,
6909 13650 : &ar->objs->linked_attributes[i],
6910 13650 : ar->objs->dsdb_repl_flags);
6911 13650 : if (la_entry == NULL) {
6912 0 : ldb_oom(ldb);
6913 0 : return LDB_ERR_OPERATIONS_ERROR;
6914 : }
6915 :
6916 : /*
6917 : * check if we're still dealing with the same source object
6918 : * as the last link
6919 : */
6920 26520 : new_srcobj = (la_group == NULL ||
6921 12870 : !la_entry_matches_group(la_entry, la_group));
6922 :
6923 13650 : if (new_srcobj) {
6924 :
6925 : /* get a new mem_ctx to lookup the source object */
6926 5368 : TALLOC_FREE(tmp_ctx);
6927 5368 : tmp_ctx = talloc_new(ar);
6928 5368 : if (tmp_ctx == NULL) {
6929 0 : ldb_oom(ldb);
6930 0 : return LDB_ERR_OPERATIONS_ERROR;
6931 : }
6932 :
6933 : /* verify the link source exists */
6934 5368 : ret = replmd_get_la_entry_source(module, la_entry,
6935 : tmp_ctx, &attr,
6936 : &src_msg);
6937 :
6938 : /*
6939 : * When we fail to find the source object, the error
6940 : * code we pass back here is really important. It flags
6941 : * back to the callers to retry this request with
6942 : * DRSUAPI_DRS_GET_ANC. This case should never happen
6943 : * if we're replicating from a Samba DC, but it is
6944 : * needed to talk to a Windows DC
6945 : */
6946 5368 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6947 0 : WERROR err = WERR_DS_DRA_MISSING_PARENT;
6948 0 : ret = replmd_replicated_request_werror(ar,
6949 : err);
6950 0 : break;
6951 : }
6952 : }
6953 :
6954 13650 : ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6955 13650 : src_msg->dn, attr);
6956 13650 : if (ret != LDB_SUCCESS) {
6957 38 : break;
6958 : }
6959 :
6960 : /* group the links together by source-object for efficiency */
6961 13612 : if (new_srcobj) {
6962 5343 : la_group = talloc_zero(replmd_private->la_ctx,
6963 : struct la_group);
6964 5343 : if (la_group == NULL) {
6965 0 : ldb_oom(ldb);
6966 0 : return LDB_ERR_OPERATIONS_ERROR;
6967 : }
6968 5343 : DLIST_ADD(replmd_private->la_list, la_group);
6969 : }
6970 13612 : DLIST_ADD(la_group->la_entries, la_entry);
6971 13612 : replmd_private->total_links++;
6972 : }
6973 :
6974 3897 : TALLOC_FREE(tmp_ctx);
6975 3897 : return ret;
6976 : }
6977 :
6978 : static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6979 :
6980 504399 : static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6981 : {
6982 0 : struct ldb_context *ldb;
6983 0 : int ret;
6984 0 : char *tmp_str;
6985 0 : char *filter;
6986 0 : struct ldb_request *search_req;
6987 0 : static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6988 : "parentGUID", "instanceType",
6989 : "replPropertyMetaData", "nTSecurityDescriptor",
6990 : "isDeleted", NULL };
6991 0 : struct GUID_txt_buf guid_str_buf;
6992 :
6993 504399 : if (ar->index_current >= ar->objs->num_objects) {
6994 :
6995 : /*
6996 : * Now that we've applied all the objects, check the new linked
6997 : * attributes and store them (we apply them in .prepare_commit)
6998 : */
6999 3897 : ret = replmd_store_linked_attributes(ar);
7000 :
7001 3897 : if (ret != LDB_SUCCESS) {
7002 38 : return ret;
7003 : }
7004 :
7005 : /* done applying objects, move on to the next stage */
7006 3859 : return replmd_replicated_uptodate_vector(ar);
7007 : }
7008 :
7009 500502 : ldb = ldb_module_get_ctx(ar->module);
7010 500502 : ar->search_msg = NULL;
7011 500502 : ar->isDeleted = false;
7012 :
7013 500502 : tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
7014 : &guid_str_buf);
7015 :
7016 500502 : filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
7017 500502 : if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7018 :
7019 500502 : ret = ldb_build_search_req(&search_req,
7020 : ldb,
7021 : ar,
7022 500502 : ar->objs->partition_dn,
7023 : LDB_SCOPE_SUBTREE,
7024 : filter,
7025 : attrs,
7026 : NULL,
7027 : ar,
7028 : replmd_replicated_apply_search_callback,
7029 : ar->req);
7030 500502 : LDB_REQ_SET_LOCATION(search_req);
7031 :
7032 : /*
7033 : * We set DSDB_SEARCH_SHOW_EXTENDED_DN to get the GUID on the
7034 : * DN. This in turn helps our operational module find the
7035 : * record by GUID, not DN lookup which is more error prone if
7036 : * DN indexing changes. We prefer to keep chasing GUIDs
7037 : * around if possible, even within a transaction.
7038 : *
7039 : * The aim here is to keep replication moving and allow a
7040 : * reindex later.
7041 : */
7042 500502 : ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED
7043 : |DSDB_SEARCH_SHOW_EXTENDED_DN);
7044 :
7045 500502 : if (ret != LDB_SUCCESS) {
7046 0 : return ret;
7047 : }
7048 :
7049 500502 : return ldb_next_request(ar->module, search_req);
7050 : }
7051 :
7052 : /*
7053 : * Returns true if we need to do extra processing to handle deleted object
7054 : * changes received via replication
7055 : */
7056 500470 : static bool replmd_should_apply_isDeleted(struct replmd_replicated_request *ar,
7057 : struct ldb_message *msg)
7058 : {
7059 0 : struct ldb_dn *deleted_objects_dn;
7060 0 : int ret;
7061 :
7062 500470 : if (!ar->isDeleted) {
7063 :
7064 : /* not a deleted object, so don't set isDeleted */
7065 348768 : return false;
7066 : }
7067 :
7068 151702 : ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module),
7069 : msg, msg->dn,
7070 : &deleted_objects_dn);
7071 :
7072 : /*
7073 : * if the Deleted Object container lookup failed, then just apply
7074 : * isDeleted (note that it doesn't exist for the Schema partition)
7075 : */
7076 151702 : if (ret != LDB_SUCCESS) {
7077 0 : return true;
7078 : }
7079 :
7080 : /*
7081 : * the Deleted Objects container has isDeleted set but is not entirely
7082 : * a deleted object, so DON'T re-apply isDeleted to it
7083 : */
7084 151702 : if (ldb_dn_compare(msg->dn, deleted_objects_dn) == 0) {
7085 412 : return false;
7086 : }
7087 :
7088 151290 : return true;
7089 : }
7090 :
7091 : /*
7092 : * This is essentially a wrapper for replmd_replicated_apply_next()
7093 : *
7094 : * This is needed to ensure that both codepaths call this handler.
7095 : */
7096 500470 : static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
7097 : {
7098 500470 : struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
7099 0 : int ret;
7100 0 : bool apply_isDeleted;
7101 500470 : struct ldb_request *del_req = NULL;
7102 500470 : struct ldb_result *res = NULL;
7103 500470 : TALLOC_CTX *tmp_ctx = NULL;
7104 :
7105 500470 : apply_isDeleted = replmd_should_apply_isDeleted(ar, msg);
7106 :
7107 500470 : if (!apply_isDeleted) {
7108 :
7109 : /* nothing to do */
7110 349180 : ar->index_current++;
7111 349180 : return replmd_replicated_apply_next(ar);
7112 : }
7113 :
7114 : /*
7115 : * Do a delete here again, so that if there is
7116 : * anything local that conflicts with this
7117 : * object being deleted, it is removed. This
7118 : * includes links. See MS-DRSR 4.1.10.6.9
7119 : * UpdateObject.
7120 : *
7121 : * If the object is already deleted, and there
7122 : * is no more work required, it doesn't do
7123 : * anything.
7124 : */
7125 :
7126 : /* This has been updated to point to the DN we eventually did the modify on */
7127 :
7128 151290 : tmp_ctx = talloc_new(ar);
7129 151290 : if (!tmp_ctx) {
7130 0 : ret = ldb_oom(ldb_module_get_ctx(ar->module));
7131 0 : return ret;
7132 : }
7133 :
7134 151290 : res = talloc_zero(tmp_ctx, struct ldb_result);
7135 151290 : if (!res) {
7136 0 : ret = ldb_oom(ldb_module_get_ctx(ar->module));
7137 0 : talloc_free(tmp_ctx);
7138 0 : return ret;
7139 : }
7140 :
7141 : /* Build a delete request, which hopefully will artually turn into nothing */
7142 151290 : ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
7143 : msg->dn,
7144 : NULL,
7145 : res,
7146 : ldb_modify_default_callback,
7147 : ar->req);
7148 151290 : LDB_REQ_SET_LOCATION(del_req);
7149 151290 : if (ret != LDB_SUCCESS) {
7150 0 : talloc_free(tmp_ctx);
7151 0 : return ret;
7152 : }
7153 :
7154 : /*
7155 : * This is the guts of the call, call back
7156 : * into our delete code, but setting the
7157 : * re_delete flag so we delete anything that
7158 : * shouldn't be there on a deleted or recycled
7159 : * object
7160 : */
7161 151290 : ret = replmd_delete_internals(ar->module, del_req, true);
7162 151290 : if (ret == LDB_SUCCESS) {
7163 151290 : ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
7164 : }
7165 :
7166 151290 : talloc_free(tmp_ctx);
7167 151290 : if (ret != LDB_SUCCESS) {
7168 0 : return ret;
7169 : }
7170 :
7171 151290 : ar->index_current++;
7172 151290 : return replmd_replicated_apply_next(ar);
7173 : }
7174 :
7175 3859 : static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
7176 : struct ldb_reply *ares)
7177 : {
7178 0 : struct ldb_context *ldb;
7179 3859 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
7180 : struct replmd_replicated_request);
7181 3859 : ldb = ldb_module_get_ctx(ar->module);
7182 :
7183 3859 : if (!ares) {
7184 0 : return ldb_module_done(ar->req, NULL, NULL,
7185 : LDB_ERR_OPERATIONS_ERROR);
7186 : }
7187 3859 : if (ares->error != LDB_SUCCESS) {
7188 0 : return ldb_module_done(ar->req, ares->controls,
7189 : ares->response, ares->error);
7190 : }
7191 :
7192 3859 : if (ares->type != LDB_REPLY_DONE) {
7193 0 : ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
7194 0 : return ldb_module_done(ar->req, NULL, NULL,
7195 : LDB_ERR_OPERATIONS_ERROR);
7196 : }
7197 :
7198 3859 : talloc_free(ares);
7199 :
7200 3859 : return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7201 : }
7202 :
7203 3859 : static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
7204 : {
7205 0 : struct ldb_context *ldb;
7206 0 : struct ldb_request *change_req;
7207 0 : enum ndr_err_code ndr_err;
7208 0 : struct ldb_message *msg;
7209 0 : struct replUpToDateVectorBlob ouv;
7210 0 : const struct ldb_val *ouv_value;
7211 0 : const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
7212 0 : struct replUpToDateVectorBlob nuv;
7213 0 : struct ldb_val nuv_value;
7214 3859 : struct ldb_message_element *nuv_el = NULL;
7215 3859 : struct ldb_message_element *orf_el = NULL;
7216 0 : struct repsFromToBlob nrf;
7217 3859 : struct ldb_val *nrf_value = NULL;
7218 3859 : struct ldb_message_element *nrf_el = NULL;
7219 0 : unsigned int i;
7220 3859 : uint32_t j,ni=0;
7221 3859 : bool found = false;
7222 3859 : time_t t = time(NULL);
7223 0 : NTTIME now;
7224 0 : int ret;
7225 0 : uint32_t instanceType;
7226 :
7227 3859 : ldb = ldb_module_get_ctx(ar->module);
7228 3859 : ruv = ar->objs->uptodateness_vector;
7229 3859 : ZERO_STRUCT(ouv);
7230 3859 : ouv.version = 2;
7231 3859 : ZERO_STRUCT(nuv);
7232 3859 : nuv.version = 2;
7233 :
7234 3859 : unix_to_nt_time(&now, t);
7235 :
7236 3859 : if (ar->search_msg == NULL) {
7237 : /* this happens for a REPL_OBJ call where we are
7238 : creating the target object by replicating it. The
7239 : subdomain join code does this for the partition DN
7240 : */
7241 0 : DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
7242 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7243 : }
7244 :
7245 3859 : instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
7246 3859 : if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
7247 0 : DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
7248 : ldb_dn_get_linearized(ar->search_msg->dn)));
7249 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7250 : }
7251 :
7252 : /*
7253 : * first create the new replUpToDateVector
7254 : */
7255 3859 : ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
7256 3859 : if (ouv_value) {
7257 3467 : ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
7258 : (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
7259 3467 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7260 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7261 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7262 : }
7263 :
7264 3467 : if (ouv.version != 2) {
7265 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7266 : }
7267 : }
7268 :
7269 : /*
7270 : * the new uptodateness vector will at least
7271 : * contain 1 entry, one for the source_dsa
7272 : *
7273 : * plus optional values from our old vector and the one from the source_dsa
7274 : */
7275 3859 : nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
7276 3859 : if (ruv) nuv.ctr.ctr2.count += ruv->count;
7277 3859 : nuv.ctr.ctr2.cursors = talloc_array(ar,
7278 : struct drsuapi_DsReplicaCursor2,
7279 : nuv.ctr.ctr2.count);
7280 3859 : if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7281 :
7282 : /* first copy the old vector */
7283 7026 : for (i=0; i < ouv.ctr.ctr2.count; i++) {
7284 3167 : nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
7285 3167 : ni++;
7286 : }
7287 :
7288 : /* merge in the source_dsa vector is available */
7289 8103 : for (i=0; (ruv && i < ruv->count); i++) {
7290 4244 : found = false;
7291 :
7292 4244 : if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7293 4244 : &ar->our_invocation_id)) {
7294 1304 : continue;
7295 : }
7296 :
7297 3507 : for (j=0; j < ni; j++) {
7298 2980 : if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7299 2980 : &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7300 567 : continue;
7301 : }
7302 :
7303 2413 : found = true;
7304 :
7305 2413 : if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7306 1873 : nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7307 : }
7308 2413 : break;
7309 : }
7310 :
7311 2940 : if (found) continue;
7312 :
7313 : /* if it's not there yet, add it */
7314 527 : nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7315 527 : ni++;
7316 : }
7317 :
7318 : /*
7319 : * finally correct the size of the cursors array
7320 : */
7321 3859 : nuv.ctr.ctr2.count = ni;
7322 :
7323 : /*
7324 : * sort the cursors
7325 : */
7326 3859 : TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7327 :
7328 : /*
7329 : * create the change ldb_message
7330 : */
7331 3859 : msg = ldb_msg_new(ar);
7332 3859 : if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7333 3859 : msg->dn = ar->search_msg->dn;
7334 :
7335 3859 : ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7336 : (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7337 3859 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7338 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7339 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7340 : }
7341 3859 : ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7342 3859 : if (ret != LDB_SUCCESS) {
7343 0 : return replmd_replicated_request_error(ar, ret);
7344 : }
7345 3859 : nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7346 :
7347 : /*
7348 : * now create the new repsFrom value from the given repsFromTo1 structure
7349 : */
7350 3859 : ZERO_STRUCT(nrf);
7351 3859 : nrf.version = 1;
7352 3859 : nrf.ctr.ctr1 = *ar->objs->source_dsa;
7353 3859 : nrf.ctr.ctr1.last_attempt = now;
7354 3859 : nrf.ctr.ctr1.last_success = now;
7355 3859 : nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7356 :
7357 : /*
7358 : * first see if we already have a repsFrom value for the current source dsa
7359 : * if so we'll later replace this value
7360 : */
7361 3859 : orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7362 3859 : if (orf_el) {
7363 3612 : for (i=0; i < orf_el->num_values; i++) {
7364 0 : struct repsFromToBlob *trf;
7365 :
7366 3605 : trf = talloc(ar, struct repsFromToBlob);
7367 3605 : if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7368 :
7369 3605 : ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7370 : (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7371 3605 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7372 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7373 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7374 : }
7375 :
7376 3605 : if (trf->version != 1) {
7377 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7378 : }
7379 :
7380 : /*
7381 : * we compare the source dsa objectGUID not the invocation_id
7382 : * because we want only one repsFrom value per source dsa
7383 : * and when the invocation_id of the source dsa has changed we don't need
7384 : * the old repsFrom with the old invocation_id
7385 : */
7386 3605 : if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7387 3605 : &ar->objs->source_dsa->source_dsa_obj_guid)) {
7388 129 : talloc_free(trf);
7389 129 : continue;
7390 : }
7391 :
7392 3476 : talloc_free(trf);
7393 3476 : nrf_value = &orf_el->values[i];
7394 3476 : break;
7395 : }
7396 :
7397 : /*
7398 : * copy over all old values to the new ldb_message
7399 : */
7400 3483 : ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7401 3483 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7402 3483 : *nrf_el = *orf_el;
7403 : }
7404 :
7405 : /*
7406 : * if we haven't found an old repsFrom value for the current source dsa
7407 : * we'll add a new value
7408 : */
7409 3859 : if (!nrf_value) {
7410 0 : struct ldb_val zero_value;
7411 383 : ZERO_STRUCT(zero_value);
7412 383 : ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7413 383 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7414 :
7415 383 : nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7416 : }
7417 :
7418 : /* we now fill the value which is already attached to ldb_message */
7419 3859 : ndr_err = ndr_push_struct_blob(nrf_value, msg,
7420 : &nrf,
7421 : (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7422 3859 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7423 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7424 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7425 : }
7426 :
7427 : /*
7428 : * the ldb_message_element for the attribute, has all the old values and the new one
7429 : * so we'll replace the whole attribute with all values
7430 : */
7431 3859 : nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7432 :
7433 3859 : if (CHECK_DEBUGLVL(4)) {
7434 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
7435 : LDB_CHANGETYPE_MODIFY,
7436 : msg);
7437 0 : DEBUG(4, ("DRS replication up-to-date modify message:\n%s\n", s));
7438 0 : talloc_free(s);
7439 : }
7440 :
7441 : /* prepare the ldb_modify() request */
7442 3859 : ret = ldb_build_mod_req(&change_req,
7443 : ldb,
7444 : ar,
7445 : msg,
7446 : ar->controls,
7447 : ar,
7448 : replmd_replicated_uptodate_modify_callback,
7449 : ar->req);
7450 3859 : LDB_REQ_SET_LOCATION(change_req);
7451 3859 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7452 :
7453 3859 : return ldb_next_request(ar->module, change_req);
7454 : }
7455 :
7456 7718 : static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7457 : struct ldb_reply *ares)
7458 : {
7459 7718 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
7460 : struct replmd_replicated_request);
7461 0 : int ret;
7462 :
7463 7718 : if (!ares) {
7464 0 : return ldb_module_done(ar->req, NULL, NULL,
7465 : LDB_ERR_OPERATIONS_ERROR);
7466 : }
7467 7718 : if (ares->error != LDB_SUCCESS &&
7468 0 : ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7469 0 : return ldb_module_done(ar->req, ares->controls,
7470 : ares->response, ares->error);
7471 : }
7472 :
7473 7718 : switch (ares->type) {
7474 3859 : case LDB_REPLY_ENTRY:
7475 3859 : ar->search_msg = talloc_steal(ar, ares->message);
7476 3859 : break;
7477 :
7478 0 : case LDB_REPLY_REFERRAL:
7479 : /* we ignore referrals */
7480 0 : break;
7481 :
7482 3859 : case LDB_REPLY_DONE:
7483 3859 : ret = replmd_replicated_uptodate_modify(ar);
7484 3859 : if (ret != LDB_SUCCESS) {
7485 0 : return ldb_module_done(ar->req, NULL, NULL, ret);
7486 : }
7487 : }
7488 :
7489 7718 : talloc_free(ares);
7490 7718 : return LDB_SUCCESS;
7491 : }
7492 :
7493 :
7494 3859 : static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7495 : {
7496 3859 : struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7497 0 : struct replmd_private *replmd_private =
7498 3859 : talloc_get_type_abort(ldb_module_get_private(ar->module),
7499 : struct replmd_private);
7500 0 : int ret;
7501 0 : static const char *attrs[] = {
7502 : "replUpToDateVector",
7503 : "repsFrom",
7504 : "instanceType",
7505 : NULL
7506 : };
7507 0 : struct ldb_request *search_req;
7508 :
7509 3859 : ar->search_msg = NULL;
7510 :
7511 : /*
7512 : * Let the caller know that we did an originating updates
7513 : */
7514 3859 : ar->objs->originating_updates = replmd_private->originating_updates;
7515 :
7516 3859 : ret = ldb_build_search_req(&search_req,
7517 : ldb,
7518 : ar,
7519 3859 : ar->objs->partition_dn,
7520 : LDB_SCOPE_BASE,
7521 : "(objectClass=*)",
7522 : attrs,
7523 : NULL,
7524 : ar,
7525 : replmd_replicated_uptodate_search_callback,
7526 : ar->req);
7527 3859 : LDB_REQ_SET_LOCATION(search_req);
7528 3859 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7529 :
7530 3859 : return ldb_next_request(ar->module, search_req);
7531 : }
7532 :
7533 :
7534 :
7535 3929 : static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7536 : {
7537 0 : struct ldb_context *ldb;
7538 0 : struct dsdb_extended_replicated_objects *objs;
7539 0 : struct replmd_replicated_request *ar;
7540 0 : struct ldb_control **ctrls;
7541 0 : int ret;
7542 :
7543 3929 : ldb = ldb_module_get_ctx(module);
7544 :
7545 3929 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7546 :
7547 3929 : objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7548 3929 : if (!objs) {
7549 0 : ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7550 0 : return LDB_ERR_PROTOCOL_ERROR;
7551 : }
7552 :
7553 3929 : if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7554 0 : ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7555 : objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7556 0 : return LDB_ERR_PROTOCOL_ERROR;
7557 : }
7558 :
7559 3929 : ar = replmd_ctx_init(module, req);
7560 3929 : if (!ar)
7561 0 : return LDB_ERR_OPERATIONS_ERROR;
7562 :
7563 : /* Set the flags to have the replmd_op_callback run over the full set of objects */
7564 3929 : ar->apply_mode = true;
7565 3929 : ar->objs = objs;
7566 3929 : ar->schema = dsdb_get_schema(ldb, ar);
7567 3929 : if (!ar->schema) {
7568 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7569 0 : talloc_free(ar);
7570 0 : DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7571 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
7572 : }
7573 :
7574 3929 : ctrls = req->controls;
7575 :
7576 3929 : if (req->controls) {
7577 3929 : req->controls = talloc_memdup(ar, req->controls,
7578 : talloc_get_size(req->controls));
7579 3929 : if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7580 : }
7581 :
7582 3929 : ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7583 3929 : if (ret != LDB_SUCCESS) {
7584 0 : return ret;
7585 : }
7586 :
7587 : /* If this change contained linked attributes in the body
7588 : * (rather than in the links section) we need to update
7589 : * backlinks in linked_attributes */
7590 3929 : ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7591 3929 : if (ret != LDB_SUCCESS) {
7592 0 : return ret;
7593 : }
7594 :
7595 3929 : ar->controls = req->controls;
7596 3929 : req->controls = ctrls;
7597 :
7598 3929 : return replmd_replicated_apply_next(ar);
7599 : }
7600 :
7601 : /**
7602 : * Checks how to handle an missing target - either we need to fail the
7603 : * replication and retry with GET_TGT, ignore the link and continue, or try to
7604 : * add a partial link to an unknown target.
7605 : */
7606 950 : static int replmd_allow_missing_target(struct ldb_module *module,
7607 : TALLOC_CTX *mem_ctx,
7608 : struct ldb_dn *target_dn,
7609 : struct ldb_dn *source_dn,
7610 : bool is_obj_commit,
7611 : struct GUID *guid,
7612 : uint32_t dsdb_repl_flags,
7613 : bool *ignore_link,
7614 : const char * missing_str)
7615 : {
7616 950 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7617 0 : bool is_in_same_nc;
7618 :
7619 : /*
7620 : * we may not be able to resolve link targets properly when
7621 : * dealing with subsets of objects, e.g. the source is a
7622 : * critical object and the target isn't
7623 : *
7624 : * TODO:
7625 : * When we implement Trusted Domains we need to consider
7626 : * whether they get treated as an incomplete replica here or not
7627 : */
7628 950 : if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7629 :
7630 : /*
7631 : * Ignore the link. We don't increase the highwater-mark in
7632 : * the object subset cases, so subsequent replications should
7633 : * resolve any missing links
7634 : */
7635 46 : DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7636 : ldb_dn_get_linearized(target_dn),
7637 : ldb_dn_get_linearized(source_dn)));
7638 46 : *ignore_link = true;
7639 46 : return LDB_SUCCESS;
7640 : }
7641 :
7642 904 : is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7643 : mem_ctx,
7644 : source_dn,
7645 : target_dn);
7646 904 : if (is_in_same_nc) {
7647 : /*
7648 : * We allow the join.py code to point out that all
7649 : * replication is completed, so failing now would just
7650 : * trigger errors, rather than trigger a GET_TGT
7651 : */
7652 0 : unsigned long long *finished_full_join_ptr =
7653 204 : talloc_get_type(ldb_get_opaque(ldb,
7654 : DSDB_FULL_JOIN_REPLICATION_COMPLETED_OPAQUE_NAME),
7655 : unsigned long long);
7656 204 : bool finished_full_join = finished_full_join_ptr && *finished_full_join_ptr;
7657 :
7658 : /*
7659 : * if the target is already be up-to-date there's no point in
7660 : * retrying. This could be due to bad timing, or if a target
7661 : * on a one-way link was deleted. We ignore the link rather
7662 : * than failing the replication cycle completely
7663 : */
7664 204 : if (finished_full_join
7665 71 : || dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7666 165 : *ignore_link = true;
7667 165 : DBG_WARNING("%s is %s "
7668 : "but up to date. Ignoring link from %s\n",
7669 : ldb_dn_get_linearized(target_dn), missing_str,
7670 : ldb_dn_get_linearized(source_dn));
7671 165 : return LDB_SUCCESS;
7672 : }
7673 :
7674 : /* otherwise fail the replication and retry with GET_TGT */
7675 39 : ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7676 : missing_str,
7677 : ldb_dn_get_linearized(target_dn),
7678 : GUID_string(mem_ctx, guid),
7679 : ldb_dn_get_linearized(source_dn));
7680 39 : return LDB_ERR_NO_SUCH_OBJECT;
7681 : }
7682 :
7683 : /*
7684 : * The target of the cross-partition link is missing. Continue
7685 : * and try to at least add the forward-link. This isn't great,
7686 : * but a partial link can be fixed by dbcheck, so it's better
7687 : * than dropping the link completely.
7688 : */
7689 700 : *ignore_link = false;
7690 :
7691 700 : if (is_obj_commit) {
7692 :
7693 : /*
7694 : * Only log this when we're actually committing the objects.
7695 : * This avoids spurious logs, i.e. if we're just verifying the
7696 : * received link during a join.
7697 : */
7698 53 : DBG_WARNING("%s cross-partition target %s linked from %s\n",
7699 : missing_str, ldb_dn_get_linearized(target_dn),
7700 : ldb_dn_get_linearized(source_dn));
7701 : }
7702 :
7703 700 : return LDB_SUCCESS;
7704 : }
7705 :
7706 : /**
7707 : * Checks that the target object for a linked attribute exists.
7708 : * @param guid returns the target object's GUID (is returned)if it exists)
7709 : * @param ignore_link set to true if the linked attribute should be ignored
7710 : * (i.e. the target doesn't exist, but that it's OK to skip the link)
7711 : */
7712 21908 : static int replmd_check_target_exists(struct ldb_module *module,
7713 : struct dsdb_dn *dsdb_dn,
7714 : struct la_entry *la_entry,
7715 : struct ldb_dn *source_dn,
7716 : bool is_obj_commit,
7717 : struct GUID *guid,
7718 : bool *ignore_link)
7719 : {
7720 21908 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7721 21908 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7722 0 : struct ldb_result *target_res;
7723 21908 : TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7724 21908 : const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7725 0 : NTSTATUS ntstatus;
7726 0 : int ret;
7727 21908 : enum deletion_state target_deletion_state = OBJECT_REMOVED;
7728 21908 : bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7729 :
7730 21908 : *ignore_link = false;
7731 21908 : ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7732 :
7733 21908 : if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7734 :
7735 : /*
7736 : * This strange behaviour (allowing a NULL/missing
7737 : * GUID) originally comes from:
7738 : *
7739 : * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7740 : * Author: Andrew Tridgell <tridge@samba.org>
7741 : * Date: Mon Dec 21 21:21:55 2009 +1100
7742 : *
7743 : * s4-drs: cope better with NULL GUIDS from DRS
7744 : *
7745 : * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7746 : * need to match by DN if possible when seeing if we should update an
7747 : * existing link.
7748 : *
7749 : * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7750 : */
7751 0 : ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7752 : dsdb_dn->dn, attrs,
7753 : DSDB_FLAG_NEXT_MODULE |
7754 : DSDB_SEARCH_SHOW_RECYCLED |
7755 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7756 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7757 : NULL);
7758 21908 : } else if (!NT_STATUS_IS_OK(ntstatus)) {
7759 0 : ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7760 0 : la->attid,
7761 : ldb_dn_get_linearized(dsdb_dn->dn),
7762 : ldb_dn_get_linearized(source_dn));
7763 0 : talloc_free(tmp_ctx);
7764 0 : return LDB_ERR_OPERATIONS_ERROR;
7765 : } else {
7766 21908 : ret = dsdb_module_search(module, tmp_ctx, &target_res,
7767 : NULL, LDB_SCOPE_SUBTREE,
7768 : attrs,
7769 : DSDB_FLAG_NEXT_MODULE |
7770 : DSDB_SEARCH_SHOW_RECYCLED |
7771 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7772 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7773 : NULL,
7774 : "objectGUID=%s",
7775 : GUID_string(tmp_ctx, guid));
7776 : }
7777 :
7778 21908 : if (ret != LDB_SUCCESS) {
7779 0 : ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7780 : GUID_string(tmp_ctx, guid),
7781 : ldb_errstring(ldb));
7782 0 : talloc_free(tmp_ctx);
7783 0 : return ret;
7784 : }
7785 :
7786 21908 : if (target_res->count == 0) {
7787 :
7788 : /*
7789 : * target object is unknown. Check whether to ignore the link,
7790 : * fail the replication, or add a partial link
7791 : */
7792 739 : ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7793 : source_dn, is_obj_commit, guid,
7794 : la_entry->dsdb_repl_flags,
7795 : ignore_link, "Unknown");
7796 :
7797 21169 : } else if (target_res->count != 1) {
7798 0 : ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7799 : GUID_string(tmp_ctx, guid));
7800 0 : ret = LDB_ERR_OPERATIONS_ERROR;
7801 : } else {
7802 21169 : struct ldb_message *target_msg = target_res->msgs[0];
7803 :
7804 21169 : dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7805 :
7806 : /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7807 21169 : replmd_deletion_state(module, target_msg,
7808 : &target_deletion_state, NULL);
7809 :
7810 : /*
7811 : * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7812 : * ProcessLinkValue(). Link updates should not be sent for
7813 : * recycled and tombstone objects (deleting the links should
7814 : * happen when we delete the object). This probably means our
7815 : * copy of the target object isn't up to date.
7816 : */
7817 21169 : if (target_deletion_state >= OBJECT_RECYCLED) {
7818 :
7819 : /*
7820 : * target object is deleted. Check whether to ignore the
7821 : * link, fail the replication, or add a partial link
7822 : */
7823 211 : ret = replmd_allow_missing_target(module, tmp_ctx,
7824 : dsdb_dn->dn, source_dn,
7825 : is_obj_commit, guid,
7826 : la_entry->dsdb_repl_flags,
7827 : ignore_link, "Deleted");
7828 : }
7829 : }
7830 :
7831 21908 : talloc_free(tmp_ctx);
7832 21908 : return ret;
7833 : }
7834 :
7835 : /**
7836 : * Extracts the key details about the source object for a
7837 : * linked-attribute entry.
7838 : * This returns the following details:
7839 : * @param ret_attr the schema details for the linked attribute
7840 : * @param source_msg the search result for the source object
7841 : */
7842 10673 : static int replmd_get_la_entry_source(struct ldb_module *module,
7843 : struct la_entry *la_entry,
7844 : TALLOC_CTX *mem_ctx,
7845 : const struct dsdb_attribute **ret_attr,
7846 : struct ldb_message **source_msg)
7847 : {
7848 10673 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7849 10673 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7850 10673 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7851 0 : int ret;
7852 0 : const struct dsdb_attribute *attr;
7853 0 : struct ldb_result *res;
7854 0 : const char *attrs[4];
7855 :
7856 : /*
7857 : linked_attributes[0]:
7858 : &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7859 : identifier : *
7860 : identifier: struct drsuapi_DsReplicaObjectIdentifier
7861 : __ndr_size : 0x0000003a (58)
7862 : __ndr_size_sid : 0x00000000 (0)
7863 : guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7864 : sid : S-0-0
7865 : __ndr_size_dn : 0x00000000 (0)
7866 : dn : ''
7867 : attid : DRSUAPI_ATTID_member (0x1F)
7868 : value: struct drsuapi_DsAttributeValue
7869 : __ndr_size : 0x0000007e (126)
7870 : blob : *
7871 : blob : DATA_BLOB length=126
7872 : flags : 0x00000001 (1)
7873 : 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7874 : originating_add_time : Wed Sep 2 22:20:01 2009 EST
7875 : meta_data: struct drsuapi_DsReplicaMetaData
7876 : version : 0x00000015 (21)
7877 : originating_change_time : Wed Sep 2 23:39:07 2009 EST
7878 : originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7879 : originating_usn : 0x000000000001e19c (123292)
7880 :
7881 : (for cases where the link is to a normal DN)
7882 : &target: struct drsuapi_DsReplicaObjectIdentifier3
7883 : __ndr_size : 0x0000007e (126)
7884 : __ndr_size_sid : 0x0000001c (28)
7885 : guid : 7639e594-db75-4086-b0d4-67890ae46031
7886 : sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7887 : __ndr_size_dn : 0x00000022 (34)
7888 : dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7889 : */
7890 :
7891 : /* find the attribute being modified */
7892 10673 : attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7893 10673 : if (attr == NULL) {
7894 0 : struct GUID_txt_buf guid_str;
7895 0 : ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7896 0 : la->attid,
7897 0 : GUID_buf_string(&la->identifier->guid,
7898 : &guid_str));
7899 0 : return LDB_ERR_OPERATIONS_ERROR;
7900 : }
7901 :
7902 : /*
7903 : * All attributes listed here must be dealt with in some way
7904 : * by replmd_process_linked_attribute() otherwise in the case
7905 : * of isDeleted: FALSE the modify will fail with:
7906 : *
7907 : * Failed to apply linked attribute change 'attribute 'isDeleted':
7908 : * invalid modify flags on
7909 : * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7910 : * 0x0'
7911 : *
7912 : * This is because isDeleted is a Boolean, so FALSE is a
7913 : * legitimate value (set by Samba's deletetest.py)
7914 : */
7915 10673 : attrs[0] = attr->lDAPDisplayName;
7916 10673 : attrs[1] = "isDeleted";
7917 10673 : attrs[2] = "isRecycled";
7918 10673 : attrs[3] = NULL;
7919 :
7920 : /*
7921 : * get the existing message from the db for the object with
7922 : * this GUID, returning attribute being modified. We will then
7923 : * use this msg as the basis for a modify call
7924 : */
7925 10673 : ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7926 : DSDB_FLAG_NEXT_MODULE |
7927 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7928 : DSDB_SEARCH_SHOW_RECYCLED |
7929 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7930 : DSDB_SEARCH_REVEAL_INTERNALS,
7931 : NULL,
7932 10673 : "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7933 10673 : if (ret != LDB_SUCCESS) {
7934 0 : return ret;
7935 : }
7936 10673 : if (res->count != 1) {
7937 0 : ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7938 0 : GUID_string(mem_ctx, &la->identifier->guid));
7939 0 : return LDB_ERR_NO_SUCH_OBJECT;
7940 : }
7941 :
7942 10673 : *source_msg = res->msgs[0];
7943 10673 : *ret_attr = attr;
7944 :
7945 10673 : return LDB_SUCCESS;
7946 : }
7947 :
7948 : /**
7949 : * Verifies the target object is known for a linked attribute
7950 : */
7951 13650 : static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7952 : TALLOC_CTX *mem_ctx,
7953 : struct la_entry *la_entry,
7954 : struct ldb_dn *src_dn,
7955 : const struct dsdb_attribute *attr)
7956 : {
7957 13650 : int ret = LDB_SUCCESS;
7958 13650 : struct ldb_module *module = ar->module;
7959 13650 : struct dsdb_dn *tgt_dsdb_dn = NULL;
7960 13650 : struct GUID guid = GUID_zero();
7961 0 : bool dummy;
7962 0 : WERROR status;
7963 13650 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7964 13650 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7965 13650 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7966 :
7967 : /* the value blob for the attribute holds the target object DN */
7968 13650 : status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7969 : la->value.blob, &tgt_dsdb_dn);
7970 13650 : if (!W_ERROR_IS_OK(status)) {
7971 0 : ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7972 0 : attr->lDAPDisplayName,
7973 : ldb_dn_get_linearized(src_dn),
7974 : win_errstr(status));
7975 0 : return LDB_ERR_OPERATIONS_ERROR;
7976 : }
7977 :
7978 : /*
7979 : * We can skip the target object checks if we're only syncing critical
7980 : * objects, or we know the target is up-to-date. If either case, we
7981 : * still continue even if the target doesn't exist
7982 : */
7983 13650 : if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7984 : DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7985 :
7986 8423 : ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7987 : src_dn, false, &guid, &dummy);
7988 : }
7989 :
7990 : /*
7991 : * When we fail to find the target object, the error code we pass
7992 : * back here is really important. It flags back to the callers to
7993 : * retry this request with DRSUAPI_DRS_GET_TGT
7994 : */
7995 13650 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7996 38 : ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7997 : }
7998 :
7999 13650 : return ret;
8000 : }
8001 :
8002 : /**
8003 : * Finds the current active Parsed-DN value for a single-valued linked
8004 : * attribute, if one exists.
8005 : * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
8006 : * @returns LDB_SUCCESS (regardless of whether a match was found), unless
8007 : * an error occurred
8008 : */
8009 8834 : static int replmd_get_active_singleval_link(struct ldb_module *module,
8010 : TALLOC_CTX *mem_ctx,
8011 : struct parsed_dn pdn_list[],
8012 : unsigned int count,
8013 : const struct dsdb_attribute *attr,
8014 : struct parsed_dn **ret_pdn)
8015 : {
8016 0 : unsigned int i;
8017 :
8018 8834 : *ret_pdn = NULL;
8019 :
8020 8834 : if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
8021 :
8022 : /* nothing to do for multi-valued linked attributes */
8023 7368 : return LDB_SUCCESS;
8024 : }
8025 :
8026 1487 : for (i = 0; i < count; i++) {
8027 33 : int ret = LDB_SUCCESS;
8028 33 : struct parsed_dn *pdn = &pdn_list[i];
8029 :
8030 : /* skip any inactive links */
8031 33 : if (dsdb_dn_is_deleted_val(pdn->v)) {
8032 21 : continue;
8033 : }
8034 :
8035 : /* we've found an active value for this attribute */
8036 12 : *ret_pdn = pdn;
8037 :
8038 12 : if (pdn->dsdb_dn == NULL) {
8039 0 : struct ldb_context *ldb = ldb_module_get_ctx(module);
8040 :
8041 0 : ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
8042 0 : attr->syntax->ldap_oid);
8043 : }
8044 :
8045 12 : return ret;
8046 : }
8047 :
8048 : /* no active link found */
8049 1454 : return LDB_SUCCESS;
8050 : }
8051 :
8052 : /**
8053 : * @returns true if the replication linked attribute info is newer than we
8054 : * already have in our DB
8055 : * @param pdn the existing linked attribute info in our DB
8056 : * @param la the new linked attribute info received during replication
8057 : */
8058 13281 : static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
8059 : struct drsuapi_DsReplicaLinkedAttribute *la)
8060 : {
8061 : /* see if this update is newer than what we have already */
8062 13281 : struct GUID invocation_id = GUID_zero();
8063 13281 : uint32_t version = 0;
8064 13281 : NTTIME change_time = 0;
8065 :
8066 13281 : if (pdn == NULL) {
8067 :
8068 : /* no existing info so update is newer */
8069 8868 : return true;
8070 : }
8071 :
8072 4413 : dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
8073 4413 : dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
8074 4413 : dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
8075 :
8076 4413 : return replmd_update_is_newer(&invocation_id,
8077 4413 : &la->meta_data.originating_invocation_id,
8078 : version,
8079 : la->meta_data.version,
8080 : change_time,
8081 : la->meta_data.originating_change_time);
8082 : }
8083 :
8084 : /**
8085 : * Marks an existing linked attribute value as deleted in the DB
8086 : * @param pdn the parsed-DN of the target-value to delete
8087 : */
8088 8 : static int replmd_delete_link_value(struct ldb_module *module,
8089 : struct replmd_private *replmd_private,
8090 : TALLOC_CTX *mem_ctx,
8091 : struct ldb_dn *src_obj_dn,
8092 : const struct dsdb_schema *schema,
8093 : const struct dsdb_attribute *attr,
8094 : uint64_t seq_num,
8095 : bool is_active,
8096 : struct GUID *target_guid,
8097 : struct dsdb_dn *target_dsdb_dn,
8098 : struct ldb_val *output_val)
8099 : {
8100 8 : struct ldb_context *ldb = ldb_module_get_ctx(module);
8101 0 : time_t t;
8102 0 : NTTIME now;
8103 8 : const struct GUID *invocation_id = NULL;
8104 0 : int ret;
8105 :
8106 8 : t = time(NULL);
8107 8 : unix_to_nt_time(&now, t);
8108 :
8109 8 : invocation_id = samdb_ntds_invocation_id(ldb);
8110 8 : if (invocation_id == NULL) {
8111 0 : return LDB_ERR_OPERATIONS_ERROR;
8112 : }
8113 :
8114 : /* if the existing link is active, remove its backlink */
8115 8 : if (is_active) {
8116 :
8117 : /*
8118 : * NOTE WELL: After this we will never (at runtime) be
8119 : * able to find this forward link (for instant
8120 : * removal) if/when the link target is deleted.
8121 : *
8122 : * We have dbcheck rules to cover this and cope otherwise
8123 : * by filtering at runtime (i.e. in the extended_dn module).
8124 : */
8125 4 : ret = replmd_add_backlink(module, replmd_private, schema,
8126 : src_obj_dn, target_guid, false,
8127 : attr, NULL);
8128 4 : if (ret != LDB_SUCCESS) {
8129 0 : return ret;
8130 : }
8131 : }
8132 :
8133 : /* mark the existing value as deleted */
8134 8 : ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
8135 : target_dsdb_dn, invocation_id, seq_num,
8136 : seq_num, now, true);
8137 8 : return ret;
8138 : }
8139 :
8140 : /**
8141 : * Checks for a conflict in single-valued link attributes, and tries to
8142 : * resolve the problem if possible.
8143 : *
8144 : * Single-valued links should only ever have one active value. If we already
8145 : * have an active link value, and during replication we receive an active link
8146 : * value for a different target DN, then we need to resolve this inconsistency
8147 : * and determine which value should be active. If the received info is better/
8148 : * newer than the existing link attribute, then we need to set our existing
8149 : * link as deleted. If the received info is worse/older, then we should continue
8150 : * to add it, but set it as an inactive link.
8151 : *
8152 : * Note that this is a corner-case that is unlikely to happen (but if it does
8153 : * happen, we don't want it to break replication completely).
8154 : *
8155 : * @param pdn_being_modified the parsed DN corresponding to the received link
8156 : * target (note this is NULL if the link does not already exist in our DB)
8157 : * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
8158 : * any existing active or inactive values for the attribute in our DB.
8159 : * @param dsdb_dn the target DN for the received link attribute
8160 : * @param add_as_inactive gets set to true if the received link is worse than
8161 : * the existing link - it should still be added, but as an inactive link.
8162 : */
8163 8834 : static int replmd_check_singleval_la_conflict(struct ldb_module *module,
8164 : struct replmd_private *replmd_private,
8165 : TALLOC_CTX *mem_ctx,
8166 : struct ldb_dn *src_obj_dn,
8167 : struct drsuapi_DsReplicaLinkedAttribute *la,
8168 : struct dsdb_dn *dsdb_dn,
8169 : struct parsed_dn *pdn_being_modified,
8170 : struct parsed_dn *pdn_list,
8171 : struct ldb_message_element *old_el,
8172 : const struct dsdb_schema *schema,
8173 : const struct dsdb_attribute *attr,
8174 : uint64_t seq_num,
8175 : bool *add_as_inactive)
8176 : {
8177 8834 : struct parsed_dn *active_pdn = NULL;
8178 8834 : bool update_is_newer = false;
8179 0 : int ret;
8180 :
8181 : /*
8182 : * check if there's a conflict for single-valued links, i.e. an active
8183 : * linked attribute already exists, but it has a different target value
8184 : */
8185 8834 : ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
8186 : old_el->num_values, attr,
8187 : &active_pdn);
8188 :
8189 8834 : if (ret != LDB_SUCCESS) {
8190 0 : return ret;
8191 : }
8192 :
8193 : /*
8194 : * If no active value exists (or the received info is for the currently
8195 : * active value), then no conflict exists
8196 : */
8197 8834 : if (active_pdn == NULL || active_pdn == pdn_being_modified) {
8198 8826 : return LDB_SUCCESS;
8199 : }
8200 :
8201 8 : DBG_WARNING("Link conflict for %s attribute on %s\n",
8202 : attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
8203 :
8204 : /* Work out how to resolve the conflict based on which info is better */
8205 8 : update_is_newer = replmd_link_update_is_newer(active_pdn, la);
8206 :
8207 8 : if (update_is_newer) {
8208 4 : DBG_WARNING("Using received value %s, over existing target %s\n",
8209 : ldb_dn_get_linearized(dsdb_dn->dn),
8210 : ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
8211 :
8212 : /*
8213 : * Delete our existing active link. The received info will then
8214 : * be added (through normal link processing) as the active value
8215 : */
8216 4 : ret = replmd_delete_link_value(module, replmd_private, old_el,
8217 : src_obj_dn, schema, attr,
8218 4 : seq_num, true, &active_pdn->guid,
8219 4 : active_pdn->dsdb_dn,
8220 4 : active_pdn->v);
8221 :
8222 4 : if (ret != LDB_SUCCESS) {
8223 0 : return ret;
8224 : }
8225 : } else {
8226 4 : DBG_WARNING("Using existing target %s, over received value %s\n",
8227 : ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
8228 : ldb_dn_get_linearized(dsdb_dn->dn));
8229 :
8230 : /*
8231 : * we want to keep our existing active link and add the
8232 : * received link as inactive
8233 : */
8234 4 : *add_as_inactive = true;
8235 : }
8236 :
8237 8 : return LDB_SUCCESS;
8238 : }
8239 :
8240 : /**
8241 : * Processes one linked attribute received via replication.
8242 : * @param src_dn the DN of the source object for the link
8243 : * @param attr schema info for the linked attribute
8244 : * @param la_entry the linked attribute info received via DRS
8245 : * @param element_ctx mem context for msg->element[] (when adding a new value
8246 : * we need to realloc old_el->values)
8247 : * @param old_el the corresponding msg->element[] for the linked attribute
8248 : * @param pdn_list a (binary-searchable) parsed DN array for the existing link
8249 : * values in the msg. E.g. for a group, this is the existing members.
8250 : * @param change what got modified: either nothing, an existing link value was
8251 : * modified, or a new link value was added.
8252 : * @returns LDB_SUCCESS if OK, an error otherwise
8253 : */
8254 13485 : static int replmd_process_linked_attribute(struct ldb_module *module,
8255 : TALLOC_CTX *mem_ctx,
8256 : struct replmd_private *replmd_private,
8257 : struct ldb_dn *src_dn,
8258 : const struct dsdb_attribute *attr,
8259 : struct la_entry *la_entry,
8260 : struct ldb_request *parent,
8261 : TALLOC_CTX *element_ctx,
8262 : struct ldb_message_element *old_el,
8263 : struct parsed_dn *pdn_list,
8264 : replmd_link_changed *change)
8265 : {
8266 13485 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
8267 13485 : struct ldb_context *ldb = ldb_module_get_ctx(module);
8268 13485 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
8269 0 : int ret;
8270 13485 : struct dsdb_dn *dsdb_dn = NULL;
8271 13485 : uint64_t seq_num = 0;
8272 0 : struct parsed_dn *pdn, *next;
8273 13485 : struct GUID guid = GUID_zero();
8274 13485 : bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
8275 0 : bool ignore_link;
8276 13485 : struct dsdb_dn *old_dsdb_dn = NULL;
8277 13485 : struct ldb_val *val_to_update = NULL;
8278 13485 : bool add_as_inactive = false;
8279 0 : WERROR status;
8280 :
8281 13485 : *change = LINK_CHANGE_NONE;
8282 :
8283 : /* the value blob for the attribute holds the target object DN */
8284 13485 : status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
8285 : la->value.blob, &dsdb_dn);
8286 13485 : if (!W_ERROR_IS_OK(status)) {
8287 0 : ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8288 0 : attr->lDAPDisplayName,
8289 : ldb_dn_get_linearized(src_dn),
8290 : win_errstr(status));
8291 0 : return LDB_ERR_OPERATIONS_ERROR;
8292 : }
8293 :
8294 13485 : ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
8295 : true, &guid, &ignore_link);
8296 :
8297 13485 : if (ret != LDB_SUCCESS) {
8298 1 : return ret;
8299 : }
8300 :
8301 : /*
8302 : * there are some cases where the target object doesn't exist, but it's
8303 : * OK to ignore the linked attribute
8304 : */
8305 13484 : if (ignore_link) {
8306 211 : return ret;
8307 : }
8308 :
8309 : /* see if this link already exists */
8310 13273 : ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8311 : &guid,
8312 13273 : dsdb_dn->dn,
8313 13273 : dsdb_dn->extra_part, 0,
8314 : &pdn, &next,
8315 13273 : attr->syntax->ldap_oid,
8316 : true);
8317 13273 : if (ret != LDB_SUCCESS) {
8318 0 : return ret;
8319 : }
8320 :
8321 13273 : if (!replmd_link_update_is_newer(pdn, la)) {
8322 4359 : DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8323 : old_el->name, ldb_dn_get_linearized(src_dn),
8324 : GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8325 4359 : return LDB_SUCCESS;
8326 : }
8327 :
8328 : /* get a seq_num for this change */
8329 8914 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8330 8914 : if (ret != LDB_SUCCESS) {
8331 0 : return ret;
8332 : }
8333 :
8334 : /*
8335 : * check for single-valued link conflicts, i.e. an active linked
8336 : * attribute already exists, but it has a different target value
8337 : */
8338 8914 : if (active) {
8339 8834 : ret = replmd_check_singleval_la_conflict(module, replmd_private,
8340 : mem_ctx, src_dn, la,
8341 : dsdb_dn, pdn, pdn_list,
8342 : old_el, schema, attr,
8343 : seq_num,
8344 : &add_as_inactive);
8345 8834 : if (ret != LDB_SUCCESS) {
8346 0 : return ret;
8347 : }
8348 : }
8349 :
8350 8914 : if (pdn != NULL) {
8351 46 : uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8352 :
8353 46 : if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8354 : /* remove the existing backlink */
8355 26 : ret = replmd_add_backlink(module, replmd_private,
8356 : schema,
8357 : src_dn,
8358 26 : &pdn->guid, false, attr,
8359 : parent);
8360 26 : if (ret != LDB_SUCCESS) {
8361 0 : return ret;
8362 : }
8363 : }
8364 :
8365 46 : val_to_update = pdn->v;
8366 46 : old_dsdb_dn = pdn->dsdb_dn;
8367 46 : *change = LINK_CHANGE_MODIFIED;
8368 :
8369 : } else {
8370 0 : unsigned offset;
8371 :
8372 : /*
8373 : * We know where the new one needs to be, from the *next
8374 : * pointer into pdn_list.
8375 : */
8376 8868 : if (next == NULL) {
8377 7946 : offset = old_el->num_values;
8378 : } else {
8379 922 : if (next->dsdb_dn == NULL) {
8380 0 : ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8381 0 : attr->syntax->ldap_oid);
8382 0 : if (ret != LDB_SUCCESS) {
8383 0 : return ret;
8384 : }
8385 : }
8386 922 : offset = next - pdn_list;
8387 922 : if (offset > old_el->num_values) {
8388 0 : return LDB_ERR_OPERATIONS_ERROR;
8389 : }
8390 : }
8391 :
8392 8868 : old_el->values = talloc_realloc(element_ctx, old_el->values,
8393 : struct ldb_val, old_el->num_values+1);
8394 8868 : if (!old_el->values) {
8395 0 : ldb_module_oom(module);
8396 0 : return LDB_ERR_OPERATIONS_ERROR;
8397 : }
8398 :
8399 8868 : if (offset != old_el->num_values) {
8400 922 : memmove(&old_el->values[offset + 1], &old_el->values[offset],
8401 922 : (old_el->num_values - offset) * sizeof(old_el->values[0]));
8402 : }
8403 :
8404 8868 : old_el->num_values++;
8405 :
8406 8868 : val_to_update = &old_el->values[offset];
8407 8868 : old_dsdb_dn = NULL;
8408 8868 : *change = LINK_CHANGE_ADDED;
8409 : }
8410 :
8411 : /* set the link attribute's value to the info that was received */
8412 8914 : ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8413 8914 : &la->meta_data.originating_invocation_id,
8414 : la->meta_data.originating_usn, seq_num,
8415 : la->meta_data.originating_change_time,
8416 : la->meta_data.version,
8417 8914 : !active);
8418 8914 : if (ret != LDB_SUCCESS) {
8419 0 : return ret;
8420 : }
8421 :
8422 8914 : if (add_as_inactive) {
8423 :
8424 : /* Set the new link as inactive/deleted to avoid conflicts */
8425 4 : ret = replmd_delete_link_value(module, replmd_private, old_el,
8426 : src_dn, schema, attr, seq_num,
8427 : false, &guid, dsdb_dn,
8428 : val_to_update);
8429 :
8430 4 : if (ret != LDB_SUCCESS) {
8431 0 : return ret;
8432 : }
8433 :
8434 8910 : } else if (active) {
8435 :
8436 : /* if the new link is active, then add the new backlink */
8437 8830 : ret = replmd_add_backlink(module, replmd_private,
8438 : schema,
8439 : src_dn,
8440 : &guid, true, attr,
8441 : parent);
8442 8830 : if (ret != LDB_SUCCESS) {
8443 0 : return ret;
8444 : }
8445 : }
8446 :
8447 8914 : ret = dsdb_check_single_valued_link(attr, old_el);
8448 8914 : if (ret != LDB_SUCCESS) {
8449 0 : return ret;
8450 : }
8451 :
8452 8914 : old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8453 :
8454 8914 : return ret;
8455 : }
8456 :
8457 1284879 : static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8458 : {
8459 1284879 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8460 3929 : return replmd_extended_replicated_objects(module, req);
8461 : }
8462 :
8463 1280950 : return ldb_next_request(module, req);
8464 : }
8465 :
8466 :
8467 : /*
8468 : we hook into the transaction operations to allow us to
8469 : perform the linked attribute updates at the end of the whole
8470 : transaction. This allows a forward linked attribute to be created
8471 : before the object is created. During a vampire, w2k8 sends us linked
8472 : attributes before the objects they are part of.
8473 : */
8474 350386 : static int replmd_start_transaction(struct ldb_module *module)
8475 : {
8476 : /* create our private structure for this transaction */
8477 350386 : struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8478 : struct replmd_private);
8479 350386 : replmd_txn_cleanup(replmd_private);
8480 :
8481 : /* free any leftover mod_usn records from cancelled
8482 : transactions */
8483 350600 : while (replmd_private->ncs) {
8484 214 : struct nc_entry *e = replmd_private->ncs;
8485 214 : DLIST_REMOVE(replmd_private->ncs, e);
8486 214 : talloc_free(e);
8487 : }
8488 :
8489 350386 : replmd_private->originating_updates = false;
8490 :
8491 350386 : return ldb_next_start_trans(module);
8492 : }
8493 :
8494 : /**
8495 : * Processes a group of linked attributes that apply to the same source-object
8496 : * and attribute-ID (and were received in the same replication chunk).
8497 : */
8498 5305 : static int replmd_process_la_group(struct ldb_module *module,
8499 : struct replmd_private *replmd_private,
8500 : struct la_group *la_group)
8501 : {
8502 5305 : struct la_entry *la = NULL;
8503 5305 : struct la_entry *prev = NULL;
8504 0 : int ret;
8505 5305 : TALLOC_CTX *tmp_ctx = NULL;
8506 5305 : struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8507 5305 : struct ldb_message *msg = NULL;
8508 5305 : enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8509 5305 : struct ldb_context *ldb = ldb_module_get_ctx(module);
8510 5305 : const struct dsdb_attribute *attr = NULL;
8511 5305 : struct ldb_message_element *old_el = NULL;
8512 5305 : struct parsed_dn *pdn_list = NULL;
8513 0 : replmd_link_changed change_type;
8514 5305 : uint32_t num_changes = 0;
8515 0 : time_t t;
8516 5305 : uint64_t seq_num = 0;
8517 :
8518 5305 : tmp_ctx = talloc_new(la_group);
8519 5305 : if (tmp_ctx == NULL) {
8520 0 : return ldb_oom(ldb);
8521 : }
8522 :
8523 : /*
8524 : * get the attribute being modified and the search result for the
8525 : * source object
8526 : */
8527 5305 : ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8528 : &msg);
8529 :
8530 5305 : if (ret != LDB_SUCCESS) {
8531 0 : return ret;
8532 : }
8533 :
8534 : /*
8535 : * Check for deleted objects per MS-DRSR 4.1.10.6.14
8536 : * ProcessLinkValue, because link updates are not applied to
8537 : * recycled and tombstone objects. We don't have to delete
8538 : * any existing link, that should have happened when the
8539 : * object deletion was replicated or initiated.
8540 : *
8541 : * This needs isDeleted and isRecycled to be included as
8542 : * attributes in the search and so in msg if set.
8543 : */
8544 5305 : replmd_deletion_state(module, msg, &deletion_state, NULL);
8545 :
8546 5305 : if (deletion_state >= OBJECT_RECYCLED) {
8547 2 : TALLOC_FREE(tmp_ctx);
8548 2 : return LDB_SUCCESS;
8549 : }
8550 :
8551 : /*
8552 : * Now that we know the deletion_state, remove the extra
8553 : * attributes added for that purpose. We need to do this
8554 : * otherwise in the case of isDeleted: FALSE the modify will
8555 : * fail with:
8556 : *
8557 : * Failed to apply linked attribute change 'attribute 'isDeleted':
8558 : * invalid modify flags on
8559 : * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8560 : * 0x0'
8561 : *
8562 : * This is because isDeleted is a Boolean, so FALSE is a
8563 : * legitimate value (set by Samba's deletetest.py)
8564 : */
8565 5303 : ldb_msg_remove_attr(msg, "isDeleted");
8566 5303 : ldb_msg_remove_attr(msg, "isRecycled");
8567 :
8568 : /* get the msg->element[] for the link attribute being processed */
8569 5303 : old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8570 5303 : if (old_el == NULL) {
8571 3438 : ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
8572 : LDB_FLAG_MOD_REPLACE, &old_el);
8573 3438 : if (ret != LDB_SUCCESS) {
8574 0 : ldb_module_oom(module);
8575 0 : return LDB_ERR_OPERATIONS_ERROR;
8576 : }
8577 : } else {
8578 1865 : old_el->flags = LDB_FLAG_MOD_REPLACE;
8579 : }
8580 :
8581 : /*
8582 : * go through and process the link target value(s) for this particular
8583 : * source object and attribute. For optimization, the same msg is used
8584 : * across multiple calls to replmd_process_linked_attribute().
8585 : * Note that we should not add or remove any msg attributes inside the
8586 : * loop (we should only add/modify *values* for the attribute being
8587 : * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8588 : * pointers will be invalidated
8589 : */
8590 18787 : for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8591 13485 : prev = DLIST_PREV(la);
8592 13485 : DLIST_REMOVE(la_group->la_entries, la);
8593 :
8594 : /*
8595 : * parse the existing links (this can be costly for a large
8596 : * group, so we try to minimize the times we do it)
8597 : */
8598 13485 : if (pdn_list == NULL) {
8599 10595 : ret = get_parsed_dns_trusted_fallback(module,
8600 : replmd_private,
8601 : tmp_ctx, old_el,
8602 : &pdn_list,
8603 10595 : attr->syntax->ldap_oid,
8604 : NULL);
8605 :
8606 10595 : if (ret != LDB_SUCCESS) {
8607 0 : return ret;
8608 : }
8609 : }
8610 13485 : ret = replmd_process_linked_attribute(module, tmp_ctx,
8611 : replmd_private,
8612 13485 : msg->dn, attr, la, NULL,
8613 13485 : msg->elements, old_el,
8614 : pdn_list, &change_type);
8615 13485 : if (ret != LDB_SUCCESS) {
8616 1 : replmd_txn_cleanup(replmd_private);
8617 1 : return ret;
8618 : }
8619 :
8620 : /*
8621 : * Adding a link reallocs memory, and so invalidates all the
8622 : * pointers in pdn_list. Reparse the PDNs on the next loop
8623 : */
8624 13484 : if (change_type == LINK_CHANGE_ADDED) {
8625 8868 : TALLOC_FREE(pdn_list);
8626 : }
8627 :
8628 13484 : if (change_type != LINK_CHANGE_NONE) {
8629 8914 : num_changes++;
8630 : }
8631 :
8632 13484 : if ((++replmd_private->num_processed % 8192) == 0) {
8633 0 : DBG_NOTICE("Processed %u/%u linked attributes\n",
8634 : replmd_private->num_processed,
8635 : replmd_private->total_links);
8636 : }
8637 : }
8638 :
8639 : /*
8640 : * it's possible we're already up-to-date and so don't need to modify
8641 : * the object at all (e.g. doing a 'drs replicate --full-sync')
8642 : */
8643 5302 : if (num_changes == 0) {
8644 1646 : TALLOC_FREE(tmp_ctx);
8645 1646 : return LDB_SUCCESS;
8646 : }
8647 :
8648 : /*
8649 : * Note that adding the whenChanged/etc attributes below will realloc
8650 : * msg->elements, invalidating the existing element/parsed-DN pointers
8651 : */
8652 3656 : old_el = NULL;
8653 3656 : TALLOC_FREE(pdn_list);
8654 :
8655 : /* update whenChanged/uSNChanged as the object has changed */
8656 3656 : t = time(NULL);
8657 3656 : ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
8658 : &seq_num);
8659 3656 : if (ret != LDB_SUCCESS) {
8660 0 : return ret;
8661 : }
8662 :
8663 3656 : ret = add_time_element(msg, "whenChanged", t);
8664 3656 : if (ret != LDB_SUCCESS) {
8665 0 : ldb_operr(ldb);
8666 0 : return ret;
8667 : }
8668 :
8669 3656 : ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8670 3656 : if (ret != LDB_SUCCESS) {
8671 0 : ldb_operr(ldb);
8672 0 : return ret;
8673 : }
8674 :
8675 : /* apply the link changes to the source object */
8676 3656 : ret = linked_attr_modify(module, msg, NULL);
8677 3656 : if (ret != LDB_SUCCESS) {
8678 0 : ldb_debug(ldb, LDB_DEBUG_WARNING,
8679 : "Failed to apply linked attribute change "
8680 : "Error: '%s' DN: '%s' Attribute: '%s'\n",
8681 : ldb_errstring(ldb),
8682 0 : ldb_dn_get_linearized(msg->dn),
8683 0 : attr->lDAPDisplayName);
8684 0 : TALLOC_FREE(tmp_ctx);
8685 0 : return ret;
8686 : }
8687 :
8688 3656 : TALLOC_FREE(tmp_ctx);
8689 3656 : return LDB_SUCCESS;
8690 : }
8691 :
8692 : /*
8693 : on prepare commit we loop over our queued la_context structures and
8694 : apply each of them
8695 : */
8696 304373 : static int replmd_prepare_commit(struct ldb_module *module)
8697 : {
8698 2188 : struct replmd_private *replmd_private =
8699 304373 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8700 2188 : struct la_group *la_group, *prev;
8701 2188 : int ret;
8702 :
8703 304373 : if (replmd_private->la_list != NULL) {
8704 356 : DBG_NOTICE("Processing linked attributes\n");
8705 : }
8706 :
8707 : /*
8708 : * Walk the list of linked attributes from DRS replication.
8709 : *
8710 : * We walk backwards, to do the first entry first, as we
8711 : * added the entries with DLIST_ADD() which puts them at the
8712 : * start of the list
8713 : *
8714 : * Links are grouped together so we process links for the same
8715 : * source object in one go.
8716 : */
8717 304373 : for (la_group = DLIST_TAIL(replmd_private->la_list);
8718 309677 : la_group != NULL;
8719 5304 : la_group = prev) {
8720 :
8721 5305 : prev = DLIST_PREV(la_group);
8722 5305 : DLIST_REMOVE(replmd_private->la_list, la_group);
8723 5305 : ret = replmd_process_la_group(module, replmd_private,
8724 : la_group);
8725 5305 : if (ret != LDB_SUCCESS) {
8726 1 : replmd_txn_cleanup(replmd_private);
8727 1 : return ret;
8728 : }
8729 : }
8730 :
8731 304372 : replmd_txn_cleanup(replmd_private);
8732 :
8733 : /* possibly change @REPLCHANGED */
8734 304372 : ret = replmd_notify_store(module, NULL);
8735 304372 : if (ret != LDB_SUCCESS) {
8736 0 : return ret;
8737 : }
8738 :
8739 304372 : return ldb_next_prepare_commit(module);
8740 : }
8741 :
8742 45579 : static int replmd_del_transaction(struct ldb_module *module)
8743 : {
8744 4 : struct replmd_private *replmd_private =
8745 45579 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8746 45579 : replmd_txn_cleanup(replmd_private);
8747 :
8748 45579 : return ldb_next_del_trans(module);
8749 : }
8750 :
8751 :
8752 : static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8753 : .name = "repl_meta_data",
8754 : .init_context = replmd_init,
8755 : .add = replmd_add,
8756 : .modify = replmd_modify,
8757 : .rename = replmd_rename,
8758 : .del = replmd_delete,
8759 : .extended = replmd_extended,
8760 : .start_transaction = replmd_start_transaction,
8761 : .prepare_commit = replmd_prepare_commit,
8762 : .del_transaction = replmd_del_transaction,
8763 : };
8764 :
8765 6286 : int ldb_repl_meta_data_module_init(const char *version)
8766 : {
8767 6286 : LDB_MODULE_CHECK_VERSION(version);
8768 6286 : return ldb_register_module(&ldb_repl_meta_data_module_ops);
8769 : }
|