s4:dsdb/repl_meta_data: check replmd_add_backlink() result in replmd_modify_la_add()
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob871e1d56ec2d05aeaad82aed6546552b8fb1d28d
1 /*
2 ldb database library
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
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Name: ldb
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
34 * Author: Simo Sorce
35 * Author: Stefan Metzmacher
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"
55 #undef strcasecmp
57 #undef DBGC_CLASS
58 #define DBGC_CLASS DBGC_DRS_REPL
60 /* the RMD_VERSION for linked attributes starts from 1 */
61 #define RMD_VERSION_INITIAL 1
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
67 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
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;
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).
96 struct la_group {
97 struct la_group *next, *prev;
98 struct la_entry *la_entries;
101 struct la_entry {
102 struct la_entry *next, *prev;
103 struct drsuapi_DsReplicaLinkedAttribute *la;
104 uint32_t dsdb_repl_flags;
107 struct replmd_replicated_request {
108 struct ldb_module *module;
109 struct ldb_request *req;
111 const struct dsdb_schema *schema;
112 struct GUID our_invocation_id;
114 /* the controls we pass down */
115 struct ldb_control **controls;
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)
122 struct la_backlink *la_backlinks;
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;
129 struct ldb_message *search_msg;
130 struct GUID local_parent_guid;
132 uint64_t seq_num;
133 bool is_urgent;
135 bool isDeleted;
137 bool fix_link_sid;
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).
145 typedef enum {
146 LINK_CHANGE_NONE,
147 LINK_CHANGE_ADDED,
148 LINK_CHANGE_MODIFIED,
149 } replmd_link_changed;
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);
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);
179 enum urgent_situation {
180 REPL_URGENT_ON_CREATE = 1,
181 REPL_URGENT_ON_UPDATE = 2,
182 REPL_URGENT_ON_DELETE = 4
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
193 static bool replmd_recyclebin_enabled(struct ldb_module *module)
195 bool enabled = false;
196 struct replmd_private *replmd_private =
197 talloc_get_type_abort(ldb_module_get_private(module),
198 struct replmd_private);
201 * only lookup the recycle-bin state once per replication, then cache
202 * the result. This can save us 1000s of DB searches
204 if (!replmd_private->recyclebin_state_known) {
205 int ret = dsdb_recyclebin_enabled(module, &enabled);
206 if (ret != LDB_SUCCESS) {
207 return false;
210 replmd_private->recyclebin_enabled = enabled;
211 replmd_private->recyclebin_state_known = true;
214 return replmd_private->recyclebin_enabled;
217 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)
222 bool enabled = false;
224 if (msg == NULL) {
225 *current_state = OBJECT_REMOVED;
226 if (next_state != NULL) {
227 *next_state = OBJECT_REMOVED;
229 return;
232 enabled = replmd_recyclebin_enabled(module);
234 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
235 if (!enabled) {
236 *current_state = OBJECT_TOMBSTONE;
237 if (next_state != NULL) {
238 *next_state = OBJECT_REMOVED;
240 return;
243 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
244 *current_state = OBJECT_RECYCLED;
245 if (next_state != NULL) {
246 *next_state = OBJECT_REMOVED;
248 return;
251 *current_state = OBJECT_DELETED;
252 if (next_state != NULL) {
253 *next_state = OBJECT_RECYCLED;
255 return;
258 *current_state = OBJECT_NOT_DELETED;
259 if (next_state == NULL) {
260 return;
263 if (enabled) {
264 *next_state = OBJECT_DELETED;
265 } else {
266 *next_state = OBJECT_TOMBSTONE;
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}
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
292 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
293 enum urgent_situation situation)
295 unsigned int i, j;
296 for (i=0; urgent_objects[i].update_name; i++) {
298 if ((situation & urgent_objects[i].repl_situation) == 0) {
299 continue;
302 for (j=0; j<objectclass_el->num_values; j++) {
303 const struct ldb_val *v = &objectclass_el->values[j];
304 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
305 return true;
309 return false;
312 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
314 if (ldb_attr_in_list(urgent_attrs, el->name)) {
315 return true;
317 return false;
320 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
323 initialise the module
324 allocate the private structure and build the list
325 of partition DNs for use by replmd_notify()
327 static int replmd_init(struct ldb_module *module)
329 struct replmd_private *replmd_private;
330 struct ldb_context *ldb = ldb_module_get_ctx(module);
331 int ret;
333 replmd_private = talloc_zero(module, struct replmd_private);
334 if (replmd_private == NULL) {
335 ldb_oom(ldb);
336 return LDB_ERR_OPERATIONS_ERROR;
339 ret = dsdb_check_samba_compatible_feature(module,
340 SAMBA_SORTED_LINKS_FEATURE,
341 &replmd_private->sorted_links);
342 if (ret != LDB_SUCCESS) {
343 talloc_free(replmd_private);
344 return ret;
347 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
348 ldb_module_set_private(module, replmd_private);
349 return ldb_next_init(module);
353 cleanup our per-transaction contexts
355 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
357 talloc_free(replmd_private->la_ctx);
358 replmd_private->la_list = NULL;
359 replmd_private->la_ctx = NULL;
360 replmd_private->recyclebin_state_known = false;
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;
373 a ldb_modify request operating on modules below the
374 current module
376 static int linked_attr_modify(struct ldb_module *module,
377 const struct ldb_message *message,
378 struct ldb_request *parent)
380 struct ldb_request *mod_req;
381 int ret;
382 struct ldb_context *ldb = ldb_module_get_ctx(module);
383 TALLOC_CTX *tmp_ctx = talloc_new(module);
384 struct ldb_result *res;
386 res = talloc_zero(tmp_ctx, struct ldb_result);
387 if (!res) {
388 talloc_free(tmp_ctx);
389 return ldb_oom(ldb_module_get_ctx(module));
392 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
393 message,
394 NULL,
395 res,
396 ldb_modify_default_callback,
397 parent);
398 LDB_REQ_SET_LOCATION(mod_req);
399 if (ret != LDB_SUCCESS) {
400 talloc_free(tmp_ctx);
401 return ret;
404 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
405 false, NULL);
406 if (ret != LDB_SUCCESS) {
407 return ret;
410 /* Run the new request */
411 ret = ldb_next_request(module, mod_req);
413 if (ret == LDB_SUCCESS) {
414 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
417 talloc_free(tmp_ctx);
418 return ret;
422 process a backlinks we accumulated during a transaction, adding and
423 deleting the backlinks from the target objects
425 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
427 struct ldb_dn *target_dn, *source_dn;
428 int ret;
429 struct ldb_context *ldb = ldb_module_get_ctx(module);
430 struct ldb_message *msg;
431 TALLOC_CTX *frame = talloc_stackframe();
432 char *dn_string;
435 - find DN of target
436 - find DN of source
437 - construct ldb_message
438 - either an add or a delete
440 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
441 if (ret != LDB_SUCCESS) {
442 struct GUID_txt_buf guid_str;
443 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
444 GUID_buf_string(&bl->target_guid, &guid_str));
445 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
446 talloc_free(frame);
447 return LDB_SUCCESS;
450 msg = ldb_msg_new(frame);
451 if (msg == NULL) {
452 ldb_module_oom(module);
453 talloc_free(frame);
454 return LDB_ERR_OPERATIONS_ERROR;
457 source_dn = ldb_dn_copy(frame, bl->forward_dn);
458 if (!source_dn) {
459 ldb_module_oom(module);
460 talloc_free(frame);
461 return LDB_ERR_OPERATIONS_ERROR;
462 } else {
463 /* Filter down to the attributes we want in the backlink */
464 const char *accept[] = { "GUID", "SID", NULL };
465 ldb_dn_extended_filter(source_dn, accept);
468 /* construct a ldb_message for adding/deleting the backlink */
469 msg->dn = target_dn;
470 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
471 if (!dn_string) {
472 ldb_module_oom(module);
473 talloc_free(frame);
474 return LDB_ERR_OPERATIONS_ERROR;
476 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
477 if (ret != LDB_SUCCESS) {
478 talloc_free(frame);
479 return ret;
481 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
483 /* a backlink should never be single valued. Unfortunately the
484 exchange schema has a attribute
485 msExchBridgeheadedLocalConnectorsDNBL which is single
486 valued and a backlink. We need to cope with that by
487 ignoring the single value flag */
488 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
490 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
491 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
492 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
493 cope with possible corruption where the backlink has
494 already been removed */
495 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
496 ldb_dn_get_linearized(target_dn),
497 ldb_dn_get_linearized(source_dn),
498 ldb_errstring(ldb)));
499 ret = LDB_SUCCESS;
500 } else if (ret != LDB_SUCCESS) {
501 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
502 bl->active?"add":"remove",
503 ldb_dn_get_linearized(source_dn),
504 ldb_dn_get_linearized(target_dn),
505 ldb_errstring(ldb));
506 talloc_free(frame);
507 return ret;
509 talloc_free(frame);
510 return ret;
514 add a backlink to the list of backlinks to add/delete in the prepare
515 commit
517 forward_dn is stolen onto the defereed context
519 static int replmd_defer_add_backlink(struct ldb_module *module,
520 struct replmd_private *replmd_private,
521 const struct dsdb_schema *schema,
522 struct replmd_replicated_request *ac,
523 struct ldb_dn *forward_dn,
524 struct GUID *target_guid, bool active,
525 const struct dsdb_attribute *schema_attr,
526 struct ldb_request *parent)
528 const struct dsdb_attribute *target_attr;
529 struct la_backlink *bl;
531 bl = talloc(ac, struct la_backlink);
532 if (bl == NULL) {
533 ldb_module_oom(module);
534 return LDB_ERR_OPERATIONS_ERROR;
537 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
538 if (!target_attr) {
540 * windows 2003 has a broken schema where the
541 * definition of msDS-IsDomainFor is missing (which is
542 * supposed to be the backlink of the
543 * msDS-HasDomainNCs attribute
545 return LDB_SUCCESS;
548 bl->attr_name = target_attr->lDAPDisplayName;
549 bl->forward_dn = talloc_steal(bl, forward_dn);
550 bl->target_guid = *target_guid;
551 bl->active = active;
553 DLIST_ADD(ac->la_backlinks, bl);
555 return LDB_SUCCESS;
559 add a backlink to the list of backlinks to add/delete in the prepare
560 commit
562 static int replmd_add_backlink(struct ldb_module *module,
563 struct replmd_private *replmd_private,
564 const struct dsdb_schema *schema,
565 struct ldb_dn *forward_dn,
566 struct GUID *target_guid, bool active,
567 const struct dsdb_attribute *schema_attr,
568 struct ldb_request *parent)
570 const struct dsdb_attribute *target_attr;
571 struct la_backlink bl;
572 int ret;
574 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
575 if (!target_attr) {
577 * windows 2003 has a broken schema where the
578 * definition of msDS-IsDomainFor is missing (which is
579 * supposed to be the backlink of the
580 * msDS-HasDomainNCs attribute
582 return LDB_SUCCESS;
585 bl.attr_name = target_attr->lDAPDisplayName;
586 bl.forward_dn = forward_dn;
587 bl.target_guid = *target_guid;
588 bl.active = active;
590 ret = replmd_process_backlink(module, &bl, parent);
591 return ret;
596 * Callback for most write operations in this module:
598 * notify the repl task that a object has changed. The notifies are
599 * gathered up in the replmd_private structure then written to the
600 * @REPLCHANGED object in each partition during the prepare_commit
602 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
604 int ret;
605 struct replmd_replicated_request *ac =
606 talloc_get_type_abort(req->context, struct replmd_replicated_request);
607 struct replmd_private *replmd_private =
608 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
609 struct nc_entry *modified_partition;
610 struct ldb_control *partition_ctrl;
611 const struct dsdb_control_current_partition *partition;
613 struct ldb_control **controls;
615 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
617 controls = ares->controls;
618 if (ldb_request_get_control(ac->req,
619 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
621 * Remove the current partition control from what we pass up
622 * the chain if it hasn't been requested manually.
624 controls = ldb_controls_except_specified(ares->controls, ares,
625 partition_ctrl);
628 if (ares->error != LDB_SUCCESS) {
629 struct GUID_txt_buf guid_txt;
630 struct ldb_message *msg = NULL;
631 char *s = NULL;
633 if (ac->apply_mode == false) {
634 DBG_NOTICE("Originating update failure. Error is: %s\n",
635 ldb_strerror(ares->error));
636 return ldb_module_done(ac->req, controls,
637 ares->response, ares->error);
640 msg = ac->objs->objects[ac->index_current].msg;
642 * Set at DBG_NOTICE as once these start to happe, they
643 * will happen a lot until resolved, due to repeated
644 * replication. The caller will probably print the
645 * ldb error string anyway.
647 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
648 ldb_dn_get_linearized(msg->dn),
649 ldb_strerror(ares->error));
651 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
653 LDB_CHANGETYPE_ADD,
654 msg);
656 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
657 ac->search_msg == NULL ? "ADD" : "MODIFY",
658 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
659 &guid_txt),
661 talloc_free(s);
662 return ldb_module_done(ac->req, controls,
663 ares->response, ares->error);
666 if (ares->type != LDB_REPLY_DONE) {
667 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
668 return ldb_module_done(ac->req, NULL,
669 NULL, LDB_ERR_OPERATIONS_ERROR);
672 if (ac->apply_mode == false) {
673 struct la_backlink *bl;
675 * process our backlink list after an replmd_add(),
676 * creating and deleting backlinks as necessary (this
677 * code is sync). The other cases are handled inline
678 * with the modify.
680 for (bl=ac->la_backlinks; bl; bl=bl->next) {
681 ret = replmd_process_backlink(ac->module, bl, ac->req);
682 if (ret != LDB_SUCCESS) {
683 return ldb_module_done(ac->req, NULL,
684 NULL, ret);
689 if (!partition_ctrl) {
690 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
691 return ldb_module_done(ac->req, NULL,
692 NULL, LDB_ERR_OPERATIONS_ERROR);
695 partition = talloc_get_type_abort(partition_ctrl->data,
696 struct dsdb_control_current_partition);
698 if (ac->seq_num > 0) {
699 for (modified_partition = replmd_private->ncs; modified_partition;
700 modified_partition = modified_partition->next) {
701 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
702 break;
706 if (modified_partition == NULL) {
707 modified_partition = talloc_zero(replmd_private, struct nc_entry);
708 if (!modified_partition) {
709 ldb_oom(ldb_module_get_ctx(ac->module));
710 return ldb_module_done(ac->req, NULL,
711 NULL, LDB_ERR_OPERATIONS_ERROR);
713 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
714 if (!modified_partition->dn) {
715 ldb_oom(ldb_module_get_ctx(ac->module));
716 return ldb_module_done(ac->req, NULL,
717 NULL, LDB_ERR_OPERATIONS_ERROR);
719 DLIST_ADD(replmd_private->ncs, modified_partition);
722 if (ac->seq_num > modified_partition->mod_usn) {
723 modified_partition->mod_usn = ac->seq_num;
724 if (ac->is_urgent) {
725 modified_partition->mod_usn_urgent = ac->seq_num;
728 if (!ac->apply_mode) {
729 replmd_private->originating_updates = true;
733 if (ac->apply_mode) {
734 ret = replmd_replicated_apply_isDeleted(ac);
735 if (ret != LDB_SUCCESS) {
736 return ldb_module_done(ac->req, NULL, NULL, ret);
738 return ret;
739 } else {
740 /* free the partition control container here, for the
741 * common path. Other cases will have it cleaned up
742 * eventually with the ares */
743 talloc_free(partition_ctrl);
744 return ldb_module_done(ac->req, controls,
745 ares->response, LDB_SUCCESS);
751 * update a @REPLCHANGED record in each partition if there have been
752 * any writes of replicated data in the partition
754 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
756 struct replmd_private *replmd_private =
757 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
759 while (replmd_private->ncs) {
760 int ret;
761 struct nc_entry *modified_partition = replmd_private->ncs;
763 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
764 modified_partition->mod_usn,
765 modified_partition->mod_usn_urgent, parent);
766 if (ret != LDB_SUCCESS) {
767 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
768 ldb_dn_get_linearized(modified_partition->dn)));
769 return ret;
772 if (ldb_dn_compare(modified_partition->dn,
773 replmd_private->schema_dn) == 0) {
774 struct ldb_result *ext_res;
775 ret = dsdb_module_extended(module,
776 replmd_private->schema_dn,
777 &ext_res,
778 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
779 ext_res,
780 DSDB_FLAG_NEXT_MODULE,
781 parent);
782 if (ret != LDB_SUCCESS) {
783 return ret;
785 talloc_free(ext_res);
788 DLIST_REMOVE(replmd_private->ncs, modified_partition);
789 talloc_free(modified_partition);
792 return LDB_SUCCESS;
797 created a replmd_replicated_request context
799 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
800 struct ldb_request *req)
802 struct ldb_context *ldb;
803 struct replmd_replicated_request *ac;
804 const struct GUID *our_invocation_id;
806 ldb = ldb_module_get_ctx(module);
808 ac = talloc_zero(req, struct replmd_replicated_request);
809 if (ac == NULL) {
810 ldb_oom(ldb);
811 return NULL;
814 ac->module = module;
815 ac->req = req;
817 ac->schema = dsdb_get_schema(ldb, ac);
818 if (!ac->schema) {
819 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
820 "replmd_modify: no dsdb_schema loaded");
821 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
822 talloc_free(ac);
823 return NULL;
826 /* get our invocationId */
827 our_invocation_id = samdb_ntds_invocation_id(ldb);
828 if (!our_invocation_id) {
829 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
830 "replmd_add: unable to find invocationId\n");
831 talloc_free(ac);
832 return NULL;
834 ac->our_invocation_id = *our_invocation_id;
836 return ac;
840 add a time element to a record
842 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
844 struct ldb_message_element *el;
845 char *s;
846 int ret;
848 if (ldb_msg_find_element(msg, attr) != NULL) {
849 return LDB_SUCCESS;
852 s = ldb_timestring(msg, t);
853 if (s == NULL) {
854 return LDB_ERR_OPERATIONS_ERROR;
857 ret = ldb_msg_add_string(msg, attr, s);
858 if (ret != LDB_SUCCESS) {
859 return ret;
862 el = ldb_msg_find_element(msg, attr);
863 /* always set as replace. This works because on add ops, the flag
864 is ignored */
865 el->flags = LDB_FLAG_MOD_REPLACE;
867 return LDB_SUCCESS;
871 add a uint64_t element to a record
873 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
874 const char *attr, uint64_t v)
876 struct ldb_message_element *el;
877 int ret;
879 if (ldb_msg_find_element(msg, attr) != NULL) {
880 return LDB_SUCCESS;
883 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
884 if (ret != LDB_SUCCESS) {
885 return ret;
888 el = ldb_msg_find_element(msg, attr);
889 /* always set as replace. This works because on add ops, the flag
890 is ignored */
891 el->flags = LDB_FLAG_MOD_REPLACE;
893 return LDB_SUCCESS;
896 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
897 const struct replPropertyMetaData1 *m2)
900 * This assignment seems inoccous, but it is critical for the
901 * system, as we need to do the comparisons as a unsigned
902 * quantity, not signed (enums are signed integers)
904 uint32_t attid_1 = m1->attid;
905 uint32_t attid_2 = m2->attid;
907 if (attid_1 == attid_2) {
908 return 0;
912 * See above regarding this being an unsigned comparison.
913 * Otherwise when the high bit is set on non-standard
914 * attributes, they would end up first, before objectClass
915 * (0).
917 return attid_1 > attid_2 ? 1 : -1;
920 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
921 struct replPropertyMetaDataCtr1 *ctr1,
922 struct ldb_dn *dn)
924 if (ctr1->count == 0) {
925 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
926 "No elements found in replPropertyMetaData for %s!\n",
927 ldb_dn_get_linearized(dn));
928 return LDB_ERR_CONSTRAINT_VIOLATION;
931 /* the objectClass attribute is value 0x00000000, so must be first */
932 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
933 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
934 "No objectClass found in replPropertyMetaData for %s!\n",
935 ldb_dn_get_linearized(dn));
936 return LDB_ERR_OBJECT_CLASS_VIOLATION;
939 return LDB_SUCCESS;
942 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
943 struct replPropertyMetaDataCtr1 *ctr1,
944 struct ldb_dn *dn)
946 /* Note this is O(n^2) for the almost-sorted case, which this is */
947 TYPESAFE_QSORT(ctr1->array, ctr1->count,
948 replmd_replPropertyMetaData1_attid_sort);
949 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
952 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
953 const struct ldb_message_element *e2,
954 const struct dsdb_schema *schema)
956 const struct dsdb_attribute *a1;
957 const struct dsdb_attribute *a2;
960 * TODO: make this faster by caching the dsdb_attribute pointer
961 * on the ldb_messag_element
964 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
965 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
968 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
969 * in the schema
971 if (!a1 || !a2) {
972 return strcasecmp(e1->name, e2->name);
974 if (a1->attributeID_id == a2->attributeID_id) {
975 return 0;
977 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
980 static void replmd_ldb_message_sort(struct ldb_message *msg,
981 const struct dsdb_schema *schema)
983 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
986 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
987 const struct GUID *invocation_id,
988 uint64_t local_usn, NTTIME nttime);
990 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
992 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
993 struct ldb_message_element *el, struct parsed_dn **pdn,
994 const char *ldap_oid, struct ldb_request *parent);
996 static int check_parsed_dn_duplicates(struct ldb_module *module,
997 struct ldb_message_element *el,
998 struct parsed_dn *pdn);
1001 fix up linked attributes in replmd_add.
1002 This involves setting up the right meta-data in extended DN
1003 components, and creating backlinks to the object
1005 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1006 struct replmd_private *replmd_private,
1007 struct ldb_message_element *el,
1008 struct replmd_replicated_request *ac,
1009 NTTIME now,
1010 struct ldb_dn *forward_dn,
1011 const struct dsdb_attribute *sa,
1012 struct ldb_request *parent)
1014 unsigned int i;
1015 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1016 struct ldb_context *ldb = ldb_module_get_ctx(module);
1017 struct parsed_dn *pdn;
1018 /* We will take a reference to the schema in replmd_add_backlink */
1019 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
1020 struct ldb_val *new_values = NULL;
1021 int ret;
1023 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1024 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1025 } else {
1026 ldb_asprintf_errstring(ldb,
1027 "Attribute %s is single valued but "
1028 "more than one value has been supplied",
1029 el->name);
1030 talloc_free(tmp_ctx);
1031 return LDB_ERR_CONSTRAINT_VIOLATION;
1035 * At the successful end of these functions el->values is
1036 * overwritten with new_values. However get_parsed_dns()
1037 * points p->v at the supplied el and it effectively gets used
1038 * as a working area by replmd_build_la_val(). So we must
1039 * duplicate it because our caller only called
1040 * ldb_msg_copy_shallow().
1043 el->values = talloc_memdup(tmp_ctx,
1044 el->values,
1045 sizeof(el->values[0]) * el->num_values);
1046 if (el->values == NULL) {
1047 ldb_module_oom(module);
1048 talloc_free(tmp_ctx);
1049 return LDB_ERR_OPERATIONS_ERROR;
1052 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1053 sa->syntax->ldap_oid, parent);
1054 if (ret != LDB_SUCCESS) {
1055 talloc_free(tmp_ctx);
1056 return ret;
1059 ret = check_parsed_dn_duplicates(module, el, pdn);
1060 if (ret != LDB_SUCCESS) {
1061 talloc_free(tmp_ctx);
1062 return ret;
1065 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1066 if (new_values == NULL) {
1067 ldb_module_oom(module);
1068 talloc_free(tmp_ctx);
1069 return LDB_ERR_OPERATIONS_ERROR;
1072 for (i = 0; i < el->num_values; i++) {
1073 struct parsed_dn *p = &pdn[i];
1074 ret = replmd_build_la_val(new_values, p->v, p->dsdb_dn,
1075 &ac->our_invocation_id,
1076 ac->seq_num, now);
1077 if (ret != LDB_SUCCESS) {
1078 talloc_free(tmp_ctx);
1079 return ret;
1082 ret = replmd_defer_add_backlink(module, replmd_private,
1083 schema, ac,
1084 forward_dn, &p->guid, true, sa,
1085 parent);
1086 if (ret != LDB_SUCCESS) {
1087 talloc_free(tmp_ctx);
1088 return ret;
1091 new_values[i] = *p->v;
1093 el->values = talloc_steal(mem_ctx, new_values);
1095 talloc_free(tmp_ctx);
1096 return LDB_SUCCESS;
1099 static int replmd_add_make_extended_dn(struct ldb_request *req,
1100 const DATA_BLOB *guid_blob,
1101 struct ldb_dn **_extended_dn)
1103 int ret;
1104 const DATA_BLOB *sid_blob;
1105 /* Calculate an extended DN for any linked attributes */
1106 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1107 if (!extended_dn) {
1108 return LDB_ERR_OPERATIONS_ERROR;
1110 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1111 if (ret != LDB_SUCCESS) {
1112 return ret;
1115 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1116 if (sid_blob != NULL) {
1117 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1118 if (ret != LDB_SUCCESS) {
1119 return ret;
1122 *_extended_dn = extended_dn;
1123 return LDB_SUCCESS;
1127 intercept add requests
1129 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1131 struct ldb_context *ldb;
1132 struct ldb_control *control;
1133 struct replmd_replicated_request *ac;
1134 enum ndr_err_code ndr_err;
1135 struct ldb_request *down_req;
1136 struct ldb_message *msg;
1137 const DATA_BLOB *guid_blob;
1138 DATA_BLOB guid_blob_stack;
1139 struct GUID guid;
1140 uint8_t guid_data[16];
1141 struct replPropertyMetaDataBlob nmd;
1142 struct ldb_val nmd_value;
1143 struct ldb_dn *extended_dn = NULL;
1146 * The use of a time_t here seems odd, but as the NTTIME
1147 * elements are actually declared as NTTIME_1sec in the IDL,
1148 * getting a higher resolution timestamp is not required.
1150 time_t t = time(NULL);
1151 NTTIME now;
1152 char *time_str;
1153 int ret;
1154 unsigned int i;
1155 unsigned int functional_level;
1156 uint32_t ni=0;
1157 bool allow_add_guid = false;
1158 bool remove_current_guid = false;
1159 bool is_urgent = false;
1160 bool is_schema_nc = false;
1161 struct ldb_message_element *objectclass_el;
1162 struct replmd_private *replmd_private =
1163 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1165 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1166 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1167 if (control) {
1168 allow_add_guid = true;
1171 /* do not manipulate our control entries */
1172 if (ldb_dn_is_special(req->op.add.message->dn)) {
1173 return ldb_next_request(module, req);
1176 ldb = ldb_module_get_ctx(module);
1178 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1180 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1181 if (guid_blob != NULL) {
1182 if (!allow_add_guid) {
1183 ldb_set_errstring(ldb,
1184 "replmd_add: it's not allowed to add an object with objectGUID!");
1185 return LDB_ERR_UNWILLING_TO_PERFORM;
1186 } else {
1187 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1188 if (!NT_STATUS_IS_OK(status)) {
1189 ldb_set_errstring(ldb,
1190 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1191 return LDB_ERR_UNWILLING_TO_PERFORM;
1193 /* we remove this attribute as it can be a string and
1194 * will not be treated correctly and then we will re-add
1195 * it later on in the good format */
1196 remove_current_guid = true;
1198 } else {
1199 /* a new GUID */
1200 guid = GUID_random();
1202 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1204 /* This can't fail */
1205 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1206 (ndr_push_flags_fn_t)ndr_push_GUID);
1207 guid_blob = &guid_blob_stack;
1210 ac = replmd_ctx_init(module, req);
1211 if (ac == NULL) {
1212 return ldb_module_oom(module);
1215 functional_level = dsdb_functional_level(ldb);
1217 /* Get a sequence number from the backend */
1218 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1219 if (ret != LDB_SUCCESS) {
1220 talloc_free(ac);
1221 return ret;
1224 /* we have to copy the message as the caller might have it as a const */
1225 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1226 if (msg == NULL) {
1227 ldb_oom(ldb);
1228 talloc_free(ac);
1229 return LDB_ERR_OPERATIONS_ERROR;
1232 /* generated times */
1233 unix_to_nt_time(&now, t);
1234 time_str = ldb_timestring(msg, t);
1235 if (!time_str) {
1236 ldb_oom(ldb);
1237 talloc_free(ac);
1238 return LDB_ERR_OPERATIONS_ERROR;
1240 if (remove_current_guid) {
1241 ldb_msg_remove_attr(msg,"objectGUID");
1245 * remove autogenerated attributes
1247 ldb_msg_remove_attr(msg, "whenCreated");
1248 ldb_msg_remove_attr(msg, "whenChanged");
1249 ldb_msg_remove_attr(msg, "uSNCreated");
1250 ldb_msg_remove_attr(msg, "uSNChanged");
1251 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1254 * readd replicated attributes
1256 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1257 if (ret != LDB_SUCCESS) {
1258 ldb_oom(ldb);
1259 talloc_free(ac);
1260 return ret;
1263 /* build the replication meta_data */
1264 ZERO_STRUCT(nmd);
1265 nmd.version = 1;
1266 nmd.ctr.ctr1.count = msg->num_elements;
1267 nmd.ctr.ctr1.array = talloc_array(msg,
1268 struct replPropertyMetaData1,
1269 nmd.ctr.ctr1.count);
1270 if (!nmd.ctr.ctr1.array) {
1271 ldb_oom(ldb);
1272 talloc_free(ac);
1273 return LDB_ERR_OPERATIONS_ERROR;
1276 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1278 for (i=0; i < msg->num_elements;) {
1279 struct ldb_message_element *e = &msg->elements[i];
1280 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1281 const struct dsdb_attribute *sa;
1283 if (e->name[0] == '@') {
1284 i++;
1285 continue;
1288 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1289 if (!sa) {
1290 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1291 "replmd_add: attribute '%s' not defined in schema\n",
1292 e->name);
1293 talloc_free(ac);
1294 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1297 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1298 /* if the attribute is not replicated (0x00000001)
1299 * or constructed (0x00000004) it has no metadata
1301 i++;
1302 continue;
1305 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1306 if (extended_dn == NULL) {
1307 ret = replmd_add_make_extended_dn(req,
1308 guid_blob,
1309 &extended_dn);
1310 if (ret != LDB_SUCCESS) {
1311 talloc_free(ac);
1312 return ret;
1317 * Prepare the context for the backlinks and
1318 * create metadata for the forward links. The
1319 * backlinks are created in
1320 * replmd_op_callback() after the successful
1321 * ADD of the object.
1323 ret = replmd_add_fix_la(module, msg->elements,
1324 replmd_private, e,
1325 ac, now,
1326 extended_dn,
1327 sa, req);
1328 if (ret != LDB_SUCCESS) {
1329 talloc_free(ac);
1330 return ret;
1332 /* linked attributes are not stored in
1333 replPropertyMetaData in FL above w2k */
1334 i++;
1335 continue;
1338 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1339 m->version = 1;
1340 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1341 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1342 const char* rdn;
1344 if (rdn_val == NULL) {
1345 ldb_oom(ldb);
1346 talloc_free(ac);
1347 return LDB_ERR_OPERATIONS_ERROR;
1350 rdn = (const char*)rdn_val->data;
1351 if (strcmp(rdn, "Deleted Objects") == 0) {
1353 * Set the originating_change_time to 29/12/9999 at 23:59:59
1354 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1356 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1357 } else {
1358 m->originating_change_time = now;
1360 } else {
1361 m->originating_change_time = now;
1363 m->originating_invocation_id = ac->our_invocation_id;
1364 m->originating_usn = ac->seq_num;
1365 m->local_usn = ac->seq_num;
1366 ni++;
1368 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1369 i++;
1370 continue;
1373 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1375 if (e->num_values != 0) {
1376 i++;
1377 continue;
1380 ldb_msg_remove_element(msg, e);
1383 /* fix meta data count */
1384 nmd.ctr.ctr1.count = ni;
1387 * sort meta data array
1389 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1390 if (ret != LDB_SUCCESS) {
1391 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1392 talloc_free(ac);
1393 return ret;
1396 /* generated NDR encoded values */
1397 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1398 &nmd,
1399 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1400 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1401 ldb_oom(ldb);
1402 talloc_free(ac);
1403 return LDB_ERR_OPERATIONS_ERROR;
1407 * add the autogenerated values
1409 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1410 if (ret != LDB_SUCCESS) {
1411 ldb_oom(ldb);
1412 talloc_free(ac);
1413 return ret;
1415 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1416 if (ret != LDB_SUCCESS) {
1417 ldb_oom(ldb);
1418 talloc_free(ac);
1419 return ret;
1421 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1422 if (ret != LDB_SUCCESS) {
1423 ldb_oom(ldb);
1424 talloc_free(ac);
1425 return ret;
1427 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1428 if (ret != LDB_SUCCESS) {
1429 ldb_oom(ldb);
1430 talloc_free(ac);
1431 return ret;
1433 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1434 if (ret != LDB_SUCCESS) {
1435 ldb_oom(ldb);
1436 talloc_free(ac);
1437 return ret;
1441 * sort the attributes by attid before storing the object
1443 replmd_ldb_message_sort(msg, ac->schema);
1446 * Assert that we do have an objectClass
1448 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1449 if (objectclass_el == NULL) {
1450 ldb_asprintf_errstring(ldb, __location__
1451 ": objectClass missing on %s\n",
1452 ldb_dn_get_linearized(msg->dn));
1453 talloc_free(ac);
1454 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1456 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1457 REPL_URGENT_ON_CREATE);
1459 ac->is_urgent = is_urgent;
1460 ret = ldb_build_add_req(&down_req, ldb, ac,
1461 msg,
1462 req->controls,
1463 ac, replmd_op_callback,
1464 req);
1466 LDB_REQ_SET_LOCATION(down_req);
1467 if (ret != LDB_SUCCESS) {
1468 talloc_free(ac);
1469 return ret;
1472 /* current partition control is needed by "replmd_op_callback" */
1473 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1474 ret = ldb_request_add_control(down_req,
1475 DSDB_CONTROL_CURRENT_PARTITION_OID,
1476 false, NULL);
1477 if (ret != LDB_SUCCESS) {
1478 talloc_free(ac);
1479 return ret;
1483 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1484 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1485 if (ret != LDB_SUCCESS) {
1486 talloc_free(ac);
1487 return ret;
1491 /* mark the relax control done */
1492 if (control) {
1493 control->critical = 0;
1495 /* go on with the call chain */
1496 return ldb_next_request(module, down_req);
1501 * update the replPropertyMetaData for one element
1503 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1504 struct ldb_message *msg,
1505 struct ldb_message_element *el,
1506 struct ldb_message_element *old_el,
1507 struct replPropertyMetaDataBlob *omd,
1508 const struct dsdb_schema *schema,
1509 uint64_t *seq_num,
1510 const struct GUID *our_invocation_id,
1511 NTTIME now,
1512 bool is_schema_nc,
1513 bool is_forced_rodc,
1514 struct ldb_request *req)
1516 uint32_t i;
1517 const struct dsdb_attribute *a;
1518 struct replPropertyMetaData1 *md1;
1519 bool may_skip = false;
1520 uint32_t attid;
1522 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1523 if (a == NULL) {
1524 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1525 /* allow this to make it possible for dbcheck
1526 to remove bad attributes */
1527 return LDB_SUCCESS;
1530 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1531 el->name));
1532 return LDB_ERR_OPERATIONS_ERROR;
1535 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1537 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1538 return LDB_SUCCESS;
1542 * if the attribute's value haven't changed, and this isn't
1543 * just a delete of everything then return LDB_SUCCESS Unless
1544 * we have the provision control or if the attribute is
1545 * interSiteTopologyGenerator as this page explain:
1546 * http://support.microsoft.com/kb/224815 this attribute is
1547 * periodicaly written by the DC responsible for the intersite
1548 * generation in a given site
1550 * Unchanged could be deleting or replacing an already-gone
1551 * thing with an unconstrained delete/empty replace or a
1552 * replace with the same value, but not an add with the same
1553 * value because that could be about adding a duplicate (which
1554 * is for someone else to error out on).
1556 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1557 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1558 may_skip = true;
1560 } else if (old_el == NULL && el->num_values == 0) {
1561 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1562 may_skip = true;
1563 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1564 may_skip = true;
1566 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1567 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1569 * We intentionally skip the version bump when attempting to
1570 * vanish links.
1572 * The control is set by dbcheck and expunge-tombstones which
1573 * both attempt to be non-replicating. Otherwise, making an
1574 * alteration to the replication state would trigger a
1575 * broadcast of all expunged objects.
1577 may_skip = true;
1580 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1581 may_skip = false;
1582 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1585 if (may_skip) {
1586 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1587 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1589 * allow this to make it possible for dbcheck
1590 * to rebuild broken metadata
1592 return LDB_SUCCESS;
1596 for (i=0; i<omd->ctr.ctr1.count; i++) {
1598 * First check if we find it under the msDS-IntID,
1599 * then check if we find it under the OID and
1600 * prefixMap ID.
1602 * This allows the administrator to simply re-write
1603 * the attributes and so restore replication, which is
1604 * likely what they will try to do.
1606 if (attid == omd->ctr.ctr1.array[i].attid) {
1607 break;
1610 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1611 break;
1615 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1616 /* linked attributes are not stored in
1617 replPropertyMetaData in FL above w2k, but we do
1618 raise the seqnum for the object */
1619 if (*seq_num == 0 &&
1620 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1621 return LDB_ERR_OPERATIONS_ERROR;
1623 return LDB_SUCCESS;
1626 if (i == omd->ctr.ctr1.count) {
1627 /* we need to add a new one */
1628 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1629 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1630 if (omd->ctr.ctr1.array == NULL) {
1631 ldb_oom(ldb);
1632 return LDB_ERR_OPERATIONS_ERROR;
1634 omd->ctr.ctr1.count++;
1635 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1638 /* Get a new sequence number from the backend. We only do this
1639 * if we have a change that requires a new
1640 * replPropertyMetaData element
1642 if (*seq_num == 0) {
1643 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1644 if (ret != LDB_SUCCESS) {
1645 return LDB_ERR_OPERATIONS_ERROR;
1649 md1 = &omd->ctr.ctr1.array[i];
1650 md1->version++;
1651 md1->attid = attid;
1653 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1654 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1655 const char* rdn;
1657 if (rdn_val == NULL) {
1658 ldb_oom(ldb);
1659 return LDB_ERR_OPERATIONS_ERROR;
1662 rdn = (const char*)rdn_val->data;
1663 if (strcmp(rdn, "Deleted Objects") == 0) {
1665 * Set the originating_change_time to 29/12/9999 at 23:59:59
1666 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1668 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1669 } else {
1670 md1->originating_change_time = now;
1672 } else {
1673 md1->originating_change_time = now;
1675 md1->originating_invocation_id = *our_invocation_id;
1676 md1->originating_usn = *seq_num;
1677 md1->local_usn = *seq_num;
1679 if (is_forced_rodc) {
1680 /* Force version to 0 to be overridden later via replication */
1681 md1->version = 0;
1684 return LDB_SUCCESS;
1688 * Bump the replPropertyMetaData version on an attribute, and if it
1689 * has changed (or forced by leaving rdn_old NULL), update the value
1690 * in the entry.
1692 * This is important, as calling a modify operation may not change the
1693 * version number if the values appear unchanged, but a rename between
1694 * parents bumps this value.
1697 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1698 struct ldb_message *msg,
1699 const struct ldb_val *rdn_new,
1700 const struct ldb_val *rdn_old,
1701 struct replPropertyMetaDataBlob *omd,
1702 struct replmd_replicated_request *ar,
1703 NTTIME now,
1704 bool is_schema_nc,
1705 bool is_forced_rodc)
1707 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1708 const struct dsdb_attribute *rdn_attr =
1709 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1710 const char *attr_name = rdn_attr != NULL ?
1711 rdn_attr->lDAPDisplayName :
1712 rdn_name;
1713 struct ldb_message_element new_el = {
1714 .flags = LDB_FLAG_MOD_REPLACE,
1715 .name = attr_name,
1716 .num_values = 1,
1717 .values = discard_const_p(struct ldb_val, rdn_new)
1719 struct ldb_message_element old_el = {
1720 .flags = LDB_FLAG_MOD_REPLACE,
1721 .name = attr_name,
1722 .num_values = rdn_old ? 1 : 0,
1723 .values = discard_const_p(struct ldb_val, rdn_old)
1726 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1727 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1728 if (ret != LDB_SUCCESS) {
1729 return ldb_oom(ldb);
1733 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1734 omd, ar->schema, &ar->seq_num,
1735 &ar->our_invocation_id,
1736 now, is_schema_nc, is_forced_rodc,
1737 ar->req);
1741 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1743 uint32_t count = omd.ctr.ctr1.count;
1744 uint64_t max = 0;
1745 uint32_t i;
1746 for (i=0; i < count; i++) {
1747 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1748 if (max < m.local_usn) {
1749 max = m.local_usn;
1752 return max;
1756 * update the replPropertyMetaData object each time we modify an
1757 * object. This is needed for DRS replication, as the merge on the
1758 * client is based on this object
1760 static int replmd_update_rpmd(struct ldb_module *module,
1761 const struct dsdb_schema *schema,
1762 struct ldb_request *req,
1763 const char * const *rename_attrs,
1764 struct ldb_message *msg, uint64_t *seq_num,
1765 time_t t, bool is_schema_nc,
1766 bool *is_urgent, bool *rodc)
1768 const struct ldb_val *omd_value;
1769 enum ndr_err_code ndr_err;
1770 struct replPropertyMetaDataBlob omd;
1771 unsigned int i;
1772 NTTIME now;
1773 const struct GUID *our_invocation_id;
1774 int ret;
1775 const char * const *attrs = NULL;
1776 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1777 struct ldb_result *res;
1778 struct ldb_context *ldb;
1779 struct ldb_message_element *objectclass_el;
1780 enum urgent_situation situation;
1781 bool rmd_is_provided;
1782 bool rmd_is_just_resorted = false;
1783 const char *not_rename_attrs[4 + msg->num_elements];
1784 bool is_forced_rodc = false;
1786 if (rename_attrs) {
1787 attrs = rename_attrs;
1788 } else {
1789 for (i = 0; i < msg->num_elements; i++) {
1790 not_rename_attrs[i] = msg->elements[i].name;
1792 not_rename_attrs[i] = "replPropertyMetaData";
1793 not_rename_attrs[i+1] = "objectClass";
1794 not_rename_attrs[i+2] = "instanceType";
1795 not_rename_attrs[i+3] = NULL;
1796 attrs = not_rename_attrs;
1799 ldb = ldb_module_get_ctx(module);
1801 ret = samdb_rodc(ldb, rodc);
1802 if (ret != LDB_SUCCESS) {
1803 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1804 *rodc = false;
1807 if (*rodc &&
1808 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1809 is_forced_rodc = true;
1812 our_invocation_id = samdb_ntds_invocation_id(ldb);
1813 if (!our_invocation_id) {
1814 /* this happens during an initial vampire while
1815 updating the schema */
1816 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1817 return LDB_SUCCESS;
1820 unix_to_nt_time(&now, t);
1822 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1823 rmd_is_provided = true;
1824 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1825 rmd_is_just_resorted = true;
1827 } else {
1828 rmd_is_provided = false;
1831 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1832 * otherwise we consider we are updating */
1833 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1834 situation = REPL_URGENT_ON_DELETE;
1835 } else if (rename_attrs) {
1836 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1837 } else {
1838 situation = REPL_URGENT_ON_UPDATE;
1841 if (rmd_is_provided) {
1842 /* In this case the change_replmetadata control was supplied */
1843 /* We check that it's the only attribute that is provided
1844 * (it's a rare case so it's better to keep the code simplier)
1845 * We also check that the highest local_usn is bigger or the same as
1846 * uSNChanged. */
1847 uint64_t db_seq;
1848 if( msg->num_elements != 1 ||
1849 strncmp(msg->elements[0].name,
1850 "replPropertyMetaData", 20) ) {
1851 DEBUG(0,(__location__ ": changereplmetada control called without "\
1852 "a specified replPropertyMetaData attribute or with others\n"));
1853 return LDB_ERR_OPERATIONS_ERROR;
1855 if (situation != REPL_URGENT_ON_UPDATE) {
1856 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1857 return LDB_ERR_OPERATIONS_ERROR;
1859 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1860 if (!omd_value) {
1861 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1862 ldb_dn_get_linearized(msg->dn)));
1863 return LDB_ERR_OPERATIONS_ERROR;
1865 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1866 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1867 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1868 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1869 ldb_dn_get_linearized(msg->dn)));
1870 return LDB_ERR_OPERATIONS_ERROR;
1873 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1874 DSDB_FLAG_NEXT_MODULE |
1875 DSDB_SEARCH_SHOW_RECYCLED |
1876 DSDB_SEARCH_SHOW_EXTENDED_DN |
1877 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1878 DSDB_SEARCH_REVEAL_INTERNALS, req);
1880 if (ret != LDB_SUCCESS) {
1881 return ret;
1884 if (rmd_is_just_resorted == false) {
1885 *seq_num = find_max_local_usn(omd);
1887 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1890 * The test here now allows for a new
1891 * replPropertyMetaData with no change, if was
1892 * just dbcheck re-sorting the values.
1894 if (*seq_num <= db_seq) {
1895 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1896 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1897 (long long)*seq_num, (long long)db_seq));
1898 return LDB_ERR_OPERATIONS_ERROR;
1902 } else {
1903 /* search for the existing replPropertyMetaDataBlob. We need
1904 * to use REVEAL and ask for DNs in storage format to support
1905 * the check for values being the same in
1906 * replmd_update_rpmd_element()
1908 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1909 DSDB_FLAG_NEXT_MODULE |
1910 DSDB_SEARCH_SHOW_RECYCLED |
1911 DSDB_SEARCH_SHOW_EXTENDED_DN |
1912 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1913 DSDB_SEARCH_REVEAL_INTERNALS, req);
1914 if (ret != LDB_SUCCESS) {
1915 return ret;
1918 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1919 if (!omd_value) {
1920 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1921 ldb_dn_get_linearized(msg->dn)));
1922 return LDB_ERR_OPERATIONS_ERROR;
1925 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1926 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1927 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1928 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1929 ldb_dn_get_linearized(msg->dn)));
1930 return LDB_ERR_OPERATIONS_ERROR;
1933 if (omd.version != 1) {
1934 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1935 omd.version, ldb_dn_get_linearized(msg->dn)));
1936 return LDB_ERR_OPERATIONS_ERROR;
1939 for (i=0; i<msg->num_elements;) {
1940 struct ldb_message_element *el = &msg->elements[i];
1941 struct ldb_message_element *old_el;
1943 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1944 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1945 &omd, schema, seq_num,
1946 our_invocation_id,
1947 now, is_schema_nc,
1948 is_forced_rodc,
1949 req);
1950 if (ret != LDB_SUCCESS) {
1951 return ret;
1954 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1955 *is_urgent = replmd_check_urgent_attribute(el);
1958 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1959 i++;
1960 continue;
1963 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1965 if (el->num_values != 0) {
1966 i++;
1967 continue;
1970 ldb_msg_remove_element(msg, el);
1975 * Assert that we have an objectClass attribute - this is major
1976 * corruption if we don't have this!
1978 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1979 if (objectclass_el != NULL) {
1981 * Now check if this objectClass means we need to do urgent replication
1983 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1984 situation)) {
1985 *is_urgent = true;
1987 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1988 ldb_asprintf_errstring(ldb, __location__
1989 ": objectClass missing on %s\n",
1990 ldb_dn_get_linearized(msg->dn));
1991 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1995 * replmd_update_rpmd_element has done an update if the
1996 * seq_num is set
1998 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1999 struct ldb_val *md_value;
2000 struct ldb_message_element *el;
2002 /*if we are RODC and this is a DRSR update then its ok*/
2003 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
2004 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
2005 && !is_forced_rodc) {
2006 unsigned instanceType;
2008 if (*rodc) {
2009 ldb_set_errstring(ldb, "RODC modify is forbidden!");
2010 return LDB_ERR_REFERRAL;
2013 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
2014 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
2015 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
2016 "cannot change replicated attribute on partial replica");
2020 md_value = talloc(msg, struct ldb_val);
2021 if (md_value == NULL) {
2022 ldb_oom(ldb);
2023 return LDB_ERR_OPERATIONS_ERROR;
2026 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2027 if (ret != LDB_SUCCESS) {
2028 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2029 return ret;
2032 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2033 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2034 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2035 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2036 ldb_dn_get_linearized(msg->dn)));
2037 return LDB_ERR_OPERATIONS_ERROR;
2040 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2041 if (ret != LDB_SUCCESS) {
2042 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2043 ldb_dn_get_linearized(msg->dn)));
2044 return ret;
2047 el->num_values = 1;
2048 el->values = md_value;
2051 return LDB_SUCCESS;
2054 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2056 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2057 if (ret == 0) {
2058 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2059 &pdn2->dsdb_dn->extra_part);
2061 return ret;
2065 get a series of message element values as an array of DNs and GUIDs
2066 the result is sorted by GUID
2068 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2069 struct ldb_message_element *el, struct parsed_dn **pdn,
2070 const char *ldap_oid, struct ldb_request *parent)
2072 unsigned int i;
2073 bool values_are_sorted = true;
2074 struct ldb_context *ldb = ldb_module_get_ctx(module);
2076 if (el == NULL) {
2077 *pdn = NULL;
2078 return LDB_SUCCESS;
2081 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2082 if (!*pdn) {
2083 ldb_module_oom(module);
2084 return LDB_ERR_OPERATIONS_ERROR;
2087 for (i=0; i<el->num_values; i++) {
2088 struct ldb_val *v = &el->values[i];
2089 NTSTATUS status;
2090 struct ldb_dn *dn;
2091 struct parsed_dn *p;
2093 p = &(*pdn)[i];
2095 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2096 if (p->dsdb_dn == NULL) {
2097 return LDB_ERR_INVALID_DN_SYNTAX;
2100 dn = p->dsdb_dn->dn;
2102 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2103 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2104 unlikely(GUID_all_zero(&p->guid))) {
2105 /* we got a DN without a GUID - go find the GUID */
2106 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2107 if (ret != LDB_SUCCESS) {
2108 char *dn_str = NULL;
2109 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2110 (dn), 1);
2111 ldb_asprintf_errstring(ldb,
2112 "Unable to find GUID for DN %s\n",
2113 dn_str);
2114 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2115 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2116 ldb_attr_cmp(el->name, "member") == 0) {
2117 return LDB_ERR_UNWILLING_TO_PERFORM;
2119 return ret;
2121 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2122 if (ret != LDB_SUCCESS) {
2123 return ret;
2125 } else if (!NT_STATUS_IS_OK(status)) {
2126 return LDB_ERR_OPERATIONS_ERROR;
2128 if (i > 0 && values_are_sorted) {
2129 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2130 if (cmp < 0) {
2131 values_are_sorted = false;
2134 /* keep a pointer to the original ldb_val */
2135 p->v = v;
2137 if (! values_are_sorted) {
2138 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2140 return LDB_SUCCESS;
2144 * Get a series of trusted message element values. The result is sorted by
2145 * GUID, even though the GUIDs might not be known. That works because we trust
2146 * the database to give us the elements like that if the
2147 * replmd_private->sorted_links flag is set.
2149 * We also ensure that the links are in the Functional Level 2003
2150 * linked attributes format.
2152 static int get_parsed_dns_trusted_fallback(struct ldb_module *module,
2153 struct replmd_private *replmd_private,
2154 TALLOC_CTX *mem_ctx,
2155 struct ldb_message_element *el,
2156 struct parsed_dn **pdn,
2157 const char *ldap_oid,
2158 struct ldb_request *parent)
2160 int ret;
2161 if (el == NULL) {
2162 *pdn = NULL;
2163 return LDB_SUCCESS;
2166 if (!replmd_private->sorted_links) {
2167 /* We need to sort the list. This is the slow old path we want
2168 to avoid.
2170 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2171 parent);
2172 if (ret != LDB_SUCCESS) {
2173 return ret;
2175 } else {
2176 ret = get_parsed_dns_trusted(mem_ctx, el, pdn);
2177 if (ret != LDB_SUCCESS) {
2178 ldb_module_oom(module);
2179 return LDB_ERR_OPERATIONS_ERROR;
2184 * This upgrades links to FL2003 style, and sorts the result
2185 * if that was needed.
2187 * TODO: Add a database feature that asserts we have no FL2000
2188 * style links to avoid this check or add a feature that
2189 * uses a similar check to find sorted/unsorted links
2190 * for an on-the-fly upgrade.
2193 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2194 *pdn, el->num_values,
2196 ldap_oid);
2197 if (ret != LDB_SUCCESS) {
2198 return ret;
2201 return LDB_SUCCESS;
2205 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2206 otherwise an error code. For compatibility the error code differs depending
2207 on whether or not the attribute is "member".
2209 As always, the parsed_dn list is assumed to be sorted.
2211 static int check_parsed_dn_duplicates(struct ldb_module *module,
2212 struct ldb_message_element *el,
2213 struct parsed_dn *pdn)
2215 unsigned int i;
2216 struct ldb_context *ldb = ldb_module_get_ctx(module);
2218 for (i = 1; i < el->num_values; i++) {
2219 struct parsed_dn *p = &pdn[i];
2220 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2221 ldb_asprintf_errstring(ldb,
2222 "Linked attribute %s has "
2223 "multiple identical values",
2224 el->name);
2225 if (ldb_attr_cmp(el->name, "member") == 0) {
2226 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2227 } else {
2228 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2232 return LDB_SUCCESS;
2236 build a new extended DN, including all meta data fields
2238 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2239 RMD_ADDTIME = originating_add_time
2240 RMD_INVOCID = originating_invocation_id
2241 RMD_CHANGETIME = originating_change_time
2242 RMD_ORIGINATING_USN = originating_usn
2243 RMD_LOCAL_USN = local_usn
2244 RMD_VERSION = version
2246 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2247 struct dsdb_dn *dsdb_dn,
2248 const struct GUID *invocation_id,
2249 uint64_t local_usn, NTTIME nttime)
2251 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2252 local_usn, local_usn, nttime,
2253 RMD_VERSION_INITIAL, false);
2256 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2257 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2258 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2259 bool deleted);
2262 check if any links need upgrading from w2k format
2264 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2265 struct parsed_dn *dns, uint32_t count,
2266 struct ldb_message_element *el,
2267 const char *ldap_oid)
2269 uint32_t i;
2270 const struct GUID *invocation_id = NULL;
2271 for (i=0; i<count; i++) {
2272 NTSTATUS status;
2273 uint32_t version;
2274 int ret;
2275 if (dns[i].dsdb_dn == NULL) {
2276 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2277 ldap_oid);
2278 if (ret != LDB_SUCCESS) {
2279 return LDB_ERR_INVALID_DN_SYNTAX;
2283 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2284 &version, "RMD_VERSION");
2285 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2287 * We optimistically assume they are all the same; if
2288 * the first one is fixed, they are all fixed.
2290 * If the first one was *not* fixed and we find a
2291 * later one that is, that is an occasion to shout
2292 * with DEBUG(0).
2294 if (i == 0) {
2295 return LDB_SUCCESS;
2297 DEBUG(0, ("Mixed w2k and fixed format "
2298 "linked attributes\n"));
2299 continue;
2302 if (invocation_id == NULL) {
2303 invocation_id = samdb_ntds_invocation_id(ldb);
2304 if (invocation_id == NULL) {
2305 return LDB_ERR_OPERATIONS_ERROR;
2310 /* it's an old one that needs upgrading */
2311 ret = replmd_update_la_val(el->values, dns[i].v,
2312 dns[i].dsdb_dn, dns[i].dsdb_dn,
2313 invocation_id, 1, 1, 0, false);
2314 if (ret != LDB_SUCCESS) {
2315 return ret;
2320 * This sort() is critical for the operation of
2321 * get_parsed_dns_trusted_fallback() because callers of this function
2322 * expect a sorted list, and FL2000 style links are not
2323 * sorted. In particular, as well as the upgrade case,
2324 * get_parsed_dns_trusted_fallback() is called from
2325 * replmd_delete_remove_link() even in FL2000 mode
2327 * We do not normally pay the cost of the qsort() due to the
2328 * early return in the RMD_VERSION found case.
2330 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2331 return LDB_SUCCESS;
2335 Sets the value for a linked attribute, including all meta data fields
2337 see replmd_build_la_val for value names
2339 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2340 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2341 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2342 uint32_t version, bool deleted)
2344 struct ldb_dn *dn = dsdb_dn->dn;
2345 const char *tstring, *usn_string, *flags_string;
2346 struct ldb_val tval;
2347 struct ldb_val iid;
2348 struct ldb_val usnv, local_usnv;
2349 struct ldb_val vers, flagsv;
2350 const struct ldb_val *old_addtime = NULL;
2351 NTSTATUS status;
2352 int ret;
2353 const char *dnstring;
2354 char *vstring;
2355 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2357 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2358 if (!tstring) {
2359 return LDB_ERR_OPERATIONS_ERROR;
2361 tval = data_blob_string_const(tstring);
2363 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2364 if (!usn_string) {
2365 return LDB_ERR_OPERATIONS_ERROR;
2367 usnv = data_blob_string_const(usn_string);
2369 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2370 if (!usn_string) {
2371 return LDB_ERR_OPERATIONS_ERROR;
2373 local_usnv = data_blob_string_const(usn_string);
2375 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2376 if (!NT_STATUS_IS_OK(status)) {
2377 return LDB_ERR_OPERATIONS_ERROR;
2380 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2381 if (!flags_string) {
2382 return LDB_ERR_OPERATIONS_ERROR;
2384 flagsv = data_blob_string_const(flags_string);
2386 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2387 if (ret != LDB_SUCCESS) return ret;
2389 /* get the ADDTIME from the original */
2390 if (old_dsdb_dn != NULL) {
2391 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2392 "RMD_ADDTIME");
2394 if (old_addtime == NULL) {
2395 old_addtime = &tval;
2397 if (dsdb_dn != old_dsdb_dn ||
2398 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2399 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2400 if (ret != LDB_SUCCESS) return ret;
2403 /* use our invocation id */
2404 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2405 if (ret != LDB_SUCCESS) return ret;
2407 /* changetime is the current time */
2408 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2409 if (ret != LDB_SUCCESS) return ret;
2411 /* update the USN */
2412 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2413 if (ret != LDB_SUCCESS) return ret;
2415 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2416 if (ret != LDB_SUCCESS) return ret;
2418 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2419 vers = data_blob_string_const(vstring);
2420 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2421 if (ret != LDB_SUCCESS) return ret;
2423 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2424 if (dnstring == NULL) {
2425 return LDB_ERR_OPERATIONS_ERROR;
2427 *v = data_blob_string_const(dnstring);
2429 return LDB_SUCCESS;
2433 * Updates the value for a linked attribute, including all meta data fields
2435 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2436 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2437 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2438 bool deleted)
2440 uint32_t old_version;
2441 uint32_t version = RMD_VERSION_INITIAL;
2442 NTSTATUS status;
2445 * We're updating the linked attribute locally, so increase the version
2446 * by 1 so that other DCs will see the change when it gets replicated out
2448 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2449 "RMD_VERSION");
2451 if (NT_STATUS_IS_OK(status)) {
2452 version = old_version + 1;
2455 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2456 usn, local_usn, nttime, version, deleted);
2460 handle adding a linked attribute
2462 static int replmd_modify_la_add(struct ldb_module *module,
2463 struct replmd_private *replmd_private,
2464 struct replmd_replicated_request *ac,
2465 struct ldb_message *msg,
2466 struct ldb_message_element *el,
2467 struct ldb_message_element *old_el,
2468 const struct dsdb_attribute *schema_attr,
2469 time_t t,
2470 struct ldb_dn *msg_dn,
2471 struct ldb_request *parent)
2473 unsigned int i, j;
2474 struct parsed_dn *dns, *old_dns;
2475 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2476 int ret;
2477 struct ldb_val *new_values = NULL;
2478 unsigned old_num_values = old_el ? old_el->num_values : 0;
2479 unsigned num_values = 0;
2480 unsigned max_num_values;
2481 struct ldb_context *ldb = ldb_module_get_ctx(module);
2482 NTTIME now;
2483 unix_to_nt_time(&now, t);
2485 /* get the DNs to be added, fully parsed.
2487 * We need full parsing because they came off the wire and we don't
2488 * trust them, besides which we need their details to know where to put
2489 * them.
2491 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2492 schema_attr->syntax->ldap_oid, parent);
2493 if (ret != LDB_SUCCESS) {
2494 talloc_free(tmp_ctx);
2495 return ret;
2498 /* get the existing DNs, lazily parsed */
2499 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2500 tmp_ctx, old_el, &old_dns,
2501 schema_attr->syntax->ldap_oid,
2502 parent);
2504 if (ret != LDB_SUCCESS) {
2505 talloc_free(tmp_ctx);
2506 return ret;
2509 max_num_values = old_num_values + el->num_values;
2510 if (max_num_values < old_num_values) {
2511 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2512 "old values: %u, new values: %u, sum: %u\n",
2513 old_num_values, el->num_values, max_num_values));
2514 talloc_free(tmp_ctx);
2515 return LDB_ERR_OPERATIONS_ERROR;
2518 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2520 if (new_values == NULL) {
2521 ldb_module_oom(module);
2522 talloc_free(tmp_ctx);
2523 return LDB_ERR_OPERATIONS_ERROR;
2527 * For each new value, find where it would go in the list. If there is
2528 * a matching GUID there, we update the existing value; otherwise we
2529 * put it in place.
2531 j = 0;
2532 for (i = 0; i < el->num_values; i++) {
2533 struct parsed_dn *exact;
2534 struct parsed_dn *next;
2535 unsigned offset;
2536 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2537 &dns[i].guid,
2538 dns[i].dsdb_dn->dn,
2539 dns[i].dsdb_dn->extra_part, 0,
2540 &exact, &next,
2541 schema_attr->syntax->ldap_oid,
2542 true);
2543 if (err != LDB_SUCCESS) {
2544 talloc_free(tmp_ctx);
2545 return err;
2548 if (ac->fix_link_sid) {
2549 char *fixed_dnstring = NULL;
2550 struct dom_sid tmp_sid = { 0, };
2551 DATA_BLOB sid_blob = data_blob_null;
2552 enum ndr_err_code ndr_err;
2553 NTSTATUS status;
2554 int num;
2556 if (exact == NULL) {
2557 talloc_free(tmp_ctx);
2558 return ldb_operr(ldb);
2561 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2562 talloc_free(tmp_ctx);
2563 return ldb_operr(ldb);
2567 * Only "<GUID=...><SID=...>" is allowed.
2569 * We get the GUID to just to find the old
2570 * value and the SID in order to add it
2571 * to the found value.
2574 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2575 if (num != 0) {
2576 talloc_free(tmp_ctx);
2577 return ldb_operr(ldb);
2580 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2581 if (num != 2) {
2582 talloc_free(tmp_ctx);
2583 return ldb_operr(ldb);
2586 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2587 &tmp_sid, "SID");
2588 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2589 /* this is what we expect */
2590 } else if (NT_STATUS_IS_OK(status)) {
2591 struct GUID_txt_buf guid_str;
2592 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2593 "i[%u] SID NOT MISSING... Attribute %s already "
2594 "exists for target GUID %s, SID %s, DN: %s",
2595 i, el->name,
2596 GUID_buf_string(&exact->guid,
2597 &guid_str),
2598 dom_sid_string(tmp_ctx, &tmp_sid),
2599 dsdb_dn_get_extended_linearized(tmp_ctx,
2600 exact->dsdb_dn, 1));
2601 talloc_free(tmp_ctx);
2602 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2603 } else {
2604 talloc_free(tmp_ctx);
2605 return ldb_operr(ldb);
2608 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2609 &tmp_sid, "SID");
2610 if (!NT_STATUS_IS_OK(status)) {
2611 struct GUID_txt_buf guid_str;
2612 ldb_asprintf_errstring(ldb,
2613 "NO SID PROVIDED... Attribute %s already "
2614 "exists for target GUID %s",
2615 el->name,
2616 GUID_buf_string(&exact->guid,
2617 &guid_str));
2618 talloc_free(tmp_ctx);
2619 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2622 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2623 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2624 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2625 talloc_free(tmp_ctx);
2626 return ldb_operr(ldb);
2629 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2630 data_blob_free(&sid_blob);
2631 if (ret != LDB_SUCCESS) {
2632 talloc_free(tmp_ctx);
2633 return ret;
2636 fixed_dnstring = dsdb_dn_get_extended_linearized(
2637 new_values, exact->dsdb_dn, 1);
2638 if (fixed_dnstring == NULL) {
2639 talloc_free(tmp_ctx);
2640 return ldb_operr(ldb);
2644 * We just replace the existing value...
2646 *exact->v = data_blob_string_const(fixed_dnstring);
2648 continue;
2651 if (exact != NULL) {
2653 * We are trying to add one that exists, which is only
2654 * allowed if it was previously deleted.
2656 * When we do undelete a link we change it in place.
2657 * It will be copied across into the right spot in due
2658 * course.
2660 uint32_t rmd_flags;
2661 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2663 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2664 struct GUID_txt_buf guid_str;
2665 ldb_asprintf_errstring(ldb,
2666 "Attribute %s already "
2667 "exists for target GUID %s",
2668 el->name,
2669 GUID_buf_string(&exact->guid,
2670 &guid_str));
2671 talloc_free(tmp_ctx);
2672 /* error codes for 'member' need to be
2673 special cased */
2674 if (ldb_attr_cmp(el->name, "member") == 0) {
2675 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2676 } else {
2677 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2681 ret = replmd_update_la_val(new_values, exact->v,
2682 dns[i].dsdb_dn,
2683 exact->dsdb_dn,
2684 &ac->our_invocation_id,
2685 ac->seq_num, ac->seq_num,
2686 now, false);
2687 if (ret != LDB_SUCCESS) {
2688 talloc_free(tmp_ctx);
2689 return ret;
2692 ret = replmd_add_backlink(module, replmd_private,
2693 ac->schema,
2694 msg_dn,
2695 &dns[i].guid,
2696 true,
2697 schema_attr,
2698 parent);
2699 if (ret != LDB_SUCCESS) {
2700 talloc_free(tmp_ctx);
2701 return ret;
2703 continue;
2706 * Here we don't have an exact match.
2708 * If next is NULL, this one goes beyond the end of the
2709 * existing list, so we need to add all of those ones first.
2711 * If next is not NULL, we need to add all the ones before
2712 * next.
2714 if (next == NULL) {
2715 offset = old_num_values;
2716 } else {
2717 /* next should have been parsed, but let's make sure */
2718 if (next->dsdb_dn == NULL) {
2719 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2720 schema_attr->syntax->ldap_oid);
2721 if (ret != LDB_SUCCESS) {
2722 talloc_free(tmp_ctx);
2723 return ret;
2726 offset = MIN(next - old_dns, old_num_values);
2729 /* put all the old ones before next on the list */
2730 for (; j < offset; j++) {
2731 new_values[num_values] = *old_dns[j].v;
2732 num_values++;
2735 ret = replmd_add_backlink(module, replmd_private,
2736 ac->schema, msg_dn,
2737 &dns[i].guid,
2738 true, schema_attr,
2739 parent);
2740 if (ret != LDB_SUCCESS) {
2741 talloc_free(tmp_ctx);
2742 return ret;
2744 /* Make the new linked attribute ldb_val. */
2745 ret = replmd_build_la_val(new_values, &new_values[num_values],
2746 dns[i].dsdb_dn, &ac->our_invocation_id,
2747 ac->seq_num, now);
2748 if (ret != LDB_SUCCESS) {
2749 talloc_free(tmp_ctx);
2750 return ret;
2752 num_values++;
2753 if (ret != LDB_SUCCESS) {
2754 talloc_free(tmp_ctx);
2755 return ret;
2758 /* copy the rest of the old ones (if any) */
2759 for (; j < old_num_values; j++) {
2760 new_values[num_values] = *old_dns[j].v;
2761 num_values++;
2764 talloc_steal(msg->elements, new_values);
2765 if (old_el != NULL) {
2766 talloc_steal(msg->elements, old_el->values);
2768 el->values = new_values;
2769 el->num_values = num_values;
2771 talloc_free(tmp_ctx);
2773 /* we now tell the backend to replace all existing values
2774 with the one we have constructed */
2775 el->flags = LDB_FLAG_MOD_REPLACE;
2777 return LDB_SUCCESS;
2782 handle deleting all active linked attributes
2784 static int replmd_modify_la_delete(struct ldb_module *module,
2785 struct replmd_private *replmd_private,
2786 struct replmd_replicated_request *ac,
2787 struct ldb_message *msg,
2788 struct ldb_message_element *el,
2789 struct ldb_message_element *old_el,
2790 const struct dsdb_attribute *schema_attr,
2791 time_t t,
2792 struct ldb_dn *msg_dn,
2793 struct ldb_request *parent)
2795 unsigned int i;
2796 struct parsed_dn *dns, *old_dns;
2797 TALLOC_CTX *tmp_ctx = NULL;
2798 int ret;
2799 struct ldb_context *ldb = ldb_module_get_ctx(module);
2800 struct ldb_control *vanish_links_ctrl = NULL;
2801 bool vanish_links = false;
2802 unsigned int num_to_delete = el->num_values;
2803 uint32_t rmd_flags;
2804 NTTIME now;
2806 unix_to_nt_time(&now, t);
2808 if (old_el == NULL || old_el->num_values == 0) {
2809 /* there is nothing to delete... */
2810 if (num_to_delete == 0) {
2811 /* and we're deleting nothing, so that's OK */
2812 return LDB_SUCCESS;
2814 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2817 tmp_ctx = talloc_new(msg);
2818 if (tmp_ctx == NULL) {
2819 return LDB_ERR_OPERATIONS_ERROR;
2822 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2823 schema_attr->syntax->ldap_oid, parent);
2824 if (ret != LDB_SUCCESS) {
2825 talloc_free(tmp_ctx);
2826 return ret;
2829 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2830 tmp_ctx, old_el, &old_dns,
2831 schema_attr->syntax->ldap_oid,
2832 parent);
2834 if (ret != LDB_SUCCESS) {
2835 talloc_free(tmp_ctx);
2836 return ret;
2839 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2840 if (vanish_links_ctrl) {
2841 vanish_links = true;
2842 vanish_links_ctrl->critical = false;
2845 /* we empty out el->values here to avoid damage if we return early. */
2846 el->num_values = 0;
2847 el->values = NULL;
2850 * If vanish links is set, we are actually removing members of
2851 * old_el->values; otherwise we are just marking them deleted.
2853 * There is a special case when no values are given: we remove them
2854 * all. When we have the vanish_links control we just have to remove
2855 * the backlinks and change our element to replace the existing values
2856 * with the empty list.
2859 if (num_to_delete == 0) {
2860 for (i = 0; i < old_el->num_values; i++) {
2861 struct parsed_dn *p = &old_dns[i];
2862 if (p->dsdb_dn == NULL) {
2863 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2864 schema_attr->syntax->ldap_oid);
2865 if (ret != LDB_SUCCESS) {
2866 return ret;
2869 ret = replmd_add_backlink(module, replmd_private,
2870 ac->schema, msg_dn, &p->guid,
2871 false, schema_attr,
2872 parent);
2873 if (ret != LDB_SUCCESS) {
2874 talloc_free(tmp_ctx);
2875 return ret;
2877 if (vanish_links) {
2878 continue;
2881 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2882 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2883 continue;
2886 ret = replmd_update_la_val(old_el->values, p->v,
2887 p->dsdb_dn, p->dsdb_dn,
2888 &ac->our_invocation_id,
2889 ac->seq_num, ac->seq_num,
2890 now, true);
2891 if (ret != LDB_SUCCESS) {
2892 talloc_free(tmp_ctx);
2893 return ret;
2897 if (vanish_links) {
2898 el->flags = LDB_FLAG_MOD_REPLACE;
2899 talloc_free(tmp_ctx);
2900 return LDB_SUCCESS;
2905 for (i = 0; i < num_to_delete; i++) {
2906 struct parsed_dn *p = &dns[i];
2907 struct parsed_dn *exact = NULL;
2908 struct parsed_dn *next = NULL;
2909 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2910 &p->guid,
2911 NULL,
2912 p->dsdb_dn->extra_part, 0,
2913 &exact, &next,
2914 schema_attr->syntax->ldap_oid,
2915 true);
2916 if (ret != LDB_SUCCESS) {
2917 talloc_free(tmp_ctx);
2918 return ret;
2920 if (exact == NULL) {
2921 struct GUID_txt_buf buf;
2922 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2923 "exist for target GUID %s",
2924 el->name,
2925 GUID_buf_string(&p->guid, &buf));
2926 if (ldb_attr_cmp(el->name, "member") == 0) {
2927 talloc_free(tmp_ctx);
2928 return LDB_ERR_UNWILLING_TO_PERFORM;
2929 } else {
2930 talloc_free(tmp_ctx);
2931 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2935 if (vanish_links) {
2936 if (CHECK_DEBUGLVL(5)) {
2937 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2938 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2939 struct GUID_txt_buf buf;
2940 const char *guid_str = \
2941 GUID_buf_string(&p->guid, &buf);
2942 DEBUG(5, ("Deleting deleted linked "
2943 "attribute %s to %s, because "
2944 "vanish_links control is set\n",
2945 el->name, guid_str));
2949 /* remove the backlink */
2950 ret = replmd_add_backlink(module,
2951 replmd_private,
2952 ac->schema,
2953 msg_dn,
2954 &p->guid,
2955 false, schema_attr,
2956 parent);
2957 if (ret != LDB_SUCCESS) {
2958 talloc_free(tmp_ctx);
2959 return ret;
2962 /* We flag the deletion and tidy it up later. */
2963 exact->v = NULL;
2964 continue;
2967 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2969 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2970 struct GUID_txt_buf buf;
2971 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2972 ldb_asprintf_errstring(ldb, "Attribute %s already "
2973 "deleted for target GUID %s",
2974 el->name, guid_str);
2975 if (ldb_attr_cmp(el->name, "member") == 0) {
2976 talloc_free(tmp_ctx);
2977 return LDB_ERR_UNWILLING_TO_PERFORM;
2978 } else {
2979 talloc_free(tmp_ctx);
2980 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2984 ret = replmd_update_la_val(old_el->values, exact->v,
2985 exact->dsdb_dn, exact->dsdb_dn,
2986 &ac->our_invocation_id,
2987 ac->seq_num, ac->seq_num,
2988 now, true);
2989 if (ret != LDB_SUCCESS) {
2990 talloc_free(tmp_ctx);
2991 return ret;
2993 ret = replmd_add_backlink(module, replmd_private,
2994 ac->schema, msg_dn,
2995 &p->guid,
2996 false, schema_attr,
2997 parent);
2998 if (ret != LDB_SUCCESS) {
2999 talloc_free(tmp_ctx);
3000 return ret;
3004 if (vanish_links) {
3005 unsigned j = 0;
3006 struct ldb_val *tmp_vals = NULL;
3008 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
3009 old_el->num_values);
3010 if (tmp_vals == NULL) {
3011 talloc_free(tmp_ctx);
3012 return ldb_module_oom(module);
3014 for (i = 0; i < old_el->num_values; i++) {
3015 if (old_dns[i].v == NULL) {
3016 continue;
3018 tmp_vals[j] = *old_dns[i].v;
3019 j++;
3021 for (i = 0; i < j; i++) {
3022 old_el->values[i] = tmp_vals[i];
3024 old_el->num_values = j;
3027 el->values = talloc_steal(msg->elements, old_el->values);
3028 el->num_values = old_el->num_values;
3030 talloc_free(tmp_ctx);
3032 /* we now tell the backend to replace all existing values
3033 with the one we have constructed */
3034 el->flags = LDB_FLAG_MOD_REPLACE;
3036 return LDB_SUCCESS;
3040 handle replacing a linked attribute
3042 static int replmd_modify_la_replace(struct ldb_module *module,
3043 struct replmd_private *replmd_private,
3044 struct replmd_replicated_request *ac,
3045 struct ldb_message *msg,
3046 struct ldb_message_element *el,
3047 struct ldb_message_element *old_el,
3048 const struct dsdb_attribute *schema_attr,
3049 time_t t,
3050 struct ldb_dn *msg_dn,
3051 struct ldb_request *parent)
3053 unsigned int i, old_i, new_i;
3054 struct parsed_dn *dns, *old_dns;
3055 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3056 int ret;
3057 struct ldb_context *ldb = ldb_module_get_ctx(module);
3058 struct ldb_val *new_values = NULL;
3059 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3060 unsigned int old_num_values;
3061 unsigned int repl_num_values;
3062 unsigned int max_num_values;
3063 NTTIME now;
3065 unix_to_nt_time(&now, t);
3068 * The replace operation is unlike the replace and delete cases in that
3069 * we need to look at every existing link to see whether it is being
3070 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3072 * As we are trying to combine two sorted lists, the algorithm we use
3073 * is akin to the merge phase of a merge sort. We interleave the two
3074 * lists, doing different things depending on which side the current
3075 * item came from.
3077 * There are three main cases, with some sub-cases.
3079 * - a DN is in the old list but not the new one. It needs to be
3080 * marked as deleted (but left in the list).
3081 * - maybe it is already deleted, and we have less to do.
3083 * - a DN is in both lists. The old data gets replaced by the new,
3084 * and the list doesn't grow. The old link may have been marked as
3085 * deleted, in which case we undelete it.
3087 * - a DN is in the new list only. We add it in the right place.
3090 old_num_values = old_el ? old_el->num_values : 0;
3091 repl_num_values = el->num_values;
3092 max_num_values = old_num_values + repl_num_values;
3094 if (max_num_values == 0) {
3095 /* There is nothing to do! */
3096 return LDB_SUCCESS;
3100 * At the successful end of these functions el->values is
3101 * overwritten with new_values. However get_parsed_dns()
3102 * points p->v at the supplied el and it effectively gets used
3103 * as a working area by replmd_build_la_val(). So we must
3104 * duplicate it because our caller only called
3105 * ldb_msg_copy_shallow().
3108 el->values = talloc_memdup(tmp_ctx,
3109 el->values,
3110 sizeof(el->values[0]) * el->num_values);
3111 if (el->values == NULL) {
3112 ldb_module_oom(module);
3113 talloc_free(tmp_ctx);
3114 return LDB_ERR_OPERATIONS_ERROR;
3117 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3118 if (ret != LDB_SUCCESS) {
3119 talloc_free(tmp_ctx);
3120 return ret;
3123 ret = check_parsed_dn_duplicates(module, el, dns);
3124 if (ret != LDB_SUCCESS) {
3125 talloc_free(tmp_ctx);
3126 return ret;
3129 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3130 ldap_oid, parent);
3131 if (ret != LDB_SUCCESS) {
3132 talloc_free(tmp_ctx);
3133 return ret;
3136 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3137 old_el, ldap_oid);
3138 if (ret != LDB_SUCCESS) {
3139 talloc_free(tmp_ctx);
3140 return ret;
3143 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3144 if (new_values == NULL) {
3145 ldb_module_oom(module);
3146 talloc_free(tmp_ctx);
3147 return LDB_ERR_OPERATIONS_ERROR;
3150 old_i = 0;
3151 new_i = 0;
3152 for (i = 0; i < max_num_values; i++) {
3153 int cmp;
3154 struct parsed_dn *old_p, *new_p;
3155 if (old_i < old_num_values && new_i < repl_num_values) {
3156 old_p = &old_dns[old_i];
3157 new_p = &dns[new_i];
3158 cmp = parsed_dn_compare(old_p, new_p);
3159 } else if (old_i < old_num_values) {
3160 /* the new list is empty, read the old list */
3161 old_p = &old_dns[old_i];
3162 new_p = NULL;
3163 cmp = -1;
3164 } else if (new_i < repl_num_values) {
3165 /* the old list is empty, read new list */
3166 old_p = NULL;
3167 new_p = &dns[new_i];
3168 cmp = 1;
3169 } else {
3170 break;
3173 if (cmp < 0) {
3175 * An old ones that come before the next replacement
3176 * (if any). We mark it as deleted and add it to the
3177 * final list.
3179 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3180 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3181 ret = replmd_update_la_val(new_values, old_p->v,
3182 old_p->dsdb_dn,
3183 old_p->dsdb_dn,
3184 &ac->our_invocation_id,
3185 ac->seq_num, ac->seq_num,
3186 now, true);
3187 if (ret != LDB_SUCCESS) {
3188 talloc_free(tmp_ctx);
3189 return ret;
3192 ret = replmd_add_backlink(module, replmd_private,
3193 ac->schema,
3194 msg_dn,
3195 &old_p->guid, false,
3196 schema_attr,
3197 parent);
3198 if (ret != LDB_SUCCESS) {
3199 talloc_free(tmp_ctx);
3200 return ret;
3203 new_values[i] = *old_p->v;
3204 old_i++;
3205 } else if (cmp == 0) {
3207 * We are overwriting one. If it was previously
3208 * deleted, we need to add a backlink.
3210 * Note that if any RMD_FLAGs in an extended new DN
3211 * will be ignored.
3213 uint32_t rmd_flags;
3215 ret = replmd_update_la_val(new_values, old_p->v,
3216 new_p->dsdb_dn,
3217 old_p->dsdb_dn,
3218 &ac->our_invocation_id,
3219 ac->seq_num, ac->seq_num,
3220 now, false);
3221 if (ret != LDB_SUCCESS) {
3222 talloc_free(tmp_ctx);
3223 return ret;
3226 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3227 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3228 ret = replmd_add_backlink(module, replmd_private,
3229 ac->schema,
3230 msg_dn,
3231 &new_p->guid, true,
3232 schema_attr,
3233 parent);
3234 if (ret != LDB_SUCCESS) {
3235 talloc_free(tmp_ctx);
3236 return ret;
3240 new_values[i] = *old_p->v;
3241 old_i++;
3242 new_i++;
3243 } else {
3245 * Replacements that don't match an existing one. We
3246 * just add them to the final list.
3248 ret = replmd_build_la_val(new_values,
3249 new_p->v,
3250 new_p->dsdb_dn,
3251 &ac->our_invocation_id,
3252 ac->seq_num, now);
3253 if (ret != LDB_SUCCESS) {
3254 talloc_free(tmp_ctx);
3255 return ret;
3257 ret = replmd_add_backlink(module, replmd_private,
3258 ac->schema,
3259 msg_dn,
3260 &new_p->guid, true,
3261 schema_attr,
3262 parent);
3263 if (ret != LDB_SUCCESS) {
3264 talloc_free(tmp_ctx);
3265 return ret;
3267 new_values[i] = *new_p->v;
3268 new_i++;
3271 if (old_el != NULL) {
3272 talloc_steal(msg->elements, old_el->values);
3274 el->values = talloc_steal(msg->elements, new_values);
3275 el->num_values = i;
3276 talloc_free(tmp_ctx);
3278 el->flags = LDB_FLAG_MOD_REPLACE;
3280 return LDB_SUCCESS;
3285 handle linked attributes in modify requests
3287 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3288 struct replmd_private *replmd_private,
3289 struct replmd_replicated_request *ac,
3290 struct ldb_message *msg,
3291 time_t t,
3292 struct ldb_request *parent)
3294 struct ldb_result *res;
3295 unsigned int i;
3296 int ret;
3297 struct ldb_context *ldb = ldb_module_get_ctx(module);
3298 struct ldb_message *old_msg;
3300 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3302 * Nothing special is required for modifying or vanishing links
3303 * in fl2000 since they are just strings in a multi-valued
3304 * attribute.
3306 struct ldb_control *ctrl = ldb_request_get_control(parent,
3307 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3308 if (ctrl) {
3309 ctrl->critical = false;
3311 return LDB_SUCCESS;
3315 * TODO:
3317 * We should restrict this to the intersection of the list of
3318 * linked attributes in the schema and the list of attributes
3319 * being modified.
3321 * This will help performance a little, as otherwise we have
3322 * to allocate the entire object value-by-value.
3324 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3325 DSDB_FLAG_NEXT_MODULE |
3326 DSDB_SEARCH_SHOW_RECYCLED |
3327 DSDB_SEARCH_REVEAL_INTERNALS |
3328 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3329 parent);
3330 if (ret != LDB_SUCCESS) {
3331 return ret;
3334 old_msg = res->msgs[0];
3336 for (i=0; i<msg->num_elements; i++) {
3337 struct ldb_message_element *el = &msg->elements[i];
3338 struct ldb_message_element *old_el, *new_el;
3339 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3340 const struct dsdb_attribute *schema_attr
3341 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3342 if (!schema_attr) {
3343 ldb_asprintf_errstring(ldb,
3344 "%s: attribute %s is not a valid attribute in schema",
3345 __FUNCTION__, el->name);
3346 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3348 if (schema_attr->linkID == 0) {
3349 continue;
3351 if ((schema_attr->linkID & 1) == 1) {
3352 struct ldb_control *ctrl;
3354 ctrl = ldb_request_get_control(parent,
3355 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3356 if (ctrl != NULL) {
3357 ctrl->critical = false;
3358 continue;
3360 ctrl = ldb_request_get_control(parent,
3361 DSDB_CONTROL_DBCHECK);
3362 if (ctrl != NULL) {
3363 continue;
3366 /* Odd is for the target. Illegal to modify */
3367 ldb_asprintf_errstring(ldb,
3368 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3369 return LDB_ERR_UNWILLING_TO_PERFORM;
3371 old_el = ldb_msg_find_element(old_msg, el->name);
3372 switch (mod_type) {
3373 case LDB_FLAG_MOD_REPLACE:
3374 ret = replmd_modify_la_replace(module, replmd_private,
3375 ac, msg, el, old_el,
3376 schema_attr, t,
3377 old_msg->dn,
3378 parent);
3379 break;
3380 case LDB_FLAG_MOD_DELETE:
3381 ret = replmd_modify_la_delete(module, replmd_private,
3382 ac, msg, el, old_el,
3383 schema_attr, t,
3384 old_msg->dn,
3385 parent);
3386 break;
3387 case LDB_FLAG_MOD_ADD:
3388 ret = replmd_modify_la_add(module, replmd_private,
3389 ac, msg, el, old_el,
3390 schema_attr, t,
3391 old_msg->dn,
3392 parent);
3393 break;
3394 default:
3395 ldb_asprintf_errstring(ldb,
3396 "invalid flags 0x%x for %s linked attribute",
3397 el->flags, el->name);
3398 return LDB_ERR_UNWILLING_TO_PERFORM;
3400 if (ret != LDB_SUCCESS) {
3401 return ret;
3403 ret = dsdb_check_single_valued_link(schema_attr, el);
3404 if (ret != LDB_SUCCESS) {
3405 ldb_asprintf_errstring(ldb,
3406 "Attribute %s is single valued but more than one value has been supplied",
3407 el->name);
3408 /* Return codes as found on Windows 2012r2 */
3409 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3410 return LDB_ERR_CONSTRAINT_VIOLATION;
3411 } else {
3412 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3414 } else {
3415 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3418 if (old_el) {
3419 ldb_msg_remove_attr(old_msg, el->name);
3421 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3422 new_el->num_values = el->num_values;
3423 new_el->values = talloc_steal(msg->elements, el->values);
3425 /* TODO: this relises a bit too heavily on the exact
3426 behaviour of ldb_msg_find_element and
3427 ldb_msg_remove_element */
3428 old_el = ldb_msg_find_element(msg, el->name);
3429 if (old_el != el) {
3430 ldb_msg_remove_element(msg, old_el);
3431 i--;
3435 talloc_free(res);
3436 return ret;
3440 static int send_rodc_referral(struct ldb_request *req,
3441 struct ldb_context *ldb,
3442 struct ldb_dn *dn)
3444 char *referral = NULL;
3445 struct loadparm_context *lp_ctx = NULL;
3446 struct ldb_dn *fsmo_role_dn = NULL;
3447 struct ldb_dn *role_owner_dn = NULL;
3448 const char *domain = NULL;
3449 WERROR werr;
3451 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3452 struct loadparm_context);
3454 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3455 &fsmo_role_dn, &role_owner_dn);
3457 if (W_ERROR_IS_OK(werr)) {
3458 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3459 if (server_dn != NULL) {
3460 ldb_dn_remove_child_components(server_dn, 1);
3461 domain = samdb_dn_to_dnshostname(ldb, req,
3462 server_dn);
3466 if (domain == NULL) {
3467 domain = lpcfg_dnsdomain(lp_ctx);
3470 referral = talloc_asprintf(req, "ldap://%s/%s",
3471 domain,
3472 ldb_dn_get_linearized(dn));
3473 if (referral == NULL) {
3474 ldb_oom(ldb);
3475 return LDB_ERR_OPERATIONS_ERROR;
3478 return ldb_module_send_referral(req, referral);
3482 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3484 struct ldb_context *ldb;
3485 struct replmd_replicated_request *ac;
3486 struct ldb_request *down_req;
3487 struct ldb_message *msg;
3488 time_t t = time(NULL);
3489 int ret;
3490 bool is_urgent = false, rodc = false;
3491 bool is_schema_nc = false;
3492 unsigned int functional_level;
3493 const struct ldb_message_element *guid_el = NULL;
3494 struct ldb_control *sd_propagation_control;
3495 struct ldb_control *fix_links_control = NULL;
3496 struct ldb_control *fix_dn_name_control = NULL;
3497 struct ldb_control *fix_dn_sid_control = NULL;
3498 struct replmd_private *replmd_private =
3499 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3501 /* do not manipulate our control entries */
3502 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3503 return ldb_next_request(module, req);
3506 sd_propagation_control = ldb_request_get_control(req,
3507 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3508 if (sd_propagation_control != NULL) {
3509 if (req->op.mod.message->num_elements != 1) {
3510 return ldb_module_operr(module);
3512 ret = strcmp(req->op.mod.message->elements[0].name,
3513 "nTSecurityDescriptor");
3514 if (ret != 0) {
3515 return ldb_module_operr(module);
3518 return ldb_next_request(module, req);
3521 ldb = ldb_module_get_ctx(module);
3523 fix_links_control = ldb_request_get_control(req,
3524 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3525 if (fix_links_control != NULL) {
3526 struct dsdb_schema *schema = NULL;
3527 const struct dsdb_attribute *sa = NULL;
3529 if (req->op.mod.message->num_elements != 1) {
3530 return ldb_module_operr(module);
3533 if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_REPLACE) {
3534 return ldb_module_operr(module);
3537 schema = dsdb_get_schema(ldb, req);
3538 if (schema == NULL) {
3539 return ldb_module_operr(module);
3542 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3543 req->op.mod.message->elements[0].name);
3544 if (sa == NULL) {
3545 return ldb_module_operr(module);
3548 if (sa->linkID == 0) {
3549 return ldb_module_operr(module);
3552 fix_links_control->critical = false;
3553 return ldb_next_request(module, req);
3556 fix_dn_name_control = ldb_request_get_control(req,
3557 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3558 if (fix_dn_name_control != NULL) {
3559 struct dsdb_schema *schema = NULL;
3560 const struct dsdb_attribute *sa = NULL;
3562 if (req->op.mod.message->num_elements != 2) {
3563 return ldb_module_operr(module);
3566 if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_DELETE) {
3567 return ldb_module_operr(module);
3570 if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[1].flags) != LDB_FLAG_MOD_ADD) {
3571 return ldb_module_operr(module);
3574 if (req->op.mod.message->elements[0].num_values != 1) {
3575 return ldb_module_operr(module);
3578 if (req->op.mod.message->elements[1].num_values != 1) {
3579 return ldb_module_operr(module);
3582 schema = dsdb_get_schema(ldb, req);
3583 if (schema == NULL) {
3584 return ldb_module_operr(module);
3587 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3588 req->op.mod.message->elements[1].name) != 0) {
3589 return ldb_module_operr(module);
3592 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3593 req->op.mod.message->elements[0].name);
3594 if (sa == NULL) {
3595 return ldb_module_operr(module);
3598 if (sa->dn_format == DSDB_INVALID_DN) {
3599 return ldb_module_operr(module);
3602 if (sa->linkID != 0) {
3603 return ldb_module_operr(module);
3607 * If we are run from dbcheck and we are not updating
3608 * a link (as these would need to be sorted and so
3609 * can't go via such a simple update, then do not
3610 * trigger replicated updates and a new USN from this
3611 * change, it wasn't a real change, just a new
3612 * (correct) string DN
3615 fix_dn_name_control->critical = false;
3616 return ldb_next_request(module, req);
3619 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3621 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3622 if (guid_el != NULL) {
3623 ldb_set_errstring(ldb,
3624 "replmd_modify: it's not allowed to change the objectGUID!");
3625 return LDB_ERR_CONSTRAINT_VIOLATION;
3628 ac = replmd_ctx_init(module, req);
3629 if (ac == NULL) {
3630 return ldb_module_oom(module);
3633 functional_level = dsdb_functional_level(ldb);
3635 /* we have to copy the message as the caller might have it as a const */
3636 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3637 if (msg == NULL) {
3638 ldb_oom(ldb);
3639 talloc_free(ac);
3640 return LDB_ERR_OPERATIONS_ERROR;
3643 fix_dn_sid_control = ldb_request_get_control(req,
3644 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3645 if (fix_dn_sid_control != NULL) {
3646 const struct dsdb_attribute *sa = NULL;
3648 if (msg->num_elements != 1) {
3649 talloc_free(ac);
3650 return ldb_module_operr(module);
3653 if (LDB_FLAG_MOD_TYPE(msg->elements[0].flags) != LDB_FLAG_MOD_ADD) {
3654 talloc_free(ac);
3655 return ldb_module_operr(module);
3658 if (msg->elements[0].num_values != 1) {
3659 talloc_free(ac);
3660 return ldb_module_operr(module);
3663 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3664 msg->elements[0].name);
3665 if (sa == NULL) {
3666 talloc_free(ac);
3667 return ldb_module_operr(module);
3670 if (sa->dn_format != DSDB_NORMAL_DN) {
3671 talloc_free(ac);
3672 return ldb_module_operr(module);
3675 fix_dn_sid_control->critical = false;
3676 ac->fix_link_sid = true;
3678 goto handle_linked_attribs;
3681 ldb_msg_remove_attr(msg, "whenChanged");
3682 ldb_msg_remove_attr(msg, "uSNChanged");
3684 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3686 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3687 msg, &ac->seq_num, t, is_schema_nc,
3688 &is_urgent, &rodc);
3689 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3690 ret = send_rodc_referral(req, ldb, msg->dn);
3691 talloc_free(ac);
3692 return ret;
3696 if (ret != LDB_SUCCESS) {
3697 talloc_free(ac);
3698 return ret;
3701 handle_linked_attribs:
3702 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3703 ac, msg, t, req);
3704 if (ret != LDB_SUCCESS) {
3705 talloc_free(ac);
3706 return ret;
3709 /* TODO:
3710 * - replace the old object with the newly constructed one
3713 ac->is_urgent = is_urgent;
3715 ret = ldb_build_mod_req(&down_req, ldb, ac,
3716 msg,
3717 req->controls,
3718 ac, replmd_op_callback,
3719 req);
3720 LDB_REQ_SET_LOCATION(down_req);
3721 if (ret != LDB_SUCCESS) {
3722 talloc_free(ac);
3723 return ret;
3726 /* current partition control is needed by "replmd_op_callback" */
3727 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3728 ret = ldb_request_add_control(down_req,
3729 DSDB_CONTROL_CURRENT_PARTITION_OID,
3730 false, NULL);
3731 if (ret != LDB_SUCCESS) {
3732 talloc_free(ac);
3733 return ret;
3737 /* If we are in functional level 2000, then
3738 * replmd_modify_handle_linked_attribs will have done
3739 * nothing */
3740 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3741 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3742 if (ret != LDB_SUCCESS) {
3743 talloc_free(ac);
3744 return ret;
3748 talloc_steal(down_req, msg);
3750 /* we only change whenChanged and uSNChanged if the seq_num
3751 has changed */
3752 if (ac->seq_num != 0) {
3753 ret = add_time_element(msg, "whenChanged", t);
3754 if (ret != LDB_SUCCESS) {
3755 talloc_free(ac);
3756 ldb_operr(ldb);
3757 return ret;
3760 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3761 if (ret != LDB_SUCCESS) {
3762 talloc_free(ac);
3763 ldb_operr(ldb);
3764 return ret;
3768 /* go on with the call chain */
3769 return ldb_next_request(module, down_req);
3772 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3775 handle a rename request
3777 On a rename we need to do an extra ldb_modify which sets the
3778 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3780 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3782 struct ldb_context *ldb;
3783 struct ldb_control *fix_dn_name_control = NULL;
3784 struct replmd_replicated_request *ac;
3785 int ret;
3786 struct ldb_request *down_req;
3788 /* do not manipulate our control entries */
3789 if (ldb_dn_is_special(req->op.rename.olddn)) {
3790 return ldb_next_request(module, req);
3793 fix_dn_name_control = ldb_request_get_control(req,
3794 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3795 if (fix_dn_name_control != NULL) {
3796 return ldb_next_request(module, req);
3799 ldb = ldb_module_get_ctx(module);
3801 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3803 ac = replmd_ctx_init(module, req);
3804 if (ac == NULL) {
3805 return ldb_module_oom(module);
3808 ret = ldb_build_rename_req(&down_req, ldb, ac,
3809 ac->req->op.rename.olddn,
3810 ac->req->op.rename.newdn,
3811 ac->req->controls,
3812 ac, replmd_rename_callback,
3813 ac->req);
3814 LDB_REQ_SET_LOCATION(down_req);
3815 if (ret != LDB_SUCCESS) {
3816 talloc_free(ac);
3817 return ret;
3820 /* go on with the call chain */
3821 return ldb_next_request(module, down_req);
3824 /* After the rename is compleated, update the whenchanged etc */
3825 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3827 struct ldb_context *ldb;
3828 struct ldb_request *down_req;
3829 struct ldb_message *msg;
3830 const struct dsdb_attribute *rdn_attr;
3831 const char *rdn_name;
3832 const struct ldb_val *rdn_val;
3833 const char *attrs[5] = { NULL, };
3834 time_t t = time(NULL);
3835 int ret;
3836 bool is_urgent = false, rodc = false;
3837 bool is_schema_nc;
3838 struct replmd_replicated_request *ac =
3839 talloc_get_type(req->context, struct replmd_replicated_request);
3840 struct replmd_private *replmd_private =
3841 talloc_get_type(ldb_module_get_private(ac->module),
3842 struct replmd_private);
3844 ldb = ldb_module_get_ctx(ac->module);
3846 if (ares->error != LDB_SUCCESS) {
3847 return ldb_module_done(ac->req, ares->controls,
3848 ares->response, ares->error);
3851 if (ares->type != LDB_REPLY_DONE) {
3852 ldb_set_errstring(ldb,
3853 "invalid reply type in repl_meta_data rename callback");
3854 talloc_free(ares);
3855 return ldb_module_done(ac->req, NULL, NULL,
3856 LDB_ERR_OPERATIONS_ERROR);
3859 /* TODO:
3860 * - replace the old object with the newly constructed one
3863 msg = ldb_msg_new(ac);
3864 if (msg == NULL) {
3865 ldb_oom(ldb);
3866 return LDB_ERR_OPERATIONS_ERROR;
3869 msg->dn = ac->req->op.rename.newdn;
3871 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3873 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3874 if (rdn_name == NULL) {
3875 talloc_free(ares);
3876 return ldb_module_done(ac->req, NULL, NULL,
3877 ldb_operr(ldb));
3880 /* normalize the rdn attribute name */
3881 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3882 if (rdn_attr == NULL) {
3883 talloc_free(ares);
3884 return ldb_module_done(ac->req, NULL, NULL,
3885 ldb_operr(ldb));
3887 rdn_name = rdn_attr->lDAPDisplayName;
3889 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3890 if (rdn_val == NULL) {
3891 talloc_free(ares);
3892 return ldb_module_done(ac->req, NULL, NULL,
3893 ldb_operr(ldb));
3896 if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
3897 talloc_free(ares);
3898 return ldb_module_done(ac->req, NULL, NULL,
3899 ldb_oom(ldb));
3901 if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
3902 talloc_free(ares);
3903 return ldb_module_done(ac->req, NULL, NULL,
3904 ldb_oom(ldb));
3908 * here we let replmd_update_rpmd() only search for
3909 * the existing "replPropertyMetaData" and rdn_name attributes.
3911 * We do not want the existing "name" attribute as
3912 * the "name" attribute needs to get the version
3913 * updated on rename even if the rdn value hasn't changed.
3915 * This is the diff of the meta data, for a moved user
3916 * on a w2k8r2 server:
3918 * # record 1
3919 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3920 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3921 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3922 * version : 0x00000001 (1)
3923 * reserved : 0x00000000 (0)
3924 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3925 * local_usn : 0x00000000000037a5 (14245)
3926 * array: struct replPropertyMetaData1
3927 * attid : DRSUAPI_ATTID_name (0x90001)
3928 * - version : 0x00000001 (1)
3929 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3930 * + version : 0x00000002 (2)
3931 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3932 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3933 * - originating_usn : 0x00000000000037a5 (14245)
3934 * - local_usn : 0x00000000000037a5 (14245)
3935 * + originating_usn : 0x0000000000003834 (14388)
3936 * + local_usn : 0x0000000000003834 (14388)
3937 * array: struct replPropertyMetaData1
3938 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3939 * version : 0x00000004 (4)
3941 attrs[0] = "replPropertyMetaData";
3942 attrs[1] = "objectClass";
3943 attrs[2] = "instanceType";
3944 attrs[3] = rdn_name;
3945 attrs[4] = NULL;
3947 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3948 msg, &ac->seq_num, t,
3949 is_schema_nc, &is_urgent, &rodc);
3950 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3951 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3952 talloc_free(ares);
3953 return ldb_module_done(req, NULL, NULL, ret);
3956 if (ret != LDB_SUCCESS) {
3957 talloc_free(ares);
3958 return ldb_module_done(ac->req, NULL, NULL, ret);
3961 if (ac->seq_num == 0) {
3962 talloc_free(ares);
3963 return ldb_module_done(ac->req, NULL, NULL,
3964 ldb_error(ldb, ret,
3965 "internal error seq_num == 0"));
3967 ac->is_urgent = is_urgent;
3969 ret = ldb_build_mod_req(&down_req, ldb, ac,
3970 msg,
3971 req->controls,
3972 ac, replmd_op_callback,
3973 req);
3974 LDB_REQ_SET_LOCATION(down_req);
3975 if (ret != LDB_SUCCESS) {
3976 talloc_free(ac);
3977 return ret;
3980 /* current partition control is needed by "replmd_op_callback" */
3981 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3982 ret = ldb_request_add_control(down_req,
3983 DSDB_CONTROL_CURRENT_PARTITION_OID,
3984 false, NULL);
3985 if (ret != LDB_SUCCESS) {
3986 talloc_free(ac);
3987 return ret;
3991 talloc_steal(down_req, msg);
3993 ret = add_time_element(msg, "whenChanged", t);
3994 if (ret != LDB_SUCCESS) {
3995 talloc_free(ac);
3996 ldb_operr(ldb);
3997 return ret;
4000 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
4001 if (ret != LDB_SUCCESS) {
4002 talloc_free(ac);
4003 ldb_operr(ldb);
4004 return ret;
4007 /* go on with the call chain - do the modify after the rename */
4008 return ldb_next_request(ac->module, down_req);
4012 * remove links from objects that point at this object when an object
4013 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
4014 * RemoveObj which states that link removal due to the object being
4015 * deleted is NOT an originating update - they just go away!
4018 static int replmd_delete_remove_link(struct ldb_module *module,
4019 const struct dsdb_schema *schema,
4020 struct replmd_private *replmd_private,
4021 struct ldb_dn *dn,
4022 struct GUID *guid,
4023 struct ldb_message_element *el,
4024 const struct dsdb_attribute *sa,
4025 struct ldb_request *parent,
4026 bool *caller_should_vanish)
4028 unsigned int i;
4029 TALLOC_CTX *tmp_ctx = talloc_new(module);
4030 struct ldb_context *ldb = ldb_module_get_ctx(module);
4032 for (i=0; i<el->num_values; i++) {
4033 struct dsdb_dn *dsdb_dn;
4034 int ret;
4035 struct ldb_message *msg;
4036 const struct dsdb_attribute *target_attr;
4037 struct ldb_message_element *el2;
4038 const char *dn_str;
4039 struct ldb_val dn_val;
4040 uint32_t dsdb_flags = 0;
4041 const char *attrs[] = { NULL, NULL };
4042 struct ldb_result *link_res;
4043 struct ldb_message *link_msg;
4044 struct ldb_message_element *link_el;
4045 struct parsed_dn *link_dns;
4046 struct parsed_dn *p = NULL, *unused = NULL;
4048 if (dsdb_dn_is_deleted_val(&el->values[i])) {
4049 continue;
4052 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4053 if (!dsdb_dn) {
4054 talloc_free(tmp_ctx);
4055 return LDB_ERR_OPERATIONS_ERROR;
4058 /* remove the link */
4059 msg = ldb_msg_new(tmp_ctx);
4060 if (!msg) {
4061 ldb_module_oom(module);
4062 talloc_free(tmp_ctx);
4063 return LDB_ERR_OPERATIONS_ERROR;
4066 msg->dn = dsdb_dn->dn;
4068 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4069 if (target_attr == NULL) {
4070 continue;
4072 attrs[0] = target_attr->lDAPDisplayName;
4074 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4075 LDB_FLAG_MOD_DELETE, &el2);
4076 if (ret != LDB_SUCCESS) {
4077 ldb_module_oom(module);
4078 talloc_free(tmp_ctx);
4079 return LDB_ERR_OPERATIONS_ERROR;
4082 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4083 msg->dn, attrs,
4084 DSDB_FLAG_NEXT_MODULE |
4085 DSDB_SEARCH_SHOW_EXTENDED_DN |
4086 DSDB_SEARCH_SHOW_RECYCLED,
4087 parent);
4089 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4090 DBG_WARNING("Failed to find forward link object %s "
4091 "to remove backlink %s on %s",
4092 ldb_dn_get_linearized(msg->dn),
4093 sa->lDAPDisplayName,
4094 ldb_dn_get_linearized(dn));
4095 *caller_should_vanish = true;
4096 continue;
4099 if (ret != LDB_SUCCESS) {
4100 talloc_free(tmp_ctx);
4101 return ret;
4104 link_msg = link_res->msgs[0];
4105 link_el = ldb_msg_find_element(link_msg,
4106 target_attr->lDAPDisplayName);
4107 if (link_el == NULL) {
4108 DBG_WARNING("Failed to find forward link on %s "
4109 "as %s to remove backlink %s on %s",
4110 ldb_dn_get_linearized(msg->dn),
4111 target_attr->lDAPDisplayName,
4112 sa->lDAPDisplayName,
4113 ldb_dn_get_linearized(dn));
4114 *caller_should_vanish = true;
4115 continue;
4119 * This call 'upgrades' the links in link_dns, but we
4120 * do not commit the result back into the database, so
4121 * this is safe to call in FL2000 or on databases that
4122 * have been run at that level in the past.
4124 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
4125 tmp_ctx,
4126 link_el, &link_dns,
4127 target_attr->syntax->ldap_oid,
4128 parent);
4129 if (ret != LDB_SUCCESS) {
4130 talloc_free(tmp_ctx);
4131 return ret;
4134 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4135 guid, dn,
4136 data_blob_null, 0,
4137 &p, &unused,
4138 target_attr->syntax->ldap_oid, false);
4139 if (ret != LDB_SUCCESS) {
4140 talloc_free(tmp_ctx);
4141 return ret;
4144 if (p == NULL) {
4145 DBG_WARNING("Failed to find forward link on %s "
4146 "as %s to remove backlink %s on %s",
4147 ldb_dn_get_linearized(msg->dn),
4148 target_attr->lDAPDisplayName,
4149 sa->lDAPDisplayName,
4150 ldb_dn_get_linearized(dn));
4151 *caller_should_vanish = true;
4152 continue;
4156 * If we find a backlink to ourself, we will delete
4157 * the forward link before we get to process that
4158 * properly, so just let the caller process this via
4159 * the forward link.
4161 * We do this once we are sure we have the forward
4162 * link (to ourself) in case something is very wrong
4163 * and they are out of sync.
4165 if (ldb_dn_compare(dsdb_dn->dn, dn) == 0) {
4166 continue;
4169 /* This needs to get the Binary DN, by first searching */
4170 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4171 p->dsdb_dn);
4173 dn_val = data_blob_string_const(dn_str);
4174 el2->values = &dn_val;
4175 el2->num_values = 1;
4178 * Ensure that we tell the modification to vanish any linked
4179 * attributes (not simply mark them as isDeleted = TRUE)
4181 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4183 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4184 if (ret != LDB_SUCCESS) {
4185 talloc_free(tmp_ctx);
4186 return ret;
4189 talloc_free(tmp_ctx);
4190 return LDB_SUCCESS;
4195 handle update of replication meta data for deletion of objects
4197 This also handles the mapping of delete to a rename operation
4198 to allow deletes to be replicated.
4200 It also handles the incoming deleted objects, to ensure they are
4201 fully deleted here. In that case re_delete is true, and we do not
4202 use this as a signal to change the deleted state, just reinforce it.
4205 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4207 int ret = LDB_ERR_OTHER;
4208 bool retb, disallow_move_on_delete;
4209 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4210 const char *rdn_name;
4211 const struct ldb_val *rdn_value, *new_rdn_value;
4212 struct GUID guid;
4213 struct ldb_context *ldb = ldb_module_get_ctx(module);
4214 const struct dsdb_schema *schema;
4215 struct ldb_message *msg, *old_msg;
4216 struct ldb_message_element *el;
4217 TALLOC_CTX *tmp_ctx;
4218 struct ldb_result *res, *parent_res;
4219 static const char * const preserved_attrs[] = {
4221 * This list MUST be kept in case-insensitive sorted order,
4222 * as we use it in a binary search with ldb_attr_cmp().
4224 * We get this hard-coded list from
4225 * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4227 "attributeID",
4228 "attributeSyntax",
4229 "distinguishedName",
4230 "dNReferenceUpdate",
4231 "dNSHostName",
4232 "flatName",
4233 "governsID",
4234 "groupType",
4235 "instanceType",
4236 "isDeleted",
4237 "isRecycled",
4238 "lastKnownParent",
4239 "lDAPDisplayName",
4240 "legacyExchangeDN",
4241 "mS-DS-CreatorSID",
4242 "msDS-LastKnownRDN",
4243 "msDS-PortLDAP",
4244 "mSMQOwnerID",
4245 "name",
4246 "nCName",
4247 "nTSecurityDescriptor",
4248 "objectClass",
4249 "objectGUID",
4250 "objectSid",
4251 "oMSyntax",
4252 "proxiedObjectName",
4253 "replPropertyMetaData",
4254 "sAMAccountName",
4255 "securityIdentifier",
4256 "sIDHistory",
4257 "subClassOf",
4258 "systemFlags",
4259 "trustAttributes",
4260 "trustDirection",
4261 "trustPartner",
4262 "trustType",
4263 "userAccountControl",
4264 "uSNChanged",
4265 "uSNCreated",
4266 "whenChanged",
4267 "whenCreated",
4269 * DO NOT JUST APPEND TO THIS LIST.
4271 * In case you missed the note at the top, this list is kept
4272 * in case-insensitive sorted order. In the unlikely event you
4273 * need to add an attrbute, please add it in the RIGHT PLACE.
4276 static const char * const all_attrs[] = {
4277 DSDB_SECRET_ATTRIBUTES,
4278 "*",
4279 NULL
4281 static const struct ldb_val true_val = {
4282 .data = discard_const_p(uint8_t, "TRUE"),
4283 .length = 4
4286 unsigned int i;
4287 uint32_t dsdb_flags = 0;
4288 struct replmd_private *replmd_private;
4289 enum deletion_state deletion_state, next_deletion_state;
4291 if (ldb_dn_is_special(req->op.del.dn)) {
4292 return ldb_next_request(module, req);
4296 * We have to allow dbcheck to remove an object that
4297 * is beyond repair, and to do so totally. This could
4298 * mean we we can get a partial object from the other
4299 * DC, causing havoc, so dbcheck suggests
4300 * re-replication first. dbcheck sets both DBCHECK
4301 * and RELAX in this situation.
4303 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4304 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4305 /* really, really remove it */
4306 return ldb_next_request(module, req);
4309 tmp_ctx = talloc_new(ldb);
4310 if (!tmp_ctx) {
4311 ldb_oom(ldb);
4312 return LDB_ERR_OPERATIONS_ERROR;
4315 schema = dsdb_get_schema(ldb, tmp_ctx);
4316 if (!schema) {
4317 talloc_free(tmp_ctx);
4318 return LDB_ERR_OPERATIONS_ERROR;
4321 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4323 /* we need the complete msg off disk, so we can work out which
4324 attributes need to be removed */
4325 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4326 DSDB_FLAG_NEXT_MODULE |
4327 DSDB_SEARCH_SHOW_RECYCLED |
4328 DSDB_SEARCH_REVEAL_INTERNALS |
4329 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4330 if (ret != LDB_SUCCESS) {
4331 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4332 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4333 re_delete ? "re-delete" : "delete",
4334 ldb_dn_get_linearized(old_dn),
4335 ldb_errstring(ldb_module_get_ctx(module)));
4336 talloc_free(tmp_ctx);
4337 return ret;
4339 old_msg = res->msgs[0];
4341 replmd_deletion_state(module, old_msg,
4342 &deletion_state,
4343 &next_deletion_state);
4345 /* This supports us noticing an incoming isDeleted and acting on it */
4346 if (re_delete) {
4347 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4348 next_deletion_state = deletion_state;
4351 if (next_deletion_state == OBJECT_REMOVED) {
4353 * We have to prevent objects being deleted, even if
4354 * the administrator really wants them gone, as
4355 * without the tombstone, we can get a partial object
4356 * from the other DC, causing havoc.
4358 * The only other valid case is when the 180 day
4359 * timeout has expired, when relax is specified.
4361 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4362 /* it is already deleted - really remove it this time */
4363 talloc_free(tmp_ctx);
4364 return ldb_next_request(module, req);
4367 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4368 "This check is to prevent corruption of the replicated state.",
4369 ldb_dn_get_linearized(old_msg->dn));
4370 return LDB_ERR_UNWILLING_TO_PERFORM;
4373 rdn_name = ldb_dn_get_rdn_name(old_dn);
4374 rdn_value = ldb_dn_get_rdn_val(old_dn);
4375 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4376 talloc_free(tmp_ctx);
4377 return ldb_operr(ldb);
4380 msg = ldb_msg_new(tmp_ctx);
4381 if (msg == NULL) {
4382 ldb_module_oom(module);
4383 talloc_free(tmp_ctx);
4384 return LDB_ERR_OPERATIONS_ERROR;
4387 msg->dn = old_dn;
4389 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4390 disallow_move_on_delete =
4391 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4392 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4394 /* work out where we will be renaming this object to */
4395 if (!disallow_move_on_delete) {
4396 struct ldb_dn *deleted_objects_dn;
4397 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4398 &deleted_objects_dn);
4401 * We should not move objects if we can't find the
4402 * deleted objects DN. Not moving (or otherwise
4403 * harming) the Deleted Objects DN itself is handled
4404 * in the caller.
4406 if (re_delete && (ret != LDB_SUCCESS)) {
4407 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4408 if (new_dn == NULL) {
4409 ldb_module_oom(module);
4410 talloc_free(tmp_ctx);
4411 return LDB_ERR_OPERATIONS_ERROR;
4413 } else if (ret != LDB_SUCCESS) {
4414 /* this is probably an attempted delete on a partition
4415 * that doesn't allow delete operations, such as the
4416 * schema partition */
4417 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4418 ldb_dn_get_linearized(old_dn));
4419 talloc_free(tmp_ctx);
4420 return LDB_ERR_UNWILLING_TO_PERFORM;
4421 } else {
4422 new_dn = deleted_objects_dn;
4424 } else {
4425 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4426 if (new_dn == NULL) {
4427 ldb_module_oom(module);
4428 talloc_free(tmp_ctx);
4429 return LDB_ERR_OPERATIONS_ERROR;
4433 /* get the objects GUID from the search we just did */
4434 guid = samdb_result_guid(old_msg, "objectGUID");
4436 if (deletion_state == OBJECT_NOT_DELETED) {
4437 struct ldb_message_element *is_deleted_el;
4439 ret = replmd_make_deleted_child_dn(tmp_ctx,
4440 ldb,
4441 new_dn,
4442 rdn_name, rdn_value,
4443 guid);
4445 if (ret != LDB_SUCCESS) {
4446 talloc_free(tmp_ctx);
4447 return ret;
4450 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4451 &is_deleted_el);
4452 if (ret != LDB_SUCCESS) {
4453 ldb_asprintf_errstring(ldb, __location__
4454 ": Failed to add isDeleted string to the msg");
4455 talloc_free(tmp_ctx);
4456 return ret;
4458 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4459 } else {
4461 * No matter what has happened with other renames etc, try again to
4462 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4465 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4466 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4467 if (!retb) {
4468 ldb_asprintf_errstring(ldb, __location__
4469 ": Unable to add a prepare rdn of %s",
4470 ldb_dn_get_linearized(rdn));
4471 talloc_free(tmp_ctx);
4472 return LDB_ERR_OPERATIONS_ERROR;
4474 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4476 retb = ldb_dn_add_child(new_dn, rdn);
4477 if (!retb) {
4478 ldb_asprintf_errstring(ldb, __location__
4479 ": Unable to add rdn %s to base dn: %s",
4480 ldb_dn_get_linearized(rdn),
4481 ldb_dn_get_linearized(new_dn));
4482 talloc_free(tmp_ctx);
4483 return LDB_ERR_OPERATIONS_ERROR;
4488 now we need to modify the object in the following ways:
4490 - add isDeleted=TRUE
4491 - update rDN and name, with new rDN
4492 - remove linked attributes
4493 - remove objectCategory and sAMAccountType
4494 - remove attribs not on the preserved list
4495 - preserved if in above list, or is rDN
4496 - remove all linked attribs from this object
4497 - remove all links from other objects to this object
4498 (note we use the backlinks to do this, so we won't find one-way
4499 links that still point to this object, or deactivated two-way
4500 links, i.e. 'member' after the user has been removed from the
4501 group)
4502 - add lastKnownParent
4503 - update replPropertyMetaData?
4505 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4508 if (deletion_state == OBJECT_NOT_DELETED) {
4509 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4510 char *parent_dn_str = NULL;
4511 struct ldb_message_element *p_el;
4513 /* we need the storage form of the parent GUID */
4514 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4515 parent_dn, NULL,
4516 DSDB_FLAG_NEXT_MODULE |
4517 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4518 DSDB_SEARCH_REVEAL_INTERNALS|
4519 DSDB_SEARCH_SHOW_RECYCLED, req);
4520 if (ret != LDB_SUCCESS) {
4521 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4522 "repmd_delete: Failed to %s %s, "
4523 "because we failed to find it's parent (%s): %s",
4524 re_delete ? "re-delete" : "delete",
4525 ldb_dn_get_linearized(old_dn),
4526 ldb_dn_get_linearized(parent_dn),
4527 ldb_errstring(ldb_module_get_ctx(module)));
4528 talloc_free(tmp_ctx);
4529 return ret;
4533 * Now we can use the DB version,
4534 * it will have the extended DN info in it
4536 parent_dn = parent_res->msgs[0]->dn;
4537 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4538 parent_dn,
4540 if (parent_dn_str == NULL) {
4541 talloc_free(tmp_ctx);
4542 return ldb_module_oom(module);
4545 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4546 parent_dn_str);
4547 if (ret != LDB_SUCCESS) {
4548 ldb_asprintf_errstring(ldb, __location__
4549 ": Failed to add lastKnownParent "
4550 "string when deleting %s",
4551 ldb_dn_get_linearized(old_dn));
4552 talloc_free(tmp_ctx);
4553 return ret;
4555 p_el = ldb_msg_find_element(msg,
4556 "lastKnownParent");
4557 if (p_el == NULL) {
4558 talloc_free(tmp_ctx);
4559 return ldb_module_operr(module);
4561 p_el->flags = LDB_FLAG_MOD_REPLACE;
4563 if (next_deletion_state == OBJECT_DELETED) {
4564 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4565 if (ret != LDB_SUCCESS) {
4566 ldb_asprintf_errstring(ldb, __location__
4567 ": Failed to add msDS-LastKnownRDN "
4568 "string when deleting %s",
4569 ldb_dn_get_linearized(old_dn));
4570 talloc_free(tmp_ctx);
4571 return ret;
4573 p_el = ldb_msg_find_element(msg,
4574 "msDS-LastKnownRDN");
4575 if (p_el == NULL) {
4576 talloc_free(tmp_ctx);
4577 return ldb_module_operr(module);
4579 p_el->flags = LDB_FLAG_MOD_ADD;
4583 switch (next_deletion_state) {
4585 case OBJECT_RECYCLED:
4586 case OBJECT_TOMBSTONE:
4589 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4590 * describes what must be removed from a tombstone
4591 * object
4593 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4594 * describes what must be removed from a recycled
4595 * object
4600 * we also mark it as recycled, meaning this object can't be
4601 * recovered (we are stripping its attributes).
4602 * This is done only if we have this schema object of course ...
4603 * This behavior is identical to the one of Windows 2008R2 which
4604 * always set the isRecycled attribute, even if the recycle-bin is
4605 * not activated and what ever the forest level is.
4607 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4608 struct ldb_message_element *is_recycled_el;
4610 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4611 &is_recycled_el);
4612 if (ret != LDB_SUCCESS) {
4613 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4614 ldb_module_oom(module);
4615 talloc_free(tmp_ctx);
4616 return ret;
4618 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4621 replmd_private = talloc_get_type(ldb_module_get_private(module),
4622 struct replmd_private);
4623 /* work out which of the old attributes we will be removing */
4624 for (i=0; i<old_msg->num_elements; i++) {
4625 const struct dsdb_attribute *sa;
4626 el = &old_msg->elements[i];
4627 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4628 if (!sa) {
4629 const char *old_dn_str
4630 = ldb_dn_get_linearized(old_dn);
4632 ldb_asprintf_errstring(ldb,
4633 __location__
4634 ": Attribute %s "
4635 "not found in schema "
4636 "when deleting %s. "
4637 "Existing record is invalid",
4638 el->name,
4639 old_dn_str);
4640 talloc_free(tmp_ctx);
4641 return LDB_ERR_OPERATIONS_ERROR;
4643 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4644 /* don't remove the rDN */
4645 continue;
4648 if (sa->linkID & 1) {
4649 bool caller_should_vanish = false;
4651 * we have a backlink in this object
4652 * that needs to be removed. We're not
4653 * allowed to remove it directly
4654 * however, so we instead setup a
4655 * modify to delete the corresponding
4656 * forward link
4658 ret = replmd_delete_remove_link(module, schema,
4659 replmd_private,
4660 old_dn, &guid,
4661 el, sa, req,
4662 &caller_should_vanish);
4663 if (ret != LDB_SUCCESS) {
4664 const char *old_dn_str
4665 = ldb_dn_get_linearized(old_dn);
4666 ldb_asprintf_errstring(ldb,
4667 __location__
4668 ": Failed to remove backlink of "
4669 "%s when deleting %s: %s",
4670 el->name,
4671 old_dn_str,
4672 ldb_errstring(ldb));
4673 talloc_free(tmp_ctx);
4674 return LDB_ERR_OPERATIONS_ERROR;
4677 if (caller_should_vanish == false) {
4679 * now we continue, which means we
4680 * won't remove this backlink
4681 * directly
4683 continue;
4687 * Otherwise vanish the link, we are
4688 * out of sync and the controlling
4689 * object does not have the source
4690 * link any more
4693 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4695 } else if (sa->linkID == 0) {
4696 const char * const *attr = NULL;
4697 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4698 continue;
4700 BINARY_ARRAY_SEARCH_V(preserved_attrs,
4701 ARRAY_SIZE(preserved_attrs),
4702 el->name,
4703 ldb_attr_cmp,
4704 attr);
4706 * If we are preserving, do not do the
4707 * ldb_msg_add_empty() below, continue
4708 * to the next element
4710 if (attr != NULL) {
4711 continue;
4713 } else {
4715 * Ensure that we tell the modification to vanish any linked
4716 * attributes (not simply mark them as isDeleted = TRUE)
4718 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4720 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, NULL);
4721 if (ret != LDB_SUCCESS) {
4722 talloc_free(tmp_ctx);
4723 ldb_module_oom(module);
4724 return ret;
4728 break;
4730 case OBJECT_DELETED:
4732 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4733 * describes what must be removed from a deleted
4734 * object
4737 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4738 if (ret != LDB_SUCCESS) {
4739 talloc_free(tmp_ctx);
4740 ldb_module_oom(module);
4741 return ret;
4744 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4745 if (ret != LDB_SUCCESS) {
4746 talloc_free(tmp_ctx);
4747 ldb_module_oom(module);
4748 return ret;
4751 break;
4753 default:
4754 break;
4757 if (deletion_state == OBJECT_NOT_DELETED) {
4758 const struct dsdb_attribute *sa;
4760 /* work out what the new rdn value is, for updating the
4761 rDN and name fields */
4762 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4763 if (new_rdn_value == NULL) {
4764 talloc_free(tmp_ctx);
4765 return ldb_operr(ldb);
4768 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4769 if (!sa) {
4770 talloc_free(tmp_ctx);
4771 return LDB_ERR_OPERATIONS_ERROR;
4774 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4775 &el);
4776 if (ret != LDB_SUCCESS) {
4777 talloc_free(tmp_ctx);
4778 return ret;
4780 el->flags = LDB_FLAG_MOD_REPLACE;
4782 el = ldb_msg_find_element(old_msg, "name");
4783 if (el) {
4784 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4785 if (ret != LDB_SUCCESS) {
4786 talloc_free(tmp_ctx);
4787 return ret;
4789 el->flags = LDB_FLAG_MOD_REPLACE;
4794 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4799 * No matter what has happned with other renames, try again to
4800 * get this to be under the deleted DN.
4802 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4803 /* now rename onto the new DN */
4804 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4805 if (ret != LDB_SUCCESS){
4806 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4807 ldb_dn_get_linearized(old_dn),
4808 ldb_dn_get_linearized(new_dn),
4809 ldb_errstring(ldb)));
4810 talloc_free(tmp_ctx);
4811 return ret;
4813 msg->dn = new_dn;
4816 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4817 if (ret != LDB_SUCCESS) {
4818 char *s = NULL;
4820 * This should not fail, so be quite verbose in the
4821 * error handling if it fails
4823 if (strcmp(ldb_dn_get_linearized(old_dn),
4824 ldb_dn_get_linearized(new_dn)) != 0) {
4825 DBG_NOTICE("Failure to handle '%s' of object %s "
4826 "after successful rename to %s. "
4827 "Error during tombstone modificaton was: %s\n",
4828 re_delete ? "re-delete" : "delete",
4829 ldb_dn_get_linearized(new_dn),
4830 ldb_dn_get_linearized(old_dn),
4831 ldb_errstring(ldb));
4832 } else {
4833 DBG_NOTICE("Failure to handle '%s' of object %s. "
4834 "Error during tombstone modificaton was: %s\n",
4835 re_delete ? "re-delete" : "delete",
4836 ldb_dn_get_linearized(new_dn),
4837 ldb_errstring(ldb));
4839 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(module),
4840 tmp_ctx,
4841 LDB_CHANGETYPE_MODIFY,
4842 msg);
4844 DBG_INFO("Failed tombstone modify%s was:\n%s\n",
4845 (dsdb_flags & DSDB_REPLMD_VANISH_LINKS) ?
4846 " with VANISH_LINKS" : "",
4848 ldb_asprintf_errstring(ldb,
4849 "replmd_delete: Failed to modify"
4850 " object %s in '%s' - %s",
4851 ldb_dn_get_linearized(old_dn),
4852 re_delete ? "re-delete" : "delete",
4853 ldb_errstring(ldb));
4854 talloc_free(tmp_ctx);
4855 return ret;
4858 talloc_free(tmp_ctx);
4860 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4863 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4865 return replmd_delete_internals(module, req, false);
4869 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4871 return ret;
4874 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4876 int ret = LDB_ERR_OTHER;
4877 /* TODO: do some error mapping */
4879 /* Let the caller know the full WERROR */
4880 ar->objs->error = status;
4882 return ret;
4886 static struct replPropertyMetaData1 *
4887 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4888 enum drsuapi_DsAttributeId attid)
4890 uint32_t i;
4891 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4893 for (i = 0; i < rpmd_ctr->count; i++) {
4894 if (rpmd_ctr->array[i].attid == attid) {
4895 return &rpmd_ctr->array[i];
4898 return NULL;
4903 return true if an update is newer than an existing entry
4904 see section 5.11 of MS-ADTS
4906 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4907 const struct GUID *update_invocation_id,
4908 uint32_t current_version,
4909 uint32_t update_version,
4910 NTTIME current_change_time,
4911 NTTIME update_change_time)
4913 if (update_version != current_version) {
4914 return update_version > current_version;
4916 if (update_change_time != current_change_time) {
4917 return update_change_time > current_change_time;
4919 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4922 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4923 struct replPropertyMetaData1 *new_m)
4925 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4926 &new_m->originating_invocation_id,
4927 cur_m->version,
4928 new_m->version,
4929 cur_m->originating_change_time,
4930 new_m->originating_change_time);
4933 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4934 struct replPropertyMetaData1 *cur_m,
4935 struct replPropertyMetaData1 *new_m)
4937 bool cmp;
4940 * If the new replPropertyMetaData entry for this attribute is
4941 * not provided (this happens in the case where we look for
4942 * ATTID_name, but the name was not changed), then the local
4943 * state is clearly still current, as the remote
4944 * server didn't send it due to being older the high watermark
4945 * USN we sent.
4947 if (new_m == NULL) {
4948 return false;
4951 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4953 * if we compare equal then do an
4954 * update. This is used when a client
4955 * asks for a FULL_SYNC, and can be
4956 * used to recover a corrupt
4957 * replica.
4959 * This call is a bit tricky, what we
4960 * are doing it turning the 'is_newer'
4961 * call into a 'not is older' by
4962 * swapping cur_m and new_m, and negating the
4963 * outcome.
4965 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4966 cur_m);
4967 } else {
4968 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4969 new_m);
4971 return cmp;
4976 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4978 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4979 struct ldb_context *ldb,
4980 struct ldb_dn *dn,
4981 const char *four_char_prefix,
4982 const char *rdn_name,
4983 const struct ldb_val *rdn_value,
4984 struct GUID guid)
4986 struct ldb_val deleted_child_rdn_val;
4987 struct GUID_txt_buf guid_str;
4988 int ret;
4989 bool retb;
4991 GUID_buf_string(&guid, &guid_str);
4993 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4994 if (!retb) {
4995 ldb_asprintf_errstring(ldb, __location__
4996 ": Unable to add a formatted child to dn: %s",
4997 ldb_dn_get_linearized(dn));
4998 return LDB_ERR_OPERATIONS_ERROR;
5002 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
5003 * we should truncate this value to ensure the RDN is not more than 255 chars.
5005 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
5007 * "Naming constraints are not enforced for replicated
5008 * updates." so this is safe and we don't have to work out not
5009 * splitting a UTF8 char right now.
5011 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
5014 * sizeof(guid_str.buf) will always be longer than
5015 * strlen(guid_str.buf) but we allocate using this and
5016 * waste the trailing bytes to avoid scaring folks
5017 * with memcpy() using strlen() below
5020 deleted_child_rdn_val.data
5021 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
5022 uint8_t,
5023 rdn_value->length + 5
5024 + sizeof(guid_str.buf));
5025 if (!deleted_child_rdn_val.data) {
5026 ldb_asprintf_errstring(ldb, __location__
5027 ": Unable to add a formatted child to dn: %s",
5028 ldb_dn_get_linearized(dn));
5029 return LDB_ERR_OPERATIONS_ERROR;
5032 deleted_child_rdn_val.length =
5033 rdn_value->length + 5
5034 + strlen(guid_str.buf);
5036 SMB_ASSERT(deleted_child_rdn_val.length <
5037 talloc_get_size(deleted_child_rdn_val.data));
5040 * talloc won't allocate more than 256MB so we can't
5041 * overflow but just to be sure
5043 if (deleted_child_rdn_val.length < rdn_value->length) {
5044 return LDB_ERR_OPERATIONS_ERROR;
5047 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
5048 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
5049 four_char_prefix, 4);
5050 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
5051 guid_str.buf,
5052 sizeof(guid_str.buf));
5054 /* Now set the value into the RDN, without parsing it */
5055 ret = ldb_dn_set_component(
5058 rdn_name,
5059 deleted_child_rdn_val);
5061 return ret;
5066 form a conflict DN
5068 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
5069 struct ldb_context *ldb,
5070 struct ldb_dn *dn,
5071 struct GUID *guid)
5073 const struct ldb_val *rdn_val;
5074 const char *rdn_name;
5075 struct ldb_dn *new_dn;
5076 int ret;
5078 rdn_val = ldb_dn_get_rdn_val(dn);
5079 rdn_name = ldb_dn_get_rdn_name(dn);
5080 if (!rdn_val || !rdn_name) {
5081 return NULL;
5084 new_dn = ldb_dn_get_parent(mem_ctx, dn);
5085 if (!new_dn) {
5086 return NULL;
5089 ret = replmd_make_prefix_child_dn(mem_ctx,
5090 ldb, new_dn,
5091 "CNF:",
5092 rdn_name,
5093 rdn_val,
5094 *guid);
5095 if (ret != LDB_SUCCESS) {
5096 return NULL;
5098 return new_dn;
5102 form a deleted DN
5104 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
5105 struct ldb_context *ldb,
5106 struct ldb_dn *dn,
5107 const char *rdn_name,
5108 const struct ldb_val *rdn_value,
5109 struct GUID guid)
5111 return replmd_make_prefix_child_dn(tmp_ctx,
5112 ldb, dn,
5113 "DEL:",
5114 rdn_name,
5115 rdn_value,
5116 guid);
5121 perform a modify operation which sets the rDN and name attributes to
5122 their current values. This has the effect of changing these
5123 attributes to have been last updated by the current DC. This is
5124 needed to ensure that renames performed as part of conflict
5125 resolution are propagated to other DCs
5127 static int replmd_name_modify(struct replmd_replicated_request *ar,
5128 struct ldb_request *req, struct ldb_dn *dn)
5130 struct ldb_message *msg;
5131 const char *rdn_name;
5132 const struct ldb_val *rdn_val;
5133 const struct dsdb_attribute *rdn_attr;
5134 int ret;
5136 msg = ldb_msg_new(req);
5137 if (msg == NULL) {
5138 goto failed;
5140 msg->dn = dn;
5142 rdn_name = ldb_dn_get_rdn_name(dn);
5143 if (rdn_name == NULL) {
5144 goto failed;
5147 /* normalize the rdn attribute name */
5148 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5149 if (rdn_attr == NULL) {
5150 goto failed;
5152 rdn_name = rdn_attr->lDAPDisplayName;
5154 rdn_val = ldb_dn_get_rdn_val(dn);
5155 if (rdn_val == NULL) {
5156 goto failed;
5159 if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
5160 goto failed;
5162 if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
5163 goto failed;
5167 * We have to mark this as a replicated update otherwise
5168 * schema_data may reject a rename in the schema partition
5171 ret = dsdb_module_modify(ar->module, msg,
5172 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5173 req);
5174 if (ret != LDB_SUCCESS) {
5175 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5176 ldb_dn_get_linearized(dn),
5177 ldb_errstring(ldb_module_get_ctx(ar->module))));
5178 return ret;
5181 talloc_free(msg);
5183 return LDB_SUCCESS;
5185 failed:
5186 talloc_free(msg);
5187 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5188 ldb_dn_get_linearized(dn)));
5189 return LDB_ERR_OPERATIONS_ERROR;
5194 callback for conflict DN handling where we have renamed the incoming
5195 record. After renaming it, we need to ensure the change of name and
5196 rDN for the incoming record is seen as an originating update by this DC.
5198 This also handles updating lastKnownParent for entries sent to lostAndFound
5200 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5202 struct replmd_replicated_request *ar =
5203 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5204 struct ldb_dn *conflict_dn = NULL;
5205 int ret;
5207 if (ares->error != LDB_SUCCESS) {
5208 /* call the normal callback for everything except success */
5209 return replmd_op_callback(req, ares);
5212 switch (req->operation) {
5213 case LDB_ADD:
5214 conflict_dn = req->op.add.message->dn;
5215 break;
5216 case LDB_MODIFY:
5217 conflict_dn = req->op.mod.message->dn;
5218 break;
5219 default:
5220 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5223 /* perform a modify of the rDN and name of the record */
5224 ret = replmd_name_modify(ar, req, conflict_dn);
5225 if (ret != LDB_SUCCESS) {
5226 ares->error = ret;
5227 return replmd_op_callback(req, ares);
5230 if (ar->objs->objects[ar->index_current].last_known_parent) {
5231 struct ldb_message *msg = ldb_msg_new(req);
5232 if (msg == NULL) {
5233 ldb_module_oom(ar->module);
5234 return LDB_ERR_OPERATIONS_ERROR;
5237 msg->dn = req->op.add.message->dn;
5239 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5240 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5241 if (ret != LDB_SUCCESS) {
5242 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5243 ldb_module_oom(ar->module);
5244 return ret;
5246 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5248 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5249 if (ret != LDB_SUCCESS) {
5250 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5251 ldb_dn_get_linearized(msg->dn),
5252 ldb_errstring(ldb_module_get_ctx(ar->module))));
5253 return ret;
5255 TALLOC_FREE(msg);
5258 return replmd_op_callback(req, ares);
5264 * A helper for replmd_op_possible_conflict_callback() and
5265 * replmd_replicated_handle_rename()
5267 static int incoming_dn_should_be_renamed(TALLOC_CTX *mem_ctx,
5268 struct replmd_replicated_request *ar,
5269 struct ldb_dn *conflict_dn,
5270 struct ldb_result **res,
5271 bool *rename_incoming_record)
5273 int ret;
5274 bool rodc;
5275 enum ndr_err_code ndr_err;
5276 const struct ldb_val *omd_value = NULL;
5277 struct replPropertyMetaDataBlob omd, *rmd = NULL;
5278 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5279 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5280 struct replPropertyMetaData1 *omd_name = NULL;
5281 struct replPropertyMetaData1 *rmd_name = NULL;
5282 struct ldb_message *msg = NULL;
5284 ret = samdb_rodc(ldb, &rodc);
5285 if (ret != LDB_SUCCESS) {
5286 ldb_asprintf_errstring(
5287 ldb,
5288 "Failed to determine if we are an RODC when attempting "
5289 "to form conflict DN: %s",
5290 ldb_errstring(ldb));
5291 return LDB_ERR_OPERATIONS_ERROR;
5294 if (rodc) {
5296 * We are on an RODC, or were a GC for this
5297 * partition, so we have to fail this until
5298 * someone who owns the partition sorts it
5299 * out
5301 ldb_asprintf_errstring(
5302 ldb,
5303 "Conflict adding object '%s' from incoming replication "
5304 "but we are read only for the partition. \n"
5305 " - We must fail the operation until a master for this "
5306 "partition resolves the conflict",
5307 ldb_dn_get_linearized(conflict_dn));
5308 return LDB_ERR_OPERATIONS_ERROR;
5312 * first we need the replPropertyMetaData attribute from the
5313 * old record
5315 ret = dsdb_module_search_dn(ar->module, mem_ctx, res, conflict_dn,
5316 attrs,
5317 DSDB_FLAG_NEXT_MODULE |
5318 DSDB_SEARCH_SHOW_DELETED |
5319 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5320 if (ret != LDB_SUCCESS) {
5321 DBG_ERR(__location__
5322 ": Unable to find object for conflicting record '%s'\n",
5323 ldb_dn_get_linearized(conflict_dn));
5324 return LDB_ERR_OPERATIONS_ERROR;
5327 msg = (*res)->msgs[0];
5328 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
5329 if (omd_value == NULL) {
5330 DBG_ERR(__location__
5331 ": Unable to find replPropertyMetaData for conflicting "
5332 "record '%s'\n",
5333 ldb_dn_get_linearized(conflict_dn));
5334 return LDB_ERR_OPERATIONS_ERROR;
5337 ndr_err = ndr_pull_struct_blob(
5338 omd_value, msg, &omd,
5339 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5340 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5341 DBG_ERR(__location__
5342 ": Failed to parse old replPropertyMetaData for %s\n",
5343 ldb_dn_get_linearized(conflict_dn));
5344 return LDB_ERR_OPERATIONS_ERROR;
5347 rmd = ar->objs->objects[ar->index_current].meta_data;
5350 * we decide which is newer based on the RPMD on the name
5351 * attribute. See [MS-DRSR] ResolveNameConflict.
5353 * We expect omd_name to be present, as this is from a local
5354 * search, but while rmd_name should have been given to us by
5355 * the remote server, if it is missing we just prefer the
5356 * local name in
5357 * replmd_replPropertyMetaData1_new_should_be_taken()
5359 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd,
5360 DRSUAPI_ATTID_name);
5361 omd_name = replmd_replPropertyMetaData1_find_attid(&omd,
5362 DRSUAPI_ATTID_name);
5363 if (!omd_name) {
5364 DBG_ERR(__location__
5365 ": Failed to find name attribute in "
5366 "local LDB replPropertyMetaData for %s\n",
5367 ldb_dn_get_linearized(conflict_dn));
5368 return LDB_ERR_OPERATIONS_ERROR;
5372 * Should we preserve the current record, and so rename the
5373 * incoming record to be a conflict?
5375 *rename_incoming_record =
5376 !replmd_replPropertyMetaData1_new_should_be_taken(
5377 (ar->objs->dsdb_repl_flags &
5378 DSDB_REPL_FLAG_PRIORITISE_INCOMING),
5379 omd_name, rmd_name);
5381 return LDB_SUCCESS;
5386 callback for replmd_replicated_apply_add()
5387 This copes with the creation of conflict records in the case where
5388 the DN exists, but with a different objectGUID
5390 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))
5392 struct ldb_dn *conflict_dn;
5393 struct replmd_replicated_request *ar =
5394 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5395 struct ldb_result *res;
5396 int ret;
5397 bool rename_incoming_record;
5398 struct ldb_message *msg;
5399 struct ldb_request *down_req = NULL;
5401 /* call the normal callback for success */
5402 if (ares->error == LDB_SUCCESS) {
5403 return callback(req, ares);
5407 * we have a conflict, and need to decide if we will keep the
5408 * new record or the old record
5411 msg = ar->objs->objects[ar->index_current].msg;
5412 conflict_dn = msg->dn;
5414 /* For failures other than conflicts, fail the whole operation here */
5415 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5416 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5417 ldb_dn_get_linearized(conflict_dn),
5418 ldb_errstring(ldb_module_get_ctx(ar->module)));
5420 return ldb_module_done(ar->req, NULL, NULL,
5421 LDB_ERR_OPERATIONS_ERROR);
5425 ret = incoming_dn_should_be_renamed(req, ar, conflict_dn, &res,
5426 &rename_incoming_record);
5427 if (ret != LDB_SUCCESS) {
5428 goto failed;
5431 if (rename_incoming_record) {
5432 struct GUID guid;
5433 struct ldb_dn *new_dn;
5435 guid = samdb_result_guid(msg, "objectGUID");
5436 if (GUID_all_zero(&guid)) {
5437 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5438 ldb_dn_get_linearized(conflict_dn)));
5439 goto failed;
5441 new_dn = replmd_conflict_dn(req,
5442 ldb_module_get_ctx(ar->module),
5443 conflict_dn, &guid);
5444 if (new_dn == NULL) {
5445 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5446 ldb_dn_get_linearized(conflict_dn)));
5447 goto failed;
5450 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5451 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5453 /* re-submit the request, but with the new DN */
5454 callback = replmd_op_name_modify_callback;
5455 msg->dn = new_dn;
5456 } else {
5457 /* we are renaming the existing record */
5458 struct GUID guid;
5459 struct ldb_dn *new_dn;
5461 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5462 if (GUID_all_zero(&guid)) {
5463 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5464 ldb_dn_get_linearized(conflict_dn)));
5465 goto failed;
5468 new_dn = replmd_conflict_dn(req,
5469 ldb_module_get_ctx(ar->module),
5470 conflict_dn, &guid);
5471 if (new_dn == NULL) {
5472 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5473 ldb_dn_get_linearized(conflict_dn)));
5474 goto failed;
5477 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5478 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5480 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5481 DSDB_FLAG_OWN_MODULE, req);
5482 if (ret != LDB_SUCCESS) {
5483 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5484 ldb_dn_get_linearized(conflict_dn),
5485 ldb_dn_get_linearized(new_dn),
5486 ldb_errstring(ldb_module_get_ctx(ar->module))));
5487 goto failed;
5491 * now we need to ensure that the rename is seen as an
5492 * originating update. We do that with a modify.
5494 ret = replmd_name_modify(ar, req, new_dn);
5495 if (ret != LDB_SUCCESS) {
5496 goto failed;
5499 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5500 ldb_dn_get_linearized(req->op.add.message->dn)));
5503 ret = ldb_build_add_req(&down_req,
5504 ldb_module_get_ctx(ar->module),
5505 req,
5506 msg,
5507 ar->controls,
5509 callback,
5510 req);
5511 if (ret != LDB_SUCCESS) {
5512 goto failed;
5514 LDB_REQ_SET_LOCATION(down_req);
5516 /* current partition control needed by "repmd_op_callback" */
5517 ret = ldb_request_add_control(down_req,
5518 DSDB_CONTROL_CURRENT_PARTITION_OID,
5519 false, NULL);
5520 if (ret != LDB_SUCCESS) {
5521 return replmd_replicated_request_error(ar, ret);
5524 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5525 /* this tells the partition module to make it a
5526 partial replica if creating an NC */
5527 ret = ldb_request_add_control(down_req,
5528 DSDB_CONTROL_PARTIAL_REPLICA,
5529 false, NULL);
5530 if (ret != LDB_SUCCESS) {
5531 return replmd_replicated_request_error(ar, ret);
5536 * Finally we re-run the add, otherwise the new record won't
5537 * exist, as we are here because of that exact failure!
5539 return ldb_next_request(ar->module, down_req);
5540 failed:
5542 /* on failure make the caller get the error. This means
5543 * replication will stop with an error, but there is not much
5544 * else we can do.
5546 if (ret == LDB_SUCCESS) {
5547 ret = LDB_ERR_OPERATIONS_ERROR;
5549 return ldb_module_done(ar->req, NULL, NULL,
5550 ret);
5554 callback for replmd_replicated_apply_add()
5555 This copes with the creation of conflict records in the case where
5556 the DN exists, but with a different objectGUID
5558 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5560 struct replmd_replicated_request *ar =
5561 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5563 if (ar->objs->objects[ar->index_current].last_known_parent) {
5564 /* This is like a conflict DN, where we put the object in LostAndFound
5565 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5566 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5569 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5573 this is called when a new object comes in over DRS
5575 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5577 struct ldb_context *ldb;
5578 struct ldb_request *change_req;
5579 enum ndr_err_code ndr_err;
5580 struct ldb_message *msg;
5581 struct replPropertyMetaDataBlob *md;
5582 struct ldb_val md_value;
5583 unsigned int i;
5584 int ret;
5585 bool remote_isDeleted = false;
5586 bool is_schema_nc;
5587 NTTIME now;
5588 time_t t = time(NULL);
5589 const struct ldb_val *rdn_val;
5590 struct replmd_private *replmd_private =
5591 talloc_get_type(ldb_module_get_private(ar->module),
5592 struct replmd_private);
5593 unix_to_nt_time(&now, t);
5595 ldb = ldb_module_get_ctx(ar->module);
5596 msg = ar->objs->objects[ar->index_current].msg;
5597 md = ar->objs->objects[ar->index_current].meta_data;
5598 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5600 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5601 if (ret != LDB_SUCCESS) {
5602 return replmd_replicated_request_error(ar, ret);
5605 ret = dsdb_msg_add_guid(msg,
5606 &ar->objs->objects[ar->index_current].object_guid,
5607 "objectGUID");
5608 if (ret != LDB_SUCCESS) {
5609 return replmd_replicated_request_error(ar, ret);
5612 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5613 if (ret != LDB_SUCCESS) {
5614 return replmd_replicated_request_error(ar, ret);
5617 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5618 if (ret != LDB_SUCCESS) {
5619 return replmd_replicated_request_error(ar, ret);
5622 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5623 if (ret != LDB_SUCCESS) {
5624 return replmd_replicated_request_error(ar, ret);
5627 /* remove any message elements that have zero values */
5628 for (i=0; i<msg->num_elements; i++) {
5629 struct ldb_message_element *el = &msg->elements[i];
5631 if (el->num_values == 0) {
5632 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5633 ldb_asprintf_errstring(ldb, __location__
5634 ": empty objectClass sent on %s, aborting replication\n",
5635 ldb_dn_get_linearized(msg->dn));
5636 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5639 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5640 el->name));
5641 ldb_msg_remove_element(msg, &msg->elements[i]);
5642 i--;
5643 continue;
5647 if (DEBUGLVL(8)) {
5648 struct GUID_txt_buf guid_txt;
5650 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5651 LDB_CHANGETYPE_ADD,
5652 msg);
5653 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5654 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5655 s));
5656 talloc_free(s);
5657 } else if (DEBUGLVL(4)) {
5658 struct GUID_txt_buf guid_txt;
5659 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5660 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5661 ldb_dn_get_linearized(msg->dn)));
5663 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5664 "isDeleted", false);
5667 * the meta data array is already sorted by the caller, except
5668 * for the RDN, which needs to be added.
5672 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5673 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5674 md, ar, now, is_schema_nc,
5675 false);
5676 if (ret != LDB_SUCCESS) {
5677 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5678 return replmd_replicated_request_error(ar, ret);
5681 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5682 if (ret != LDB_SUCCESS) {
5683 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5684 return replmd_replicated_request_error(ar, ret);
5687 for (i=0; i < md->ctr.ctr1.count; i++) {
5688 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5690 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5691 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5692 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5693 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5694 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5696 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5697 if (ret != LDB_SUCCESS) {
5698 return replmd_replicated_request_error(ar, ret);
5701 replmd_ldb_message_sort(msg, ar->schema);
5703 if (!remote_isDeleted) {
5705 * Ensure any local ACL inheritence is applied from
5706 * the parent object.
5708 * This is needed because descriptor is above
5709 * repl_meta_data in the module stack, so this will
5710 * not be trigered 'naturally' by the flow of
5711 * operations.
5713 ret = dsdb_module_schedule_sd_propagation(ar->module,
5714 ar->objs->partition_dn,
5715 ar->objs->objects[ar->index_current].object_guid,
5716 ar->objs->objects[ar->index_current].parent_guid ?
5717 *ar->objs->objects[ar->index_current].parent_guid :
5718 GUID_zero(),
5719 true);
5720 if (ret != LDB_SUCCESS) {
5721 return replmd_replicated_request_error(ar, ret);
5725 ar->isDeleted = remote_isDeleted;
5727 ret = ldb_build_add_req(&change_req,
5728 ldb,
5730 msg,
5731 ar->controls,
5733 replmd_op_add_callback,
5734 ar->req);
5735 LDB_REQ_SET_LOCATION(change_req);
5736 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5738 /* current partition control needed by "repmd_op_callback" */
5739 ret = ldb_request_add_control(change_req,
5740 DSDB_CONTROL_CURRENT_PARTITION_OID,
5741 false, NULL);
5742 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5744 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5745 /* this tells the partition module to make it a
5746 partial replica if creating an NC */
5747 ret = ldb_request_add_control(change_req,
5748 DSDB_CONTROL_PARTIAL_REPLICA,
5749 false, NULL);
5750 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5753 return ldb_next_request(ar->module, change_req);
5756 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5757 struct ldb_reply *ares)
5759 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5760 struct replmd_replicated_request);
5761 int ret;
5763 if (!ares) {
5764 return ldb_module_done(ar->req, NULL, NULL,
5765 LDB_ERR_OPERATIONS_ERROR);
5769 * The error NO_SUCH_OBJECT is not expected, unless the search
5770 * base is the partition DN, and that case doesn't happen here
5771 * because then we wouldn't get a parent_guid_value in any
5772 * case.
5774 if (ares->error != LDB_SUCCESS) {
5775 return ldb_module_done(ar->req, ares->controls,
5776 ares->response, ares->error);
5779 switch (ares->type) {
5780 case LDB_REPLY_ENTRY:
5782 struct ldb_message *parent_msg = ares->message;
5783 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5784 struct ldb_dn *parent_dn = NULL;
5785 int comp_num;
5787 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5788 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5789 /* Per MS-DRSR 4.1.10.6.10
5790 * FindBestParentObject we need to move this
5791 * new object under a deleted object to
5792 * lost-and-found */
5793 struct ldb_dn *nc_root;
5795 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5796 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5797 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5798 "No suitable NC root found for %s. "
5799 "We need to move this object because parent object %s "
5800 "is deleted, but this object is not.",
5801 ldb_dn_get_linearized(msg->dn),
5802 ldb_dn_get_linearized(parent_msg->dn));
5803 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5804 } else if (ret != LDB_SUCCESS) {
5805 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5806 "Unable to find NC root for %s: %s. "
5807 "We need to move this object because parent object %s "
5808 "is deleted, but this object is not.",
5809 ldb_dn_get_linearized(msg->dn),
5810 ldb_errstring(ldb_module_get_ctx(ar->module)),
5811 ldb_dn_get_linearized(parent_msg->dn));
5812 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5815 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5816 nc_root,
5817 DS_GUID_LOSTANDFOUND_CONTAINER,
5818 &parent_dn);
5819 if (ret != LDB_SUCCESS) {
5820 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5821 "Unable to find LostAndFound Container for %s "
5822 "in partition %s: %s. "
5823 "We need to move this object because parent object %s "
5824 "is deleted, but this object is not.",
5825 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5826 ldb_errstring(ldb_module_get_ctx(ar->module)),
5827 ldb_dn_get_linearized(parent_msg->dn));
5828 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5830 ar->objs->objects[ar->index_current].last_known_parent
5831 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5833 } else {
5834 parent_dn
5835 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5838 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5840 comp_num = ldb_dn_get_comp_num(msg->dn);
5841 if (comp_num > 1) {
5842 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5843 talloc_free(ares);
5844 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5847 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5848 talloc_free(ares);
5849 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5851 break;
5853 case LDB_REPLY_REFERRAL:
5854 /* we ignore referrals */
5855 break;
5857 case LDB_REPLY_DONE:
5859 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5860 struct GUID_txt_buf str_buf;
5861 if (ar->search_msg != NULL) {
5862 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5863 "No parent with GUID %s found for object locally known as %s",
5864 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5865 ldb_dn_get_linearized(ar->search_msg->dn));
5866 } else {
5867 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5868 "No parent with GUID %s found for object remotely known as %s",
5869 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5870 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5874 * This error code is really important, as it
5875 * is the flag back to the callers to retry
5876 * this with DRSUAPI_DRS_GET_ANC, and so get
5877 * the parent objects before the child
5878 * objects
5880 return ldb_module_done(ar->req, NULL, NULL,
5881 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5884 if (ar->search_msg != NULL) {
5885 ret = replmd_replicated_apply_merge(ar);
5886 } else {
5887 ret = replmd_replicated_apply_add(ar);
5889 if (ret != LDB_SUCCESS) {
5890 return ldb_module_done(ar->req, NULL, NULL, ret);
5894 talloc_free(ares);
5895 return LDB_SUCCESS;
5899 * Look for the parent object, so we put the new object in the right
5900 * place This is akin to NameObject in MS-DRSR - this routine and the
5901 * callbacks find the right parent name, and correct name for this
5902 * object
5905 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5907 struct ldb_context *ldb;
5908 int ret;
5909 char *tmp_str;
5910 char *filter;
5911 struct ldb_request *search_req;
5912 static const char *attrs[] = {"isDeleted", NULL};
5913 struct GUID_txt_buf guid_str_buf;
5915 ldb = ldb_module_get_ctx(ar->module);
5917 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5918 if (ar->search_msg != NULL) {
5919 return replmd_replicated_apply_merge(ar);
5920 } else {
5921 return replmd_replicated_apply_add(ar);
5925 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5926 &guid_str_buf);
5928 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5929 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5931 ret = ldb_build_search_req(&search_req,
5932 ldb,
5934 ar->objs->partition_dn,
5935 LDB_SCOPE_SUBTREE,
5936 filter,
5937 attrs,
5938 NULL,
5940 replmd_replicated_apply_search_for_parent_callback,
5941 ar->req);
5942 LDB_REQ_SET_LOCATION(search_req);
5944 ret = dsdb_request_add_controls(search_req,
5945 DSDB_SEARCH_SHOW_RECYCLED|
5946 DSDB_SEARCH_SHOW_DELETED|
5947 DSDB_SEARCH_SHOW_EXTENDED_DN);
5948 if (ret != LDB_SUCCESS) {
5949 return ret;
5952 return ldb_next_request(ar->module, search_req);
5956 handle renames that come in over DRS replication
5958 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5959 struct ldb_message *msg,
5960 struct ldb_request *parent,
5961 bool *renamed_to_conflict)
5963 int ret;
5964 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5965 struct ldb_result *res;
5966 struct ldb_dn *conflict_dn;
5967 bool rename_incoming_record;
5968 struct ldb_dn *new_dn;
5969 struct GUID guid;
5971 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5972 ldb_dn_get_linearized(ar->search_msg->dn),
5973 ldb_dn_get_linearized(msg->dn)));
5976 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5977 DSDB_FLAG_NEXT_MODULE, ar->req);
5978 if (ret == LDB_SUCCESS) {
5979 talloc_free(tmp_ctx);
5980 return ret;
5983 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5984 talloc_free(tmp_ctx);
5985 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5986 ldb_dn_get_linearized(ar->search_msg->dn),
5987 ldb_dn_get_linearized(msg->dn),
5988 ldb_errstring(ldb_module_get_ctx(ar->module)));
5989 return ret;
5992 conflict_dn = msg->dn;
5995 ret = incoming_dn_should_be_renamed(tmp_ctx, ar, conflict_dn, &res,
5996 &rename_incoming_record);
5997 if (ret != LDB_SUCCESS) {
5998 goto failed;
6001 if (rename_incoming_record) {
6003 new_dn = replmd_conflict_dn(msg,
6004 ldb_module_get_ctx(ar->module),
6005 msg->dn,
6006 &ar->objs->objects[ar->index_current].object_guid);
6007 if (new_dn == NULL) {
6008 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6009 "Failed to form conflict DN for %s\n",
6010 ldb_dn_get_linearized(msg->dn));
6012 talloc_free(tmp_ctx);
6013 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6016 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
6017 DSDB_FLAG_NEXT_MODULE, ar->req);
6018 if (ret != LDB_SUCCESS) {
6019 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6020 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
6021 ldb_dn_get_linearized(conflict_dn),
6022 ldb_dn_get_linearized(ar->search_msg->dn),
6023 ldb_dn_get_linearized(new_dn),
6024 ldb_errstring(ldb_module_get_ctx(ar->module)));
6025 talloc_free(tmp_ctx);
6026 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6029 msg->dn = new_dn;
6030 *renamed_to_conflict = true;
6031 talloc_free(tmp_ctx);
6032 return LDB_SUCCESS;
6035 /* we are renaming the existing record */
6037 guid = samdb_result_guid(res->msgs[0], "objectGUID");
6038 if (GUID_all_zero(&guid)) {
6039 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
6040 ldb_dn_get_linearized(conflict_dn)));
6041 goto failed;
6044 new_dn = replmd_conflict_dn(tmp_ctx,
6045 ldb_module_get_ctx(ar->module),
6046 conflict_dn, &guid);
6047 if (new_dn == NULL) {
6048 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
6049 ldb_dn_get_linearized(conflict_dn)));
6050 goto failed;
6053 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
6054 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
6056 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
6057 DSDB_FLAG_OWN_MODULE, ar->req);
6058 if (ret != LDB_SUCCESS) {
6059 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
6060 ldb_dn_get_linearized(conflict_dn),
6061 ldb_dn_get_linearized(new_dn),
6062 ldb_errstring(ldb_module_get_ctx(ar->module))));
6063 goto failed;
6067 * now we need to ensure that the rename is seen as an
6068 * originating update. We do that with a modify.
6070 ret = replmd_name_modify(ar, ar->req, new_dn);
6071 if (ret != LDB_SUCCESS) {
6072 goto failed;
6075 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
6076 ldb_dn_get_linearized(ar->search_msg->dn),
6077 ldb_dn_get_linearized(msg->dn)));
6080 * With the other record out of the way, do the rename we had
6081 * at the top again
6083 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6084 DSDB_FLAG_NEXT_MODULE, ar->req);
6085 if (ret != LDB_SUCCESS) {
6086 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6087 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 goto failed;
6093 talloc_free(tmp_ctx);
6094 return ret;
6095 failed:
6097 * On failure make the caller get the error
6098 * This means replication will stop with an error,
6099 * but there is not much else we can do. In the
6100 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6101 * needed.
6103 if (ret == LDB_SUCCESS) {
6104 ret = LDB_ERR_OPERATIONS_ERROR;
6107 talloc_free(tmp_ctx);
6108 return ret;
6112 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6114 struct ldb_context *ldb;
6115 struct ldb_request *change_req;
6116 enum ndr_err_code ndr_err;
6117 struct ldb_message *msg;
6118 struct replPropertyMetaDataBlob *rmd;
6119 struct replPropertyMetaDataBlob omd;
6120 const struct ldb_val *omd_value;
6121 struct replPropertyMetaDataBlob nmd;
6122 struct ldb_val nmd_value;
6123 struct GUID remote_parent_guid;
6124 unsigned int i;
6125 uint32_t j,ni=0;
6126 unsigned int removed_attrs = 0;
6127 int ret;
6128 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6129 bool isDeleted = false;
6130 bool local_isDeleted = false;
6131 bool remote_isDeleted = false;
6132 bool take_remote_isDeleted = false;
6133 bool sd_updated = false;
6134 bool renamed = false;
6135 bool renamed_to_conflict = false;
6136 bool is_schema_nc = false;
6137 NTSTATUS nt_status;
6138 const struct ldb_val *old_rdn, *new_rdn;
6139 struct replmd_private *replmd_private =
6140 talloc_get_type(ldb_module_get_private(ar->module),
6141 struct replmd_private);
6142 NTTIME now;
6143 time_t t = time(NULL);
6144 unix_to_nt_time(&now, t);
6146 ldb = ldb_module_get_ctx(ar->module);
6147 msg = ar->objs->objects[ar->index_current].msg;
6149 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6151 rmd = ar->objs->objects[ar->index_current].meta_data;
6152 ZERO_STRUCT(omd);
6153 omd.version = 1;
6155 /* find existing meta data */
6156 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6157 if (omd_value) {
6158 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6159 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6160 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6161 nt_status = ndr_map_error2ntstatus(ndr_err);
6162 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6165 if (omd.version != 1) {
6166 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6170 if (DEBUGLVL(8)) {
6171 struct GUID_txt_buf guid_txt;
6173 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6174 LDB_CHANGETYPE_MODIFY, msg);
6175 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6176 "%s\n"
6177 "%s\n",
6178 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6180 ndr_print_struct_string(s,
6181 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6182 "existing replPropertyMetaData",
6183 &omd),
6184 ndr_print_struct_string(s,
6185 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6186 "incoming replPropertyMetaData",
6187 rmd)));
6188 talloc_free(s);
6189 } else if (DEBUGLVL(4)) {
6190 struct GUID_txt_buf guid_txt;
6192 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6193 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6194 &guid_txt),
6195 ldb_dn_get_linearized(msg->dn)));
6198 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6199 "isDeleted", false);
6200 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6201 "isDeleted", false);
6204 * Fill in the remote_parent_guid with the GUID or an all-zero
6205 * GUID.
6207 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6208 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6209 } else {
6210 remote_parent_guid = GUID_zero();
6214 * To ensure we follow a complex rename chain around, we have
6215 * to confirm that the DN is the same (mostly to confirm the
6216 * RDN) and the parentGUID is the same.
6218 * This ensures we keep things under the correct parent, which
6219 * replmd_replicated_handle_rename() will do.
6222 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6223 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6224 ret = LDB_SUCCESS;
6225 } else {
6227 * handle renames, even just by case that come in over
6228 * DRS. Changes in the parent DN don't hit us here,
6229 * because the search for a parent will clean up those
6230 * components.
6232 * We also have already filtered out the case where
6233 * the peer has an older name to what we have (see
6234 * replmd_replicated_apply_search_callback())
6236 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed_to_conflict);
6239 * This looks strange, but we must set this after any
6240 * rename, otherwise the SD propegation will not
6241 * happen (which might matter if we have a new parent)
6243 * The additional case of calling
6244 * replmd_op_name_modify_callback (below) is
6245 * controlled by renamed_to_conflict.
6247 renamed = true;
6250 if (ret != LDB_SUCCESS) {
6251 ldb_debug(ldb, LDB_DEBUG_FATAL,
6252 "replmd_replicated_request rename %s => %s failed - %s\n",
6253 ldb_dn_get_linearized(ar->search_msg->dn),
6254 ldb_dn_get_linearized(msg->dn),
6255 ldb_errstring(ldb));
6256 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6259 if (renamed_to_conflict == true) {
6261 * Set the callback to one that will fix up the name
6262 * metadata on the new conflict DN
6264 callback = replmd_op_name_modify_callback;
6267 ZERO_STRUCT(nmd);
6268 nmd.version = 1;
6269 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6270 nmd.ctr.ctr1.array = talloc_array(ar,
6271 struct replPropertyMetaData1,
6272 nmd.ctr.ctr1.count);
6273 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6275 /* first copy the old meta data */
6276 for (i=0; i < omd.ctr.ctr1.count; i++) {
6277 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6278 ni++;
6281 ar->seq_num = 0;
6282 /* now merge in the new meta data */
6283 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6284 bool found = false;
6286 for (j=0; j < ni; j++) {
6287 bool cmp;
6289 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6290 continue;
6293 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6294 ar->objs->dsdb_repl_flags,
6295 &nmd.ctr.ctr1.array[j],
6296 &rmd->ctr.ctr1.array[i]);
6297 if (cmp) {
6298 /* replace the entry */
6299 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6300 if (ar->seq_num == 0) {
6301 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6302 if (ret != LDB_SUCCESS) {
6303 return replmd_replicated_request_error(ar, ret);
6306 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6307 switch (nmd.ctr.ctr1.array[j].attid) {
6308 case DRSUAPI_ATTID_ntSecurityDescriptor:
6309 sd_updated = true;
6310 break;
6311 case DRSUAPI_ATTID_isDeleted:
6312 take_remote_isDeleted = true;
6313 break;
6314 default:
6315 break;
6317 found = true;
6318 break;
6321 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6322 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6323 msg->elements[i-removed_attrs].name,
6324 ldb_dn_get_linearized(msg->dn),
6325 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6328 /* we don't want to apply this change so remove the attribute */
6329 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6330 removed_attrs++;
6332 found = true;
6333 break;
6336 if (found) continue;
6338 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6339 if (ar->seq_num == 0) {
6340 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6341 if (ret != LDB_SUCCESS) {
6342 return replmd_replicated_request_error(ar, ret);
6345 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6346 switch (nmd.ctr.ctr1.array[ni].attid) {
6347 case DRSUAPI_ATTID_ntSecurityDescriptor:
6348 sd_updated = true;
6349 break;
6350 case DRSUAPI_ATTID_isDeleted:
6351 take_remote_isDeleted = true;
6352 break;
6353 default:
6354 break;
6356 ni++;
6360 * finally correct the size of the meta_data array
6362 nmd.ctr.ctr1.count = ni;
6364 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6365 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6367 if (renamed) {
6368 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6369 &nmd, ar, now, is_schema_nc,
6370 false);
6371 if (ret != LDB_SUCCESS) {
6372 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6373 return replmd_replicated_request_error(ar, ret);
6377 * sort the new meta data array
6379 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6380 if (ret != LDB_SUCCESS) {
6381 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6382 return ret;
6386 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6387 * UpdateObject.
6389 * This also controls SD propagation below
6391 if (take_remote_isDeleted) {
6392 isDeleted = remote_isDeleted;
6393 } else {
6394 isDeleted = local_isDeleted;
6397 ar->isDeleted = isDeleted;
6400 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6402 if (msg->num_elements == 0) {
6403 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6404 ar->index_current);
6406 return replmd_replicated_apply_isDeleted(ar);
6409 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6410 ar->index_current, msg->num_elements);
6412 if (renamed) {
6414 * This is an new name for this object, so we must
6415 * inherit from the parent
6417 * This is needed because descriptor is above
6418 * repl_meta_data in the module stack, so this will
6419 * not be trigered 'naturally' by the flow of
6420 * operations.
6422 ret = dsdb_module_schedule_sd_propagation(ar->module,
6423 ar->objs->partition_dn,
6424 ar->objs->objects[ar->index_current].object_guid,
6425 ar->objs->objects[ar->index_current].parent_guid ?
6426 *ar->objs->objects[ar->index_current].parent_guid :
6427 GUID_zero(),
6428 true);
6429 if (ret != LDB_SUCCESS) {
6430 return ldb_operr(ldb);
6434 if (sd_updated && !isDeleted) {
6436 * This is an existing object, so there is no need to
6437 * inherit from the parent, but we must inherit any
6438 * incoming changes to our child objects.
6440 * This is needed because descriptor is above
6441 * repl_meta_data in the module stack, so this will
6442 * not be trigered 'naturally' by the flow of
6443 * operations.
6445 ret = dsdb_module_schedule_sd_propagation(ar->module,
6446 ar->objs->partition_dn,
6447 ar->objs->objects[ar->index_current].object_guid,
6448 ar->objs->objects[ar->index_current].parent_guid ?
6449 *ar->objs->objects[ar->index_current].parent_guid :
6450 GUID_zero(),
6451 false);
6452 if (ret != LDB_SUCCESS) {
6453 return ldb_operr(ldb);
6457 /* create the meta data value */
6458 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6459 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6461 nt_status = ndr_map_error2ntstatus(ndr_err);
6462 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6466 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6467 * and replPopertyMetaData attributes
6469 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6470 if (ret != LDB_SUCCESS) {
6471 return replmd_replicated_request_error(ar, ret);
6473 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6474 if (ret != LDB_SUCCESS) {
6475 return replmd_replicated_request_error(ar, ret);
6477 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6478 if (ret != LDB_SUCCESS) {
6479 return replmd_replicated_request_error(ar, ret);
6482 replmd_ldb_message_sort(msg, ar->schema);
6484 /* we want to replace the old values */
6485 for (i=0; i < msg->num_elements; i++) {
6486 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6487 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6488 if (msg->elements[i].num_values == 0) {
6489 ldb_asprintf_errstring(ldb, __location__
6490 ": objectClass removed on %s, aborting replication\n",
6491 ldb_dn_get_linearized(msg->dn));
6492 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6497 if (DEBUGLVL(8)) {
6498 struct GUID_txt_buf guid_txt;
6500 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6501 LDB_CHANGETYPE_MODIFY,
6502 msg);
6503 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6504 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6505 &guid_txt),
6506 s));
6507 talloc_free(s);
6508 } else if (DEBUGLVL(4)) {
6509 struct GUID_txt_buf guid_txt;
6511 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6512 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6513 &guid_txt),
6514 ldb_dn_get_linearized(msg->dn)));
6517 ret = ldb_build_mod_req(&change_req,
6518 ldb,
6520 msg,
6521 ar->controls,
6523 callback,
6524 ar->req);
6525 LDB_REQ_SET_LOCATION(change_req);
6526 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6528 /* current partition control needed by "repmd_op_callback" */
6529 ret = ldb_request_add_control(change_req,
6530 DSDB_CONTROL_CURRENT_PARTITION_OID,
6531 false, NULL);
6532 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6534 return ldb_next_request(ar->module, change_req);
6537 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6538 struct ldb_reply *ares)
6540 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6541 struct replmd_replicated_request);
6542 int ret;
6544 if (!ares) {
6545 return ldb_module_done(ar->req, NULL, NULL,
6546 LDB_ERR_OPERATIONS_ERROR);
6548 if (ares->error != LDB_SUCCESS &&
6549 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6550 return ldb_module_done(ar->req, ares->controls,
6551 ares->response, ares->error);
6554 switch (ares->type) {
6555 case LDB_REPLY_ENTRY:
6556 ar->search_msg = talloc_steal(ar, ares->message);
6557 break;
6559 case LDB_REPLY_REFERRAL:
6560 /* we ignore referrals */
6561 break;
6563 case LDB_REPLY_DONE:
6565 struct replPropertyMetaData1 *md_remote;
6566 struct replPropertyMetaData1 *md_local;
6568 struct replPropertyMetaDataBlob omd;
6569 const struct ldb_val *omd_value;
6570 struct replPropertyMetaDataBlob *rmd;
6571 struct ldb_message *msg;
6572 int instanceType;
6573 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6574 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6577 * This is the ADD case, find the appropriate parent,
6578 * as this object doesn't exist locally:
6580 if (ar->search_msg == NULL) {
6581 ret = replmd_replicated_apply_search_for_parent(ar);
6582 if (ret != LDB_SUCCESS) {
6583 return ldb_module_done(ar->req, NULL, NULL, ret);
6585 talloc_free(ares);
6586 return LDB_SUCCESS;
6590 * Otherwise, in the MERGE case, work out if we are
6591 * attempting a rename, and if so find the parent the
6592 * newly renamed object wants to belong under (which
6593 * may not be the parent in it's attached string DN
6595 rmd = ar->objs->objects[ar->index_current].meta_data;
6596 ZERO_STRUCT(omd);
6597 omd.version = 1;
6599 /* find existing meta data */
6600 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6601 if (omd_value) {
6602 enum ndr_err_code ndr_err;
6603 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6604 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6605 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6606 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6607 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6610 if (omd.version != 1) {
6611 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6615 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6617 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6618 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6619 && GUID_all_zero(&ar->local_parent_guid)) {
6620 DEBUG(0, ("Refusing to replicate new version of %s "
6621 "as local object has an all-zero parentGUID attribute, "
6622 "despite not being an NC root\n",
6623 ldb_dn_get_linearized(ar->search_msg->dn)));
6624 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6628 * now we need to check for double renames. We could have a
6629 * local rename pending which our replication partner hasn't
6630 * received yet. We choose which one wins by looking at the
6631 * attribute stamps on the two objects, the newer one wins.
6633 * This also simply applies the correct algorithms for
6634 * determining if a change was made to name at all, or
6635 * if the object has just been renamed under the same
6636 * parent.
6638 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6639 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6640 if (!md_local) {
6641 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6642 ldb_dn_get_linearized(ar->search_msg->dn)));
6643 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6647 * if there is no name attribute given then we have to assume the
6648 * object we've received has the older name
6650 if (replmd_replPropertyMetaData1_new_should_be_taken(
6651 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6652 md_local, md_remote)) {
6653 struct GUID_txt_buf p_guid_local;
6654 struct GUID_txt_buf p_guid_remote;
6655 msg = ar->objs->objects[ar->index_current].msg;
6657 /* Merge on the existing object, with rename */
6659 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6660 "as incoming object changing to %s under %s\n",
6661 ldb_dn_get_linearized(ar->search_msg->dn),
6662 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6663 ldb_dn_get_linearized(msg->dn),
6664 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6665 &p_guid_remote)));
6666 ret = replmd_replicated_apply_search_for_parent(ar);
6667 } else {
6668 struct GUID_txt_buf p_guid_local;
6669 struct GUID_txt_buf p_guid_remote;
6670 msg = ar->objs->objects[ar->index_current].msg;
6673 * Merge on the existing object, force no
6674 * rename (code below just to explain why in
6675 * the DEBUG() logs)
6678 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6679 ldb_dn_get_linearized(msg->dn)) == 0) {
6680 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6681 GUID_equal(&ar->local_parent_guid,
6682 ar->objs->objects[ar->index_current].parent_guid)
6683 == false) {
6684 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6685 "despite incoming object changing parent to %s\n",
6686 ldb_dn_get_linearized(ar->search_msg->dn),
6687 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6688 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6689 &p_guid_remote)));
6691 } else {
6692 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6693 " and rejecting older rename to %s under %s\n",
6694 ldb_dn_get_linearized(ar->search_msg->dn),
6695 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6696 ldb_dn_get_linearized(msg->dn),
6697 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6698 &p_guid_remote)));
6701 * This assignment ensures that the strcmp()
6702 * and GUID_equal() calls in
6703 * replmd_replicated_apply_merge() avoids the
6704 * rename call
6706 ar->objs->objects[ar->index_current].parent_guid =
6707 &ar->local_parent_guid;
6709 msg->dn = ar->search_msg->dn;
6710 ret = replmd_replicated_apply_merge(ar);
6712 if (ret != LDB_SUCCESS) {
6713 return ldb_module_done(ar->req, NULL, NULL, ret);
6718 talloc_free(ares);
6719 return LDB_SUCCESS;
6723 * Returns true if we can group together processing this link attribute,
6724 * i.e. it has the same source-object and attribute ID as other links
6725 * already in the group
6727 static bool la_entry_matches_group(struct la_entry *la_entry,
6728 struct la_group *la_group)
6730 struct la_entry *prev = la_group->la_entries;
6732 return (la_entry->la->attid == prev->la->attid &&
6733 GUID_equal(&la_entry->la->identifier->guid,
6734 &prev->la->identifier->guid));
6738 * Creates a new la_entry to store replication info for a single
6739 * linked attribute.
6741 static struct la_entry *
6742 create_la_entry(struct replmd_private *replmd_private,
6743 struct drsuapi_DsReplicaLinkedAttribute *la,
6744 uint32_t dsdb_repl_flags)
6746 struct la_entry *la_entry;
6748 if (replmd_private->la_ctx == NULL) {
6749 replmd_private->la_ctx = talloc_new(replmd_private);
6751 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6752 if (la_entry == NULL) {
6753 return NULL;
6755 la_entry->la = talloc(la_entry,
6756 struct drsuapi_DsReplicaLinkedAttribute);
6757 if (la_entry->la == NULL) {
6758 talloc_free(la_entry);
6759 return NULL;
6761 *la_entry->la = *la;
6762 la_entry->dsdb_repl_flags = dsdb_repl_flags;
6765 * we need to steal the non-scalars so they stay
6766 * around until the end of the transaction
6768 talloc_steal(la_entry->la, la_entry->la->identifier);
6769 talloc_steal(la_entry->la, la_entry->la->value.blob);
6771 return la_entry;
6775 * Stores the linked attributes received in the replication chunk - these get
6776 * applied at the end of the transaction. We also check that each linked
6777 * attribute is valid, i.e. source and target objects are known.
6779 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6781 int ret = LDB_SUCCESS;
6782 uint32_t i;
6783 struct ldb_module *module = ar->module;
6784 struct replmd_private *replmd_private =
6785 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6786 struct la_group *la_group = NULL;
6787 struct ldb_context *ldb;
6788 TALLOC_CTX *tmp_ctx = NULL;
6789 struct ldb_message *src_msg = NULL;
6790 const struct dsdb_attribute *attr = NULL;
6792 ldb = ldb_module_get_ctx(module);
6794 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6796 /* save away the linked attributes for the end of the transaction */
6797 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6798 struct la_entry *la_entry;
6799 bool new_srcobj;
6801 /* create an entry to store the received link attribute info */
6802 la_entry = create_la_entry(replmd_private,
6803 &ar->objs->linked_attributes[i],
6804 ar->objs->dsdb_repl_flags);
6805 if (la_entry == NULL) {
6806 ldb_oom(ldb);
6807 return LDB_ERR_OPERATIONS_ERROR;
6811 * check if we're still dealing with the same source object
6812 * as the last link
6814 new_srcobj = (la_group == NULL ||
6815 !la_entry_matches_group(la_entry, la_group));
6817 if (new_srcobj) {
6819 /* get a new mem_ctx to lookup the source object */
6820 TALLOC_FREE(tmp_ctx);
6821 tmp_ctx = talloc_new(ar);
6822 if (tmp_ctx == NULL) {
6823 ldb_oom(ldb);
6824 return LDB_ERR_OPERATIONS_ERROR;
6827 /* verify the link source exists */
6828 ret = replmd_get_la_entry_source(module, la_entry,
6829 tmp_ctx, &attr,
6830 &src_msg);
6833 * When we fail to find the source object, the error
6834 * code we pass back here is really important. It flags
6835 * back to the callers to retry this request with
6836 * DRSUAPI_DRS_GET_ANC. This case should never happen
6837 * if we're replicating from a Samba DC, but it is
6838 * needed to talk to a Windows DC
6840 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6841 WERROR err = WERR_DS_DRA_MISSING_PARENT;
6842 ret = replmd_replicated_request_werror(ar,
6843 err);
6844 break;
6848 ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6849 src_msg->dn, attr);
6850 if (ret != LDB_SUCCESS) {
6851 break;
6854 /* group the links together by source-object for efficiency */
6855 if (new_srcobj) {
6856 la_group = talloc_zero(replmd_private->la_ctx,
6857 struct la_group);
6858 if (la_group == NULL) {
6859 ldb_oom(ldb);
6860 return LDB_ERR_OPERATIONS_ERROR;
6862 DLIST_ADD(replmd_private->la_list, la_group);
6864 DLIST_ADD(la_group->la_entries, la_entry);
6865 replmd_private->total_links++;
6868 TALLOC_FREE(tmp_ctx);
6869 return ret;
6872 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6874 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6876 struct ldb_context *ldb;
6877 int ret;
6878 char *tmp_str;
6879 char *filter;
6880 struct ldb_request *search_req;
6881 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6882 "parentGUID", "instanceType",
6883 "replPropertyMetaData", "nTSecurityDescriptor",
6884 "isDeleted", NULL };
6885 struct GUID_txt_buf guid_str_buf;
6887 if (ar->index_current >= ar->objs->num_objects) {
6890 * Now that we've applied all the objects, check the new linked
6891 * attributes and store them (we apply them in .prepare_commit)
6893 ret = replmd_store_linked_attributes(ar);
6895 if (ret != LDB_SUCCESS) {
6896 return ret;
6899 /* done applying objects, move on to the next stage */
6900 return replmd_replicated_uptodate_vector(ar);
6903 ldb = ldb_module_get_ctx(ar->module);
6904 ar->search_msg = NULL;
6905 ar->isDeleted = false;
6907 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6908 &guid_str_buf);
6910 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6911 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6913 ret = ldb_build_search_req(&search_req,
6914 ldb,
6916 ar->objs->partition_dn,
6917 LDB_SCOPE_SUBTREE,
6918 filter,
6919 attrs,
6920 NULL,
6922 replmd_replicated_apply_search_callback,
6923 ar->req);
6924 LDB_REQ_SET_LOCATION(search_req);
6927 * We set DSDB_SEARCH_SHOW_EXTENDED_DN to get the GUID on the
6928 * DN. This in turn helps our operational module find the
6929 * record by GUID, not DN lookup which is more error prone if
6930 * DN indexing changes. We prefer to keep chasing GUIDs
6931 * around if possible, even within a transaction.
6933 * The aim here is to keep replication moving and allow a
6934 * reindex later.
6936 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED
6937 |DSDB_SEARCH_SHOW_EXTENDED_DN);
6939 if (ret != LDB_SUCCESS) {
6940 return ret;
6943 return ldb_next_request(ar->module, search_req);
6947 * Returns true if we need to do extra processing to handle deleted object
6948 * changes received via replication
6950 static bool replmd_should_apply_isDeleted(struct replmd_replicated_request *ar,
6951 struct ldb_message *msg)
6953 struct ldb_dn *deleted_objects_dn;
6954 int ret;
6956 if (!ar->isDeleted) {
6958 /* not a deleted object, so don't set isDeleted */
6959 return false;
6962 ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module),
6963 msg, msg->dn,
6964 &deleted_objects_dn);
6967 * if the Deleted Object container lookup failed, then just apply
6968 * isDeleted (note that it doesn't exist for the Schema partition)
6970 if (ret != LDB_SUCCESS) {
6971 return true;
6975 * the Deleted Objects container has isDeleted set but is not entirely
6976 * a deleted object, so DON'T re-apply isDeleted to it
6978 if (ldb_dn_compare(msg->dn, deleted_objects_dn) == 0) {
6979 return false;
6982 return true;
6986 * This is essentially a wrapper for replmd_replicated_apply_next()
6988 * This is needed to ensure that both codepaths call this handler.
6990 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6992 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6993 int ret;
6994 bool apply_isDeleted;
6995 struct ldb_request *del_req = NULL;
6996 struct ldb_result *res = NULL;
6997 TALLOC_CTX *tmp_ctx = NULL;
6999 apply_isDeleted = replmd_should_apply_isDeleted(ar, msg);
7001 if (!apply_isDeleted) {
7003 /* nothing to do */
7004 ar->index_current++;
7005 return replmd_replicated_apply_next(ar);
7009 * Do a delete here again, so that if there is
7010 * anything local that conflicts with this
7011 * object being deleted, it is removed. This
7012 * includes links. See MS-DRSR 4.1.10.6.9
7013 * UpdateObject.
7015 * If the object is already deleted, and there
7016 * is no more work required, it doesn't do
7017 * anything.
7020 /* This has been updated to point to the DN we eventually did the modify on */
7022 tmp_ctx = talloc_new(ar);
7023 if (!tmp_ctx) {
7024 ret = ldb_oom(ldb_module_get_ctx(ar->module));
7025 return ret;
7028 res = talloc_zero(tmp_ctx, struct ldb_result);
7029 if (!res) {
7030 ret = ldb_oom(ldb_module_get_ctx(ar->module));
7031 talloc_free(tmp_ctx);
7032 return ret;
7035 /* Build a delete request, which hopefully will artually turn into nothing */
7036 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
7037 msg->dn,
7038 NULL,
7039 res,
7040 ldb_modify_default_callback,
7041 ar->req);
7042 LDB_REQ_SET_LOCATION(del_req);
7043 if (ret != LDB_SUCCESS) {
7044 talloc_free(tmp_ctx);
7045 return ret;
7049 * This is the guts of the call, call back
7050 * into our delete code, but setting the
7051 * re_delete flag so we delete anything that
7052 * shouldn't be there on a deleted or recycled
7053 * object
7055 ret = replmd_delete_internals(ar->module, del_req, true);
7056 if (ret == LDB_SUCCESS) {
7057 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
7060 talloc_free(tmp_ctx);
7061 if (ret != LDB_SUCCESS) {
7062 return ret;
7065 ar->index_current++;
7066 return replmd_replicated_apply_next(ar);
7069 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
7070 struct ldb_reply *ares)
7072 struct ldb_context *ldb;
7073 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7074 struct replmd_replicated_request);
7075 ldb = ldb_module_get_ctx(ar->module);
7077 if (!ares) {
7078 return ldb_module_done(ar->req, NULL, NULL,
7079 LDB_ERR_OPERATIONS_ERROR);
7081 if (ares->error != LDB_SUCCESS) {
7082 return ldb_module_done(ar->req, ares->controls,
7083 ares->response, ares->error);
7086 if (ares->type != LDB_REPLY_DONE) {
7087 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
7088 return ldb_module_done(ar->req, NULL, NULL,
7089 LDB_ERR_OPERATIONS_ERROR);
7092 talloc_free(ares);
7094 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7097 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
7099 struct ldb_context *ldb;
7100 struct ldb_request *change_req;
7101 enum ndr_err_code ndr_err;
7102 struct ldb_message *msg;
7103 struct replUpToDateVectorBlob ouv;
7104 const struct ldb_val *ouv_value;
7105 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
7106 struct replUpToDateVectorBlob nuv;
7107 struct ldb_val nuv_value;
7108 struct ldb_message_element *nuv_el = NULL;
7109 struct ldb_message_element *orf_el = NULL;
7110 struct repsFromToBlob nrf;
7111 struct ldb_val *nrf_value = NULL;
7112 struct ldb_message_element *nrf_el = NULL;
7113 unsigned int i;
7114 uint32_t j,ni=0;
7115 bool found = false;
7116 time_t t = time(NULL);
7117 NTTIME now;
7118 int ret;
7119 uint32_t instanceType;
7121 ldb = ldb_module_get_ctx(ar->module);
7122 ruv = ar->objs->uptodateness_vector;
7123 ZERO_STRUCT(ouv);
7124 ouv.version = 2;
7125 ZERO_STRUCT(nuv);
7126 nuv.version = 2;
7128 unix_to_nt_time(&now, t);
7130 if (ar->search_msg == NULL) {
7131 /* this happens for a REPL_OBJ call where we are
7132 creating the target object by replicating it. The
7133 subdomain join code does this for the partition DN
7135 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
7136 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7139 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
7140 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
7141 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
7142 ldb_dn_get_linearized(ar->search_msg->dn)));
7143 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7147 * first create the new replUpToDateVector
7149 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
7150 if (ouv_value) {
7151 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
7152 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
7153 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7154 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7155 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7158 if (ouv.version != 2) {
7159 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7164 * the new uptodateness vector will at least
7165 * contain 1 entry, one for the source_dsa
7167 * plus optional values from our old vector and the one from the source_dsa
7169 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
7170 if (ruv) nuv.ctr.ctr2.count += ruv->count;
7171 nuv.ctr.ctr2.cursors = talloc_array(ar,
7172 struct drsuapi_DsReplicaCursor2,
7173 nuv.ctr.ctr2.count);
7174 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7176 /* first copy the old vector */
7177 for (i=0; i < ouv.ctr.ctr2.count; i++) {
7178 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
7179 ni++;
7182 /* merge in the source_dsa vector is available */
7183 for (i=0; (ruv && i < ruv->count); i++) {
7184 found = false;
7186 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7187 &ar->our_invocation_id)) {
7188 continue;
7191 for (j=0; j < ni; j++) {
7192 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7193 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7194 continue;
7197 found = true;
7199 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7200 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7202 break;
7205 if (found) continue;
7207 /* if it's not there yet, add it */
7208 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7209 ni++;
7213 * finally correct the size of the cursors array
7215 nuv.ctr.ctr2.count = ni;
7218 * sort the cursors
7220 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7223 * create the change ldb_message
7225 msg = ldb_msg_new(ar);
7226 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7227 msg->dn = ar->search_msg->dn;
7229 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7230 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7231 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7232 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7233 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7235 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7236 if (ret != LDB_SUCCESS) {
7237 return replmd_replicated_request_error(ar, ret);
7239 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7242 * now create the new repsFrom value from the given repsFromTo1 structure
7244 ZERO_STRUCT(nrf);
7245 nrf.version = 1;
7246 nrf.ctr.ctr1 = *ar->objs->source_dsa;
7247 nrf.ctr.ctr1.last_attempt = now;
7248 nrf.ctr.ctr1.last_success = now;
7249 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7252 * first see if we already have a repsFrom value for the current source dsa
7253 * if so we'll later replace this value
7255 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7256 if (orf_el) {
7257 for (i=0; i < orf_el->num_values; i++) {
7258 struct repsFromToBlob *trf;
7260 trf = talloc(ar, struct repsFromToBlob);
7261 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7263 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7264 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7265 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7266 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7267 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7270 if (trf->version != 1) {
7271 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7275 * we compare the source dsa objectGUID not the invocation_id
7276 * because we want only one repsFrom value per source dsa
7277 * and when the invocation_id of the source dsa has changed we don't need
7278 * the old repsFrom with the old invocation_id
7280 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7281 &ar->objs->source_dsa->source_dsa_obj_guid)) {
7282 talloc_free(trf);
7283 continue;
7286 talloc_free(trf);
7287 nrf_value = &orf_el->values[i];
7288 break;
7292 * copy over all old values to the new ldb_message
7294 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7295 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7296 *nrf_el = *orf_el;
7300 * if we haven't found an old repsFrom value for the current source dsa
7301 * we'll add a new value
7303 if (!nrf_value) {
7304 struct ldb_val zero_value;
7305 ZERO_STRUCT(zero_value);
7306 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7307 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7309 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7312 /* we now fill the value which is already attached to ldb_message */
7313 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7314 &nrf,
7315 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7316 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7317 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7318 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7322 * the ldb_message_element for the attribute, has all the old values and the new one
7323 * so we'll replace the whole attribute with all values
7325 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7327 if (CHECK_DEBUGLVL(4)) {
7328 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7329 LDB_CHANGETYPE_MODIFY,
7330 msg);
7331 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7332 talloc_free(s);
7335 /* prepare the ldb_modify() request */
7336 ret = ldb_build_mod_req(&change_req,
7337 ldb,
7339 msg,
7340 ar->controls,
7342 replmd_replicated_uptodate_modify_callback,
7343 ar->req);
7344 LDB_REQ_SET_LOCATION(change_req);
7345 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7347 return ldb_next_request(ar->module, change_req);
7350 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7351 struct ldb_reply *ares)
7353 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7354 struct replmd_replicated_request);
7355 int ret;
7357 if (!ares) {
7358 return ldb_module_done(ar->req, NULL, NULL,
7359 LDB_ERR_OPERATIONS_ERROR);
7361 if (ares->error != LDB_SUCCESS &&
7362 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7363 return ldb_module_done(ar->req, ares->controls,
7364 ares->response, ares->error);
7367 switch (ares->type) {
7368 case LDB_REPLY_ENTRY:
7369 ar->search_msg = talloc_steal(ar, ares->message);
7370 break;
7372 case LDB_REPLY_REFERRAL:
7373 /* we ignore referrals */
7374 break;
7376 case LDB_REPLY_DONE:
7377 ret = replmd_replicated_uptodate_modify(ar);
7378 if (ret != LDB_SUCCESS) {
7379 return ldb_module_done(ar->req, NULL, NULL, ret);
7383 talloc_free(ares);
7384 return LDB_SUCCESS;
7388 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7390 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7391 struct replmd_private *replmd_private =
7392 talloc_get_type_abort(ldb_module_get_private(ar->module),
7393 struct replmd_private);
7394 int ret;
7395 static const char *attrs[] = {
7396 "replUpToDateVector",
7397 "repsFrom",
7398 "instanceType",
7399 NULL
7401 struct ldb_request *search_req;
7403 ar->search_msg = NULL;
7406 * Let the caller know that we did an originating updates
7408 ar->objs->originating_updates = replmd_private->originating_updates;
7410 ret = ldb_build_search_req(&search_req,
7411 ldb,
7413 ar->objs->partition_dn,
7414 LDB_SCOPE_BASE,
7415 "(objectClass=*)",
7416 attrs,
7417 NULL,
7419 replmd_replicated_uptodate_search_callback,
7420 ar->req);
7421 LDB_REQ_SET_LOCATION(search_req);
7422 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7424 return ldb_next_request(ar->module, search_req);
7429 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7431 struct ldb_context *ldb;
7432 struct dsdb_extended_replicated_objects *objs;
7433 struct replmd_replicated_request *ar;
7434 struct ldb_control **ctrls;
7435 int ret;
7437 ldb = ldb_module_get_ctx(module);
7439 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7441 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7442 if (!objs) {
7443 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7444 return LDB_ERR_PROTOCOL_ERROR;
7447 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7448 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7449 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7450 return LDB_ERR_PROTOCOL_ERROR;
7453 ar = replmd_ctx_init(module, req);
7454 if (!ar)
7455 return LDB_ERR_OPERATIONS_ERROR;
7457 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7458 ar->apply_mode = true;
7459 ar->objs = objs;
7460 ar->schema = dsdb_get_schema(ldb, ar);
7461 if (!ar->schema) {
7462 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7463 talloc_free(ar);
7464 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7465 return LDB_ERR_CONSTRAINT_VIOLATION;
7468 ctrls = req->controls;
7470 if (req->controls) {
7471 req->controls = talloc_memdup(ar, req->controls,
7472 talloc_get_size(req->controls));
7473 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7476 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7477 if (ret != LDB_SUCCESS) {
7478 return ret;
7481 /* If this change contained linked attributes in the body
7482 * (rather than in the links section) we need to update
7483 * backlinks in linked_attributes */
7484 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7485 if (ret != LDB_SUCCESS) {
7486 return ret;
7489 ar->controls = req->controls;
7490 req->controls = ctrls;
7492 return replmd_replicated_apply_next(ar);
7496 * Checks how to handle an missing target - either we need to fail the
7497 * replication and retry with GET_TGT, ignore the link and continue, or try to
7498 * add a partial link to an unknown target.
7500 static int replmd_allow_missing_target(struct ldb_module *module,
7501 TALLOC_CTX *mem_ctx,
7502 struct ldb_dn *target_dn,
7503 struct ldb_dn *source_dn,
7504 bool is_obj_commit,
7505 struct GUID *guid,
7506 uint32_t dsdb_repl_flags,
7507 bool *ignore_link,
7508 const char * missing_str)
7510 struct ldb_context *ldb = ldb_module_get_ctx(module);
7511 bool is_in_same_nc;
7514 * we may not be able to resolve link targets properly when
7515 * dealing with subsets of objects, e.g. the source is a
7516 * critical object and the target isn't
7518 * TODO:
7519 * When we implement Trusted Domains we need to consider
7520 * whether they get treated as an incomplete replica here or not
7522 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7525 * Ignore the link. We don't increase the highwater-mark in
7526 * the object subset cases, so subsequent replications should
7527 * resolve any missing links
7529 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7530 ldb_dn_get_linearized(target_dn),
7531 ldb_dn_get_linearized(source_dn)));
7532 *ignore_link = true;
7533 return LDB_SUCCESS;
7536 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7537 mem_ctx,
7538 source_dn,
7539 target_dn);
7540 if (is_in_same_nc) {
7542 * We allow the join.py code to point out that all
7543 * replication is completed, so failing now would just
7544 * trigger errors, rather than trigger a GET_TGT
7546 int *finished_full_join_ptr =
7547 talloc_get_type(ldb_get_opaque(ldb,
7548 DSDB_FULL_JOIN_REPLICATION_COMPLETED_OPAQUE_NAME),
7549 int);
7550 bool finished_full_join = finished_full_join_ptr && *finished_full_join_ptr;
7553 * if the target is already be up-to-date there's no point in
7554 * retrying. This could be due to bad timing, or if a target
7555 * on a one-way link was deleted. We ignore the link rather
7556 * than failing the replication cycle completely
7558 if (finished_full_join
7559 || dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7560 *ignore_link = true;
7561 DBG_WARNING("%s is %s "
7562 "but up to date. Ignoring link from %s\n",
7563 ldb_dn_get_linearized(target_dn), missing_str,
7564 ldb_dn_get_linearized(source_dn));
7565 return LDB_SUCCESS;
7568 /* otherwise fail the replication and retry with GET_TGT */
7569 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7570 missing_str,
7571 ldb_dn_get_linearized(target_dn),
7572 GUID_string(mem_ctx, guid),
7573 ldb_dn_get_linearized(source_dn));
7574 return LDB_ERR_NO_SUCH_OBJECT;
7578 * The target of the cross-partition link is missing. Continue
7579 * and try to at least add the forward-link. This isn't great,
7580 * but a partial link can be fixed by dbcheck, so it's better
7581 * than dropping the link completely.
7583 *ignore_link = false;
7585 if (is_obj_commit) {
7588 * Only log this when we're actually committing the objects.
7589 * This avoids spurious logs, i.e. if we're just verifying the
7590 * received link during a join.
7592 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7593 missing_str, ldb_dn_get_linearized(target_dn),
7594 ldb_dn_get_linearized(source_dn));
7597 return LDB_SUCCESS;
7601 * Checks that the target object for a linked attribute exists.
7602 * @param guid returns the target object's GUID (is returned)if it exists)
7603 * @param ignore_link set to true if the linked attribute should be ignored
7604 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7606 static int replmd_check_target_exists(struct ldb_module *module,
7607 struct dsdb_dn *dsdb_dn,
7608 struct la_entry *la_entry,
7609 struct ldb_dn *source_dn,
7610 bool is_obj_commit,
7611 struct GUID *guid,
7612 bool *ignore_link)
7614 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7615 struct ldb_context *ldb = ldb_module_get_ctx(module);
7616 struct ldb_result *target_res;
7617 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7618 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7619 NTSTATUS ntstatus;
7620 int ret;
7621 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7622 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7624 *ignore_link = false;
7625 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7627 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7630 * This strange behaviour (allowing a NULL/missing
7631 * GUID) originally comes from:
7633 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7634 * Author: Andrew Tridgell <tridge@samba.org>
7635 * Date: Mon Dec 21 21:21:55 2009 +1100
7637 * s4-drs: cope better with NULL GUIDS from DRS
7639 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7640 * need to match by DN if possible when seeing if we should update an
7641 * existing link.
7643 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7645 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7646 dsdb_dn->dn, attrs,
7647 DSDB_FLAG_NEXT_MODULE |
7648 DSDB_SEARCH_SHOW_RECYCLED |
7649 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7650 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7651 NULL);
7652 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7653 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7654 la->attid,
7655 ldb_dn_get_linearized(dsdb_dn->dn),
7656 ldb_dn_get_linearized(source_dn));
7657 talloc_free(tmp_ctx);
7658 return LDB_ERR_OPERATIONS_ERROR;
7659 } else {
7660 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7661 NULL, LDB_SCOPE_SUBTREE,
7662 attrs,
7663 DSDB_FLAG_NEXT_MODULE |
7664 DSDB_SEARCH_SHOW_RECYCLED |
7665 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7666 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7667 NULL,
7668 "objectGUID=%s",
7669 GUID_string(tmp_ctx, guid));
7672 if (ret != LDB_SUCCESS) {
7673 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7674 GUID_string(tmp_ctx, guid),
7675 ldb_errstring(ldb));
7676 talloc_free(tmp_ctx);
7677 return ret;
7680 if (target_res->count == 0) {
7683 * target object is unknown. Check whether to ignore the link,
7684 * fail the replication, or add a partial link
7686 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7687 source_dn, is_obj_commit, guid,
7688 la_entry->dsdb_repl_flags,
7689 ignore_link, "Unknown");
7691 } else if (target_res->count != 1) {
7692 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7693 GUID_string(tmp_ctx, guid));
7694 ret = LDB_ERR_OPERATIONS_ERROR;
7695 } else {
7696 struct ldb_message *target_msg = target_res->msgs[0];
7698 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7700 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7701 replmd_deletion_state(module, target_msg,
7702 &target_deletion_state, NULL);
7705 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7706 * ProcessLinkValue(). Link updates should not be sent for
7707 * recycled and tombstone objects (deleting the links should
7708 * happen when we delete the object). This probably means our
7709 * copy of the target object isn't up to date.
7711 if (target_deletion_state >= OBJECT_RECYCLED) {
7714 * target object is deleted. Check whether to ignore the
7715 * link, fail the replication, or add a partial link
7717 ret = replmd_allow_missing_target(module, tmp_ctx,
7718 dsdb_dn->dn, source_dn,
7719 is_obj_commit, guid,
7720 la_entry->dsdb_repl_flags,
7721 ignore_link, "Deleted");
7725 talloc_free(tmp_ctx);
7726 return ret;
7730 * Extracts the key details about the source object for a
7731 * linked-attribute entry.
7732 * This returns the following details:
7733 * @param ret_attr the schema details for the linked attribute
7734 * @param source_msg the search result for the source object
7736 static int replmd_get_la_entry_source(struct ldb_module *module,
7737 struct la_entry *la_entry,
7738 TALLOC_CTX *mem_ctx,
7739 const struct dsdb_attribute **ret_attr,
7740 struct ldb_message **source_msg)
7742 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7743 struct ldb_context *ldb = ldb_module_get_ctx(module);
7744 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7745 int ret;
7746 const struct dsdb_attribute *attr;
7747 struct ldb_result *res;
7748 const char *attrs[4];
7751 linked_attributes[0]:
7752 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7753 identifier : *
7754 identifier: struct drsuapi_DsReplicaObjectIdentifier
7755 __ndr_size : 0x0000003a (58)
7756 __ndr_size_sid : 0x00000000 (0)
7757 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7758 sid : S-0-0
7759 __ndr_size_dn : 0x00000000 (0)
7760 dn : ''
7761 attid : DRSUAPI_ATTID_member (0x1F)
7762 value: struct drsuapi_DsAttributeValue
7763 __ndr_size : 0x0000007e (126)
7764 blob : *
7765 blob : DATA_BLOB length=126
7766 flags : 0x00000001 (1)
7767 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7768 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7769 meta_data: struct drsuapi_DsReplicaMetaData
7770 version : 0x00000015 (21)
7771 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7772 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7773 originating_usn : 0x000000000001e19c (123292)
7775 (for cases where the link is to a normal DN)
7776 &target: struct drsuapi_DsReplicaObjectIdentifier3
7777 __ndr_size : 0x0000007e (126)
7778 __ndr_size_sid : 0x0000001c (28)
7779 guid : 7639e594-db75-4086-b0d4-67890ae46031
7780 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7781 __ndr_size_dn : 0x00000022 (34)
7782 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7785 /* find the attribute being modified */
7786 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7787 if (attr == NULL) {
7788 struct GUID_txt_buf guid_str;
7789 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7790 la->attid,
7791 GUID_buf_string(&la->identifier->guid,
7792 &guid_str));
7793 return LDB_ERR_OPERATIONS_ERROR;
7797 * All attributes listed here must be dealt with in some way
7798 * by replmd_process_linked_attribute() otherwise in the case
7799 * of isDeleted: FALSE the modify will fail with:
7801 * Failed to apply linked attribute change 'attribute 'isDeleted':
7802 * invalid modify flags on
7803 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7804 * 0x0'
7806 * This is becaue isDeleted is a Boolean, so FALSE is a
7807 * legitimate value (set by Samba's deletetest.py)
7809 attrs[0] = attr->lDAPDisplayName;
7810 attrs[1] = "isDeleted";
7811 attrs[2] = "isRecycled";
7812 attrs[3] = NULL;
7815 * get the existing message from the db for the object with
7816 * this GUID, returning attribute being modified. We will then
7817 * use this msg as the basis for a modify call
7819 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7820 DSDB_FLAG_NEXT_MODULE |
7821 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7822 DSDB_SEARCH_SHOW_RECYCLED |
7823 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7824 DSDB_SEARCH_REVEAL_INTERNALS,
7825 NULL,
7826 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7827 if (ret != LDB_SUCCESS) {
7828 return ret;
7830 if (res->count != 1) {
7831 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7832 GUID_string(mem_ctx, &la->identifier->guid));
7833 return LDB_ERR_NO_SUCH_OBJECT;
7836 *source_msg = res->msgs[0];
7837 *ret_attr = attr;
7839 return LDB_SUCCESS;
7843 * Verifies the target object is known for a linked attribute
7845 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7846 TALLOC_CTX *mem_ctx,
7847 struct la_entry *la_entry,
7848 struct ldb_dn *src_dn,
7849 const struct dsdb_attribute *attr)
7851 int ret = LDB_SUCCESS;
7852 struct ldb_module *module = ar->module;
7853 struct dsdb_dn *tgt_dsdb_dn = NULL;
7854 struct GUID guid = GUID_zero();
7855 bool dummy;
7856 WERROR status;
7857 struct ldb_context *ldb = ldb_module_get_ctx(module);
7858 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7859 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7861 /* the value blob for the attribute holds the target object DN */
7862 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7863 la->value.blob, &tgt_dsdb_dn);
7864 if (!W_ERROR_IS_OK(status)) {
7865 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7866 attr->lDAPDisplayName,
7867 ldb_dn_get_linearized(src_dn),
7868 win_errstr(status));
7869 return LDB_ERR_OPERATIONS_ERROR;
7873 * We can skip the target object checks if we're only syncing critical
7874 * objects, or we know the target is up-to-date. If either case, we
7875 * still continue even if the target doesn't exist
7877 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7878 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7880 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7881 src_dn, false, &guid, &dummy);
7885 * When we fail to find the target object, the error code we pass
7886 * back here is really important. It flags back to the callers to
7887 * retry this request with DRSUAPI_DRS_GET_TGT
7889 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7890 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7893 return ret;
7897 * Finds the current active Parsed-DN value for a single-valued linked
7898 * attribute, if one exists.
7899 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7900 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7901 * an error occurred
7903 static int replmd_get_active_singleval_link(struct ldb_module *module,
7904 TALLOC_CTX *mem_ctx,
7905 struct parsed_dn pdn_list[],
7906 unsigned int count,
7907 const struct dsdb_attribute *attr,
7908 struct parsed_dn **ret_pdn)
7910 unsigned int i;
7912 *ret_pdn = NULL;
7914 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7916 /* nothing to do for multi-valued linked attributes */
7917 return LDB_SUCCESS;
7920 for (i = 0; i < count; i++) {
7921 int ret = LDB_SUCCESS;
7922 struct parsed_dn *pdn = &pdn_list[i];
7924 /* skip any inactive links */
7925 if (dsdb_dn_is_deleted_val(pdn->v)) {
7926 continue;
7929 /* we've found an active value for this attribute */
7930 *ret_pdn = pdn;
7932 if (pdn->dsdb_dn == NULL) {
7933 struct ldb_context *ldb = ldb_module_get_ctx(module);
7935 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7936 attr->syntax->ldap_oid);
7939 return ret;
7942 /* no active link found */
7943 return LDB_SUCCESS;
7947 * @returns true if the replication linked attribute info is newer than we
7948 * already have in our DB
7949 * @param pdn the existing linked attribute info in our DB
7950 * @param la the new linked attribute info received during replication
7952 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7953 struct drsuapi_DsReplicaLinkedAttribute *la)
7955 /* see if this update is newer than what we have already */
7956 struct GUID invocation_id = GUID_zero();
7957 uint32_t version = 0;
7958 NTTIME change_time = 0;
7960 if (pdn == NULL) {
7962 /* no existing info so update is newer */
7963 return true;
7966 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7967 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7968 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7970 return replmd_update_is_newer(&invocation_id,
7971 &la->meta_data.originating_invocation_id,
7972 version,
7973 la->meta_data.version,
7974 change_time,
7975 la->meta_data.originating_change_time);
7979 * Marks an existing linked attribute value as deleted in the DB
7980 * @param pdn the parsed-DN of the target-value to delete
7982 static int replmd_delete_link_value(struct ldb_module *module,
7983 struct replmd_private *replmd_private,
7984 TALLOC_CTX *mem_ctx,
7985 struct ldb_dn *src_obj_dn,
7986 const struct dsdb_schema *schema,
7987 const struct dsdb_attribute *attr,
7988 uint64_t seq_num,
7989 bool is_active,
7990 struct GUID *target_guid,
7991 struct dsdb_dn *target_dsdb_dn,
7992 struct ldb_val *output_val)
7994 struct ldb_context *ldb = ldb_module_get_ctx(module);
7995 time_t t;
7996 NTTIME now;
7997 const struct GUID *invocation_id = NULL;
7998 int ret;
8000 t = time(NULL);
8001 unix_to_nt_time(&now, t);
8003 invocation_id = samdb_ntds_invocation_id(ldb);
8004 if (invocation_id == NULL) {
8005 return LDB_ERR_OPERATIONS_ERROR;
8008 /* if the existing link is active, remove its backlink */
8009 if (is_active) {
8012 * NOTE WELL: After this we will never (at runtime) be
8013 * able to find this forward link (for instant
8014 * removal) if/when the link target is deleted.
8016 * We have dbcheck rules to cover this and cope otherwise
8017 * by filtering at runtime (i.e. in the extended_dn module).
8019 ret = replmd_add_backlink(module, replmd_private, schema,
8020 src_obj_dn, target_guid, false,
8021 attr, NULL);
8022 if (ret != LDB_SUCCESS) {
8023 return ret;
8027 /* mark the existing value as deleted */
8028 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
8029 target_dsdb_dn, invocation_id, seq_num,
8030 seq_num, now, true);
8031 return ret;
8035 * Checks for a conflict in single-valued link attributes, and tries to
8036 * resolve the problem if possible.
8038 * Single-valued links should only ever have one active value. If we already
8039 * have an active link value, and during replication we receive an active link
8040 * value for a different target DN, then we need to resolve this inconsistency
8041 * and determine which value should be active. If the received info is better/
8042 * newer than the existing link attribute, then we need to set our existing
8043 * link as deleted. If the received info is worse/older, then we should continue
8044 * to add it, but set it as an inactive link.
8046 * Note that this is a corner-case that is unlikely to happen (but if it does
8047 * happen, we don't want it to break replication completely).
8049 * @param pdn_being_modified the parsed DN corresponding to the received link
8050 * target (note this is NULL if the link does not already exist in our DB)
8051 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
8052 * any existing active or inactive values for the attribute in our DB.
8053 * @param dsdb_dn the target DN for the received link attribute
8054 * @param add_as_inactive gets set to true if the received link is worse than
8055 * the existing link - it should still be added, but as an inactive link.
8057 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
8058 struct replmd_private *replmd_private,
8059 TALLOC_CTX *mem_ctx,
8060 struct ldb_dn *src_obj_dn,
8061 struct drsuapi_DsReplicaLinkedAttribute *la,
8062 struct dsdb_dn *dsdb_dn,
8063 struct parsed_dn *pdn_being_modified,
8064 struct parsed_dn *pdn_list,
8065 struct ldb_message_element *old_el,
8066 const struct dsdb_schema *schema,
8067 const struct dsdb_attribute *attr,
8068 uint64_t seq_num,
8069 bool *add_as_inactive)
8071 struct parsed_dn *active_pdn = NULL;
8072 bool update_is_newer = false;
8073 int ret;
8076 * check if there's a conflict for single-valued links, i.e. an active
8077 * linked attribute already exists, but it has a different target value
8079 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
8080 old_el->num_values, attr,
8081 &active_pdn);
8083 if (ret != LDB_SUCCESS) {
8084 return ret;
8088 * If no active value exists (or the received info is for the currently
8089 * active value), then no conflict exists
8091 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
8092 return LDB_SUCCESS;
8095 DBG_WARNING("Link conflict for %s attribute on %s\n",
8096 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
8098 /* Work out how to resolve the conflict based on which info is better */
8099 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
8101 if (update_is_newer) {
8102 DBG_WARNING("Using received value %s, over existing target %s\n",
8103 ldb_dn_get_linearized(dsdb_dn->dn),
8104 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
8107 * Delete our existing active link. The received info will then
8108 * be added (through normal link processing) as the active value
8110 ret = replmd_delete_link_value(module, replmd_private, old_el,
8111 src_obj_dn, schema, attr,
8112 seq_num, true, &active_pdn->guid,
8113 active_pdn->dsdb_dn,
8114 active_pdn->v);
8116 if (ret != LDB_SUCCESS) {
8117 return ret;
8119 } else {
8120 DBG_WARNING("Using existing target %s, over received value %s\n",
8121 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
8122 ldb_dn_get_linearized(dsdb_dn->dn));
8125 * we want to keep our existing active link and add the
8126 * received link as inactive
8128 *add_as_inactive = true;
8131 return LDB_SUCCESS;
8135 * Processes one linked attribute received via replication.
8136 * @param src_dn the DN of the source object for the link
8137 * @param attr schema info for the linked attribute
8138 * @param la_entry the linked attribute info received via DRS
8139 * @param element_ctx mem context for msg->element[] (when adding a new value
8140 * we need to realloc old_el->values)
8141 * @param old_el the corresponding msg->element[] for the linked attribute
8142 * @param pdn_list a (binary-searchable) parsed DN array for the existing link
8143 * values in the msg. E.g. for a group, this is the existing members.
8144 * @param change what got modified: either nothing, an existing link value was
8145 * modified, or a new link value was added.
8146 * @returns LDB_SUCCESS if OK, an error otherwise
8148 static int replmd_process_linked_attribute(struct ldb_module *module,
8149 TALLOC_CTX *mem_ctx,
8150 struct replmd_private *replmd_private,
8151 struct ldb_dn *src_dn,
8152 const struct dsdb_attribute *attr,
8153 struct la_entry *la_entry,
8154 struct ldb_request *parent,
8155 TALLOC_CTX *element_ctx,
8156 struct ldb_message_element *old_el,
8157 struct parsed_dn *pdn_list,
8158 replmd_link_changed *change)
8160 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
8161 struct ldb_context *ldb = ldb_module_get_ctx(module);
8162 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
8163 int ret;
8164 struct dsdb_dn *dsdb_dn = NULL;
8165 uint64_t seq_num = 0;
8166 struct parsed_dn *pdn, *next;
8167 struct GUID guid = GUID_zero();
8168 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
8169 bool ignore_link;
8170 struct dsdb_dn *old_dsdb_dn = NULL;
8171 struct ldb_val *val_to_update = NULL;
8172 bool add_as_inactive = false;
8173 WERROR status;
8175 *change = LINK_CHANGE_NONE;
8177 /* the value blob for the attribute holds the target object DN */
8178 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
8179 la->value.blob, &dsdb_dn);
8180 if (!W_ERROR_IS_OK(status)) {
8181 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8182 attr->lDAPDisplayName,
8183 ldb_dn_get_linearized(src_dn),
8184 win_errstr(status));
8185 return LDB_ERR_OPERATIONS_ERROR;
8188 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
8189 true, &guid, &ignore_link);
8191 if (ret != LDB_SUCCESS) {
8192 return ret;
8196 * there are some cases where the target object doesn't exist, but it's
8197 * OK to ignore the linked attribute
8199 if (ignore_link) {
8200 return ret;
8203 /* see if this link already exists */
8204 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8205 &guid,
8206 dsdb_dn->dn,
8207 dsdb_dn->extra_part, 0,
8208 &pdn, &next,
8209 attr->syntax->ldap_oid,
8210 true);
8211 if (ret != LDB_SUCCESS) {
8212 return ret;
8215 if (!replmd_link_update_is_newer(pdn, la)) {
8216 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8217 old_el->name, ldb_dn_get_linearized(src_dn),
8218 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8219 return LDB_SUCCESS;
8222 /* get a seq_num for this change */
8223 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8224 if (ret != LDB_SUCCESS) {
8225 return ret;
8229 * check for single-valued link conflicts, i.e. an active linked
8230 * attribute already exists, but it has a different target value
8232 if (active) {
8233 ret = replmd_check_singleval_la_conflict(module, replmd_private,
8234 mem_ctx, src_dn, la,
8235 dsdb_dn, pdn, pdn_list,
8236 old_el, schema, attr,
8237 seq_num,
8238 &add_as_inactive);
8239 if (ret != LDB_SUCCESS) {
8240 return ret;
8244 if (pdn != NULL) {
8245 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8247 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8248 /* remove the existing backlink */
8249 ret = replmd_add_backlink(module, replmd_private,
8250 schema,
8251 src_dn,
8252 &pdn->guid, false, attr,
8253 parent);
8254 if (ret != LDB_SUCCESS) {
8255 return ret;
8259 val_to_update = pdn->v;
8260 old_dsdb_dn = pdn->dsdb_dn;
8261 *change = LINK_CHANGE_MODIFIED;
8263 } else {
8264 unsigned offset;
8267 * We know where the new one needs to be, from the *next
8268 * pointer into pdn_list.
8270 if (next == NULL) {
8271 offset = old_el->num_values;
8272 } else {
8273 if (next->dsdb_dn == NULL) {
8274 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8275 attr->syntax->ldap_oid);
8276 if (ret != LDB_SUCCESS) {
8277 return ret;
8280 offset = next - pdn_list;
8281 if (offset > old_el->num_values) {
8282 return LDB_ERR_OPERATIONS_ERROR;
8286 old_el->values = talloc_realloc(element_ctx, old_el->values,
8287 struct ldb_val, old_el->num_values+1);
8288 if (!old_el->values) {
8289 ldb_module_oom(module);
8290 return LDB_ERR_OPERATIONS_ERROR;
8293 if (offset != old_el->num_values) {
8294 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8295 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8298 old_el->num_values++;
8300 val_to_update = &old_el->values[offset];
8301 old_dsdb_dn = NULL;
8302 *change = LINK_CHANGE_ADDED;
8305 /* set the link attribute's value to the info that was received */
8306 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8307 &la->meta_data.originating_invocation_id,
8308 la->meta_data.originating_usn, seq_num,
8309 la->meta_data.originating_change_time,
8310 la->meta_data.version,
8311 !active);
8312 if (ret != LDB_SUCCESS) {
8313 return ret;
8316 if (add_as_inactive) {
8318 /* Set the new link as inactive/deleted to avoid conflicts */
8319 ret = replmd_delete_link_value(module, replmd_private, old_el,
8320 src_dn, schema, attr, seq_num,
8321 false, &guid, dsdb_dn,
8322 val_to_update);
8324 if (ret != LDB_SUCCESS) {
8325 return ret;
8328 } else if (active) {
8330 /* if the new link is active, then add the new backlink */
8331 ret = replmd_add_backlink(module, replmd_private,
8332 schema,
8333 src_dn,
8334 &guid, true, attr,
8335 parent);
8336 if (ret != LDB_SUCCESS) {
8337 return ret;
8341 ret = dsdb_check_single_valued_link(attr, old_el);
8342 if (ret != LDB_SUCCESS) {
8343 return ret;
8346 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8348 return ret;
8351 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8353 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8354 return replmd_extended_replicated_objects(module, req);
8357 return ldb_next_request(module, req);
8362 we hook into the transaction operations to allow us to
8363 perform the linked attribute updates at the end of the whole
8364 transaction. This allows a forward linked attribute to be created
8365 before the object is created. During a vampire, w2k8 sends us linked
8366 attributes before the objects they are part of.
8368 static int replmd_start_transaction(struct ldb_module *module)
8370 /* create our private structure for this transaction */
8371 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8372 struct replmd_private);
8373 replmd_txn_cleanup(replmd_private);
8375 /* free any leftover mod_usn records from cancelled
8376 transactions */
8377 while (replmd_private->ncs) {
8378 struct nc_entry *e = replmd_private->ncs;
8379 DLIST_REMOVE(replmd_private->ncs, e);
8380 talloc_free(e);
8383 replmd_private->originating_updates = false;
8385 return ldb_next_start_trans(module);
8389 * Processes a group of linked attributes that apply to the same source-object
8390 * and attribute-ID (and were received in the same replication chunk).
8392 static int replmd_process_la_group(struct ldb_module *module,
8393 struct replmd_private *replmd_private,
8394 struct la_group *la_group)
8396 struct la_entry *la = NULL;
8397 struct la_entry *prev = NULL;
8398 int ret;
8399 TALLOC_CTX *tmp_ctx = NULL;
8400 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8401 struct ldb_message *msg = NULL;
8402 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8403 struct ldb_context *ldb = ldb_module_get_ctx(module);
8404 const struct dsdb_attribute *attr = NULL;
8405 struct ldb_message_element *old_el = NULL;
8406 struct parsed_dn *pdn_list = NULL;
8407 replmd_link_changed change_type;
8408 uint32_t num_changes = 0;
8409 time_t t;
8410 uint64_t seq_num = 0;
8412 tmp_ctx = talloc_new(la_group);
8413 if (tmp_ctx == NULL) {
8414 return ldb_oom(ldb);
8418 * get the attribute being modified and the search result for the
8419 * source object
8421 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8422 &msg);
8424 if (ret != LDB_SUCCESS) {
8425 return ret;
8429 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8430 * ProcessLinkValue, because link updates are not applied to
8431 * recycled and tombstone objects. We don't have to delete
8432 * any existing link, that should have happened when the
8433 * object deletion was replicated or initiated.
8435 * This needs isDeleted and isRecycled to be included as
8436 * attributes in the search and so in msg if set.
8438 replmd_deletion_state(module, msg, &deletion_state, NULL);
8440 if (deletion_state >= OBJECT_RECYCLED) {
8441 TALLOC_FREE(tmp_ctx);
8442 return LDB_SUCCESS;
8446 * Now that we know the deletion_state, remove the extra
8447 * attributes added for that purpose. We need to do this
8448 * otherwise in the case of isDeleted: FALSE the modify will
8449 * fail with:
8451 * Failed to apply linked attribute change 'attribute 'isDeleted':
8452 * invalid modify flags on
8453 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8454 * 0x0'
8456 * This is becaue isDeleted is a Boolean, so FALSE is a
8457 * legitimate value (set by Samba's deletetest.py)
8459 ldb_msg_remove_attr(msg, "isDeleted");
8460 ldb_msg_remove_attr(msg, "isRecycled");
8462 /* get the msg->element[] for the link attribute being processed */
8463 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8464 if (old_el == NULL) {
8465 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
8466 LDB_FLAG_MOD_REPLACE, &old_el);
8467 if (ret != LDB_SUCCESS) {
8468 ldb_module_oom(module);
8469 return LDB_ERR_OPERATIONS_ERROR;
8471 } else {
8472 old_el->flags = LDB_FLAG_MOD_REPLACE;
8476 * go through and process the link target value(s) for this particular
8477 * source object and attribute. For optimization, the same msg is used
8478 * across multiple calls to replmd_process_linked_attribute().
8479 * Note that we should not add or remove any msg attributes inside the
8480 * loop (we should only add/modify *values* for the attribute being
8481 * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8482 * pointers will be invalidated
8484 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8485 prev = DLIST_PREV(la);
8486 DLIST_REMOVE(la_group->la_entries, la);
8489 * parse the existing links (this can be costly for a large
8490 * group, so we try to minimize the times we do it)
8492 if (pdn_list == NULL) {
8493 ret = get_parsed_dns_trusted_fallback(module,
8494 replmd_private,
8495 tmp_ctx, old_el,
8496 &pdn_list,
8497 attr->syntax->ldap_oid,
8498 NULL);
8500 if (ret != LDB_SUCCESS) {
8501 return ret;
8504 ret = replmd_process_linked_attribute(module, tmp_ctx,
8505 replmd_private,
8506 msg->dn, attr, la, NULL,
8507 msg->elements, old_el,
8508 pdn_list, &change_type);
8509 if (ret != LDB_SUCCESS) {
8510 replmd_txn_cleanup(replmd_private);
8511 return ret;
8515 * Adding a link reallocs memory, and so invalidates all the
8516 * pointers in pdn_list. Reparse the PDNs on the next loop
8518 if (change_type == LINK_CHANGE_ADDED) {
8519 TALLOC_FREE(pdn_list);
8522 if (change_type != LINK_CHANGE_NONE) {
8523 num_changes++;
8526 if ((++replmd_private->num_processed % 8192) == 0) {
8527 DBG_NOTICE("Processed %u/%u linked attributes\n",
8528 replmd_private->num_processed,
8529 replmd_private->total_links);
8534 * it's possible we're already up-to-date and so don't need to modify
8535 * the object at all (e.g. doing a 'drs replicate --full-sync')
8537 if (num_changes == 0) {
8538 TALLOC_FREE(tmp_ctx);
8539 return LDB_SUCCESS;
8543 * Note that adding the whenChanged/etc attributes below will realloc
8544 * msg->elements, invalidating the existing element/parsed-DN pointers
8546 old_el = NULL;
8547 TALLOC_FREE(pdn_list);
8549 /* update whenChanged/uSNChanged as the object has changed */
8550 t = time(NULL);
8551 ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
8552 &seq_num);
8553 if (ret != LDB_SUCCESS) {
8554 return ret;
8557 ret = add_time_element(msg, "whenChanged", t);
8558 if (ret != LDB_SUCCESS) {
8559 ldb_operr(ldb);
8560 return ret;
8563 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8564 if (ret != LDB_SUCCESS) {
8565 ldb_operr(ldb);
8566 return ret;
8569 /* apply the link changes to the source object */
8570 ret = linked_attr_modify(module, msg, NULL);
8571 if (ret != LDB_SUCCESS) {
8572 ldb_debug(ldb, LDB_DEBUG_WARNING,
8573 "Failed to apply linked attribute change "
8574 "Error: '%s' DN: '%s' Attribute: '%s'\n",
8575 ldb_errstring(ldb),
8576 ldb_dn_get_linearized(msg->dn),
8577 attr->lDAPDisplayName);
8578 TALLOC_FREE(tmp_ctx);
8579 return ret;
8582 TALLOC_FREE(tmp_ctx);
8583 return LDB_SUCCESS;
8587 on prepare commit we loop over our queued la_context structures and
8588 apply each of them
8590 static int replmd_prepare_commit(struct ldb_module *module)
8592 struct replmd_private *replmd_private =
8593 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8594 struct la_group *la_group, *prev;
8595 int ret;
8597 if (replmd_private->la_list != NULL) {
8598 DBG_NOTICE("Processing linked attributes\n");
8602 * Walk the list of linked attributes from DRS replication.
8604 * We walk backwards, to do the first entry first, as we
8605 * added the entries with DLIST_ADD() which puts them at the
8606 * start of the list
8608 * Links are grouped together so we process links for the same
8609 * source object in one go.
8611 for (la_group = DLIST_TAIL(replmd_private->la_list);
8612 la_group != NULL;
8613 la_group = prev) {
8615 prev = DLIST_PREV(la_group);
8616 DLIST_REMOVE(replmd_private->la_list, la_group);
8617 ret = replmd_process_la_group(module, replmd_private,
8618 la_group);
8619 if (ret != LDB_SUCCESS) {
8620 replmd_txn_cleanup(replmd_private);
8621 return ret;
8625 replmd_txn_cleanup(replmd_private);
8627 /* possibly change @REPLCHANGED */
8628 ret = replmd_notify_store(module, NULL);
8629 if (ret != LDB_SUCCESS) {
8630 return ret;
8633 return ldb_next_prepare_commit(module);
8636 static int replmd_del_transaction(struct ldb_module *module)
8638 struct replmd_private *replmd_private =
8639 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8640 replmd_txn_cleanup(replmd_private);
8642 return ldb_next_del_trans(module);
8646 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8647 .name = "repl_meta_data",
8648 .init_context = replmd_init,
8649 .add = replmd_add,
8650 .modify = replmd_modify,
8651 .rename = replmd_rename,
8652 .del = replmd_delete,
8653 .extended = replmd_extended,
8654 .start_transaction = replmd_start_transaction,
8655 .prepare_commit = replmd_prepare_commit,
8656 .del_transaction = replmd_del_transaction,
8659 int ldb_repl_meta_data_module_init(const char *version)
8661 LDB_MODULE_CHECK_VERSION(version);
8662 return ldb_register_module(&ldb_repl_meta_data_module_ops);