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