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/>.
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
35 * Author: Stefan Metzmacher
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"
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 {
71 struct la_group *la_list;
73 struct nc_entry *prev, *next;
76 uint64_t mod_usn_urgent;
78 struct ldb_dn *schema_dn;
79 bool originating_updates;
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).
97 struct la_group *next, *prev;
98 struct la_entry *la_entries;
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
122 struct la_backlink *la_backlinks;
124 /* details for the mode where we apply a bunch of inbound replication meessages */
126 uint32_t index_current;
127 struct dsdb_extended_replicated_objects *objs;
129 struct ldb_message *search_msg;
130 struct GUID local_parent_guid;
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).
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,
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,
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,
175 const char *rdn_name,
176 const struct ldb_val *rdn_value,
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,
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) {
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;
225 *current_state = OBJECT_REMOVED;
226 if (next_state != NULL) {
227 *next_state = OBJECT_REMOVED;
232 enabled = replmd_recyclebin_enabled(module);
234 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
236 *current_state = OBJECT_TOMBSTONE;
237 if (next_state != NULL) {
238 *next_state = OBJECT_REMOVED;
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;
251 *current_state = OBJECT_DELETED;
252 if (next_state != NULL) {
253 *next_state = OBJECT_RECYCLED;
258 *current_state = OBJECT_NOT_DELETED;
259 if (next_state == NULL) {
264 *next_state = OBJECT_DELETED;
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)},
283 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
284 static const char *urgent_attrs[] = {
287 "userAccountControl",
292 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
293 enum urgent_situation situation)
296 for (i=0; urgent_objects[i].update_name; i++) {
298 if ((situation & urgent_objects[i].repl_situation) == 0) {
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) {
312 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
314 if (ldb_attr_in_list(urgent_attrs, el->name)) {
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);
333 replmd_private = talloc_zero(module, struct replmd_private);
334 if (replmd_private == NULL) {
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);
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;
365 struct la_backlink *next, *prev;
366 const char *attr_name;
367 struct ldb_dn *forward_dn;
368 struct GUID target_guid;
373 a ldb_modify request operating on modules below the
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;
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);
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,
396 ldb_modify_default_callback,
398 LDB_REQ_SET_LOCATION(mod_req);
399 if (ret != LDB_SUCCESS) {
400 talloc_free(tmp_ctx);
404 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
406 if (ret != LDB_SUCCESS) {
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);
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;
429 struct ldb_context *ldb = ldb_module_get_ctx(module);
430 struct ldb_message *msg;
431 TALLOC_CTX *frame = talloc_stackframe();
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");
450 msg = ldb_msg_new(frame);
452 ldb_module_oom(module);
454 return LDB_ERR_OPERATIONS_ERROR;
457 source_dn = ldb_dn_copy(frame, bl->forward_dn);
459 ldb_module_oom(module);
461 return LDB_ERR_OPERATIONS_ERROR;
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 */
470 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
472 ldb_module_oom(module);
474 return LDB_ERR_OPERATIONS_ERROR;
476 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
477 if (ret != LDB_SUCCESS) {
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)));
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),
514 add a backlink to the list of backlinks to add/delete in the prepare
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);
533 ldb_module_oom(module);
534 return LDB_ERR_OPERATIONS_ERROR;
537 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
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
548 bl->attr_name = target_attr->lDAPDisplayName;
549 bl->forward_dn = talloc_steal(bl, forward_dn);
550 bl->target_guid = *target_guid;
553 DLIST_ADD(ac->la_backlinks, bl);
559 add a backlink to the list of backlinks to add/delete in the prepare
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;
574 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
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
585 bl.attr_name = target_attr->lDAPDisplayName;
586 bl.forward_dn = forward_dn;
587 bl.target_guid = *target_guid;
590 ret = replmd_process_backlink(module, &bl, parent);
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)
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,
628 if (ares->error != LDB_SUCCESS) {
629 struct GUID_txt_buf guid_txt;
630 struct ldb_message *msg = 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),
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,
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
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,
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) {
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;
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);
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) {
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)));
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,
778 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
780 DSDB_FLAG_NEXT_MODULE,
782 if (ret != LDB_SUCCESS) {
785 talloc_free(ext_res);
788 DLIST_REMOVE(replmd_private->ncs, modified_partition);
789 talloc_free(modified_partition);
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);
817 ac->schema = dsdb_get_schema(ldb, ac);
819 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
820 "replmd_modify: no dsdb_schema loaded");
821 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
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");
834 ac->our_invocation_id = *our_invocation_id;
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;
848 if (ldb_msg_find_element(msg, attr) != NULL) {
852 s = ldb_timestring(msg, t);
854 return LDB_ERR_OPERATIONS_ERROR;
857 ret = ldb_msg_add_string(msg, attr, s);
858 if (ret != LDB_SUCCESS) {
862 el = ldb_msg_find_element(msg, attr);
863 /* always set as replace. This works because on add ops, the flag
865 el->flags = LDB_FLAG_MOD_REPLACE;
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;
879 if (ldb_msg_find_element(msg, attr) != NULL) {
883 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
884 if (ret != LDB_SUCCESS) {
888 el = ldb_msg_find_element(msg, attr);
889 /* always set as replace. This works because on add ops, the flag
891 el->flags = LDB_FLAG_MOD_REPLACE;
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) {
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
917 return attid_1 > attid_2 ? 1 : -1;
920 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
921 struct replPropertyMetaDataCtr1 *ctr1,
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;
942 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
943 struct replPropertyMetaDataCtr1 *ctr1,
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
972 return strcasecmp(e1->name, e2->name);
974 if (a1->attributeID_id == a2->attributeID_id) {
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,
1010 struct ldb_dn *forward_dn,
1011 const struct dsdb_attribute *sa,
1012 struct ldb_request *parent)
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;
1023 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1024 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1026 ldb_asprintf_errstring(ldb,
1027 "Attribute %s is single valued but "
1028 "more than one value has been supplied",
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,
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);
1059 ret = check_parsed_dn_duplicates(module, el, pdn);
1060 if (ret != LDB_SUCCESS) {
1061 talloc_free(tmp_ctx);
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,
1077 if (ret != LDB_SUCCESS) {
1078 talloc_free(tmp_ctx);
1082 ret = replmd_defer_add_backlink(module, replmd_private,
1084 forward_dn, &p->guid, true, sa,
1086 if (ret != LDB_SUCCESS) {
1087 talloc_free(tmp_ctx);
1091 new_values[i] = *p->v;
1093 el->values = talloc_steal(mem_ctx, new_values);
1095 talloc_free(tmp_ctx);
1099 static int replmd_add_make_extended_dn(struct ldb_request *req,
1100 const DATA_BLOB *guid_blob,
1101 struct ldb_dn **_extended_dn)
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);
1108 return LDB_ERR_OPERATIONS_ERROR;
1110 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1111 if (ret != LDB_SUCCESS) {
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) {
1122 *_extended_dn = extended_dn;
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;
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);
1155 unsigned int functional_level;
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);
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;
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;
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);
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) {
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);
1229 return LDB_ERR_OPERATIONS_ERROR;
1232 /* generated times */
1233 unix_to_nt_time(&now, t);
1234 time_str = ldb_timestring(msg, t);
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) {
1263 /* build the replication meta_data */
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) {
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] == '@') {
1288 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1290 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1291 "replmd_add: attribute '%s' not defined in schema\n",
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
1305 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1306 if (extended_dn == NULL) {
1307 ret = replmd_add_make_extended_dn(req,
1310 if (ret != LDB_SUCCESS) {
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,
1328 if (ret != LDB_SUCCESS) {
1332 /* linked attributes are not stored in
1333 replPropertyMetaData in FL above w2k */
1338 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1340 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1341 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1344 if (rdn_val == NULL) {
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;
1358 m->originating_change_time = now;
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;
1368 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1373 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1375 if (e->num_values != 0) {
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));
1396 /* generated NDR encoded values */
1397 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1399 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1400 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
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) {
1415 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1416 if (ret != LDB_SUCCESS) {
1421 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1422 if (ret != LDB_SUCCESS) {
1427 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1428 if (ret != LDB_SUCCESS) {
1433 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1434 if (ret != LDB_SUCCESS) {
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));
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,
1463 ac, replmd_op_callback,
1466 LDB_REQ_SET_LOCATION(down_req);
1467 if (ret != LDB_SUCCESS) {
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,
1477 if (ret != LDB_SUCCESS) {
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) {
1491 /* mark the relax control done */
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,
1510 const struct GUID *our_invocation_id,
1513 bool is_forced_rodc,
1514 struct ldb_request *req)
1517 const struct dsdb_attribute *a;
1518 struct replPropertyMetaData1 *md1;
1519 bool may_skip = false;
1522 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
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 */
1530 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
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)) {
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) {
1560 } else if (old_el == NULL && el->num_values == 0) {
1561 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1563 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
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
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.
1580 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1582 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
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
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
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) {
1610 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
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;
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) {
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];
1653 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1654 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1657 if (rdn_val == NULL) {
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;
1670 md1->originating_change_time = now;
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 */
1688 * Bump the replPropertyMetaData version on an attribute, and if it
1689 * has changed (or forced by leaving rdn_old NULL), update the value
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,
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 :
1713 struct ldb_message_element new_el = {
1714 .flags = LDB_FLAG_MOD_REPLACE,
1717 .values = discard_const_p(struct ldb_val, rdn_new)
1719 struct ldb_message_element old_el = {
1720 .flags = LDB_FLAG_MOD_REPLACE,
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,
1741 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1743 uint32_t count = omd.ctr.ctr1.count;
1746 for (i=0; i < count; i++) {
1747 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1748 if (max < m.local_usn) {
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;
1773 const struct GUID *our_invocation_id;
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;
1787 attrs = rename_attrs;
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"));
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"));
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;
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;
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
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");
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) {
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;
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) {
1918 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
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,
1950 if (ret != LDB_SUCCESS) {
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)) {
1963 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1965 if (el->num_values != 0) {
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,
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
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;
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) {
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));
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)));
2048 el->values = md_value;
2054 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2056 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2058 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2059 &pdn2->dsdb_dn->extra_part);
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)
2073 bool values_are_sorted = true;
2074 struct ldb_context *ldb = ldb_module_get_ctx(module);
2081 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
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];
2091 struct parsed_dn *p;
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,
2111 ldb_asprintf_errstring(ldb,
2112 "Unable to find GUID for DN %s\n",
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;
2121 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2122 if (ret != LDB_SUCCESS) {
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]);
2131 values_are_sorted = false;
2134 /* keep a pointer to the original ldb_val */
2137 if (! values_are_sorted) {
2138 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
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)
2166 if (!replmd_private->sorted_links) {
2167 /* We need to sort the list. This is the slow old path we want
2170 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2172 if (ret != LDB_SUCCESS) {
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,
2197 if (ret != 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)
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",
2225 if (ldb_attr_cmp(el->name, "member") == 0) {
2226 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2228 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
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,
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)
2270 const struct GUID *invocation_id = NULL;
2271 for (i=0; i<count; i++) {
2275 if (dns[i].dsdb_dn == NULL) {
2276 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
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
2297 DEBUG(0, ("Mixed w2k and fixed format "
2298 "linked attributes\n"));
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) {
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);
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;
2348 struct ldb_val usnv, local_usnv;
2349 struct ldb_val vers, flagsv;
2350 const struct ldb_val *old_addtime = NULL;
2353 const char *dnstring;
2355 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2357 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
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);
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);
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,
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);
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,
2440 uint32_t old_version;
2441 uint32_t version = RMD_VERSION_INITIAL;
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,
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,
2470 struct ldb_dn *msg_dn,
2471 struct ldb_request *parent)
2474 struct parsed_dn *dns, *old_dns;
2475 TALLOC_CTX *tmp_ctx = talloc_new(msg);
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);
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
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);
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,
2504 if (ret != LDB_SUCCESS) {
2505 talloc_free(tmp_ctx);
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
2532 for (i = 0; i < el->num_values; i++) {
2533 struct parsed_dn *exact;
2534 struct parsed_dn *next;
2536 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2539 dns[i].dsdb_dn->extra_part, 0,
2541 schema_attr->syntax->ldap_oid,
2543 if (err != LDB_SUCCESS) {
2544 talloc_free(tmp_ctx);
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;
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);
2576 talloc_free(tmp_ctx);
2577 return ldb_operr(ldb);
2580 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2582 talloc_free(tmp_ctx);
2583 return ldb_operr(ldb);
2586 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
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",
2596 GUID_buf_string(&exact->guid,
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;
2604 talloc_free(tmp_ctx);
2605 return ldb_operr(ldb);
2608 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
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",
2616 GUID_buf_string(&exact->guid,
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);
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);
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
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",
2669 GUID_buf_string(&exact->guid,
2671 talloc_free(tmp_ctx);
2672 /* error codes for 'member' need to be
2674 if (ldb_attr_cmp(el->name, "member") == 0) {
2675 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2677 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2681 ret = replmd_update_la_val(new_values, exact->v,
2684 &ac->our_invocation_id,
2685 ac->seq_num, ac->seq_num,
2687 if (ret != LDB_SUCCESS) {
2688 talloc_free(tmp_ctx);
2692 ret = replmd_add_backlink(module, replmd_private,
2699 if (ret != LDB_SUCCESS) {
2700 talloc_free(tmp_ctx);
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
2715 offset = old_num_values;
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) {
2725 offset = MIN(next - old_dns, old_num_values);
2728 /* put all the old ones before next on the list */
2729 for (; j < offset; j++) {
2730 new_values[num_values] = *old_dns[j].v;
2734 ret = replmd_add_backlink(module, replmd_private,
2739 /* Make the new linked attribute ldb_val. */
2740 ret = replmd_build_la_val(new_values, &new_values[num_values],
2741 dns[i].dsdb_dn, &ac->our_invocation_id,
2743 if (ret != LDB_SUCCESS) {
2744 talloc_free(tmp_ctx);
2748 if (ret != LDB_SUCCESS) {
2749 talloc_free(tmp_ctx);
2753 /* copy the rest of the old ones (if any) */
2754 for (; j < old_num_values; j++) {
2755 new_values[num_values] = *old_dns[j].v;
2759 talloc_steal(msg->elements, new_values);
2760 if (old_el != NULL) {
2761 talloc_steal(msg->elements, old_el->values);
2763 el->values = new_values;
2764 el->num_values = num_values;
2766 talloc_free(tmp_ctx);
2768 /* we now tell the backend to replace all existing values
2769 with the one we have constructed */
2770 el->flags = LDB_FLAG_MOD_REPLACE;
2777 handle deleting all active linked attributes
2779 static int replmd_modify_la_delete(struct ldb_module *module,
2780 struct replmd_private *replmd_private,
2781 struct replmd_replicated_request *ac,
2782 struct ldb_message *msg,
2783 struct ldb_message_element *el,
2784 struct ldb_message_element *old_el,
2785 const struct dsdb_attribute *schema_attr,
2787 struct ldb_dn *msg_dn,
2788 struct ldb_request *parent)
2791 struct parsed_dn *dns, *old_dns;
2792 TALLOC_CTX *tmp_ctx = NULL;
2794 struct ldb_context *ldb = ldb_module_get_ctx(module);
2795 struct ldb_control *vanish_links_ctrl = NULL;
2796 bool vanish_links = false;
2797 unsigned int num_to_delete = el->num_values;
2801 unix_to_nt_time(&now, t);
2803 if (old_el == NULL || old_el->num_values == 0) {
2804 /* there is nothing to delete... */
2805 if (num_to_delete == 0) {
2806 /* and we're deleting nothing, so that's OK */
2809 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2812 tmp_ctx = talloc_new(msg);
2813 if (tmp_ctx == NULL) {
2814 return LDB_ERR_OPERATIONS_ERROR;
2817 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2818 schema_attr->syntax->ldap_oid, parent);
2819 if (ret != LDB_SUCCESS) {
2820 talloc_free(tmp_ctx);
2824 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2825 tmp_ctx, old_el, &old_dns,
2826 schema_attr->syntax->ldap_oid,
2829 if (ret != LDB_SUCCESS) {
2830 talloc_free(tmp_ctx);
2834 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2835 if (vanish_links_ctrl) {
2836 vanish_links = true;
2837 vanish_links_ctrl->critical = false;
2840 /* we empty out el->values here to avoid damage if we return early. */
2845 * If vanish links is set, we are actually removing members of
2846 * old_el->values; otherwise we are just marking them deleted.
2848 * There is a special case when no values are given: we remove them
2849 * all. When we have the vanish_links control we just have to remove
2850 * the backlinks and change our element to replace the existing values
2851 * with the empty list.
2854 if (num_to_delete == 0) {
2855 for (i = 0; i < old_el->num_values; i++) {
2856 struct parsed_dn *p = &old_dns[i];
2857 if (p->dsdb_dn == NULL) {
2858 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2859 schema_attr->syntax->ldap_oid);
2860 if (ret != LDB_SUCCESS) {
2864 ret = replmd_add_backlink(module, replmd_private,
2865 ac->schema, msg_dn, &p->guid,
2868 if (ret != LDB_SUCCESS) {
2869 talloc_free(tmp_ctx);
2876 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2877 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2881 ret = replmd_update_la_val(old_el->values, p->v,
2882 p->dsdb_dn, p->dsdb_dn,
2883 &ac->our_invocation_id,
2884 ac->seq_num, ac->seq_num,
2886 if (ret != LDB_SUCCESS) {
2887 talloc_free(tmp_ctx);
2893 el->flags = LDB_FLAG_MOD_REPLACE;
2894 talloc_free(tmp_ctx);
2900 for (i = 0; i < num_to_delete; i++) {
2901 struct parsed_dn *p = &dns[i];
2902 struct parsed_dn *exact = NULL;
2903 struct parsed_dn *next = NULL;
2904 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2907 p->dsdb_dn->extra_part, 0,
2909 schema_attr->syntax->ldap_oid,
2911 if (ret != LDB_SUCCESS) {
2912 talloc_free(tmp_ctx);
2915 if (exact == NULL) {
2916 struct GUID_txt_buf buf;
2917 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2918 "exist for target GUID %s",
2920 GUID_buf_string(&p->guid, &buf));
2921 if (ldb_attr_cmp(el->name, "member") == 0) {
2922 talloc_free(tmp_ctx);
2923 return LDB_ERR_UNWILLING_TO_PERFORM;
2925 talloc_free(tmp_ctx);
2926 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2931 if (CHECK_DEBUGLVL(5)) {
2932 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2933 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2934 struct GUID_txt_buf buf;
2935 const char *guid_str = \
2936 GUID_buf_string(&p->guid, &buf);
2937 DEBUG(5, ("Deleting deleted linked "
2938 "attribute %s to %s, because "
2939 "vanish_links control is set\n",
2940 el->name, guid_str));
2944 /* remove the backlink */
2945 ret = replmd_add_backlink(module,
2952 if (ret != LDB_SUCCESS) {
2953 talloc_free(tmp_ctx);
2957 /* We flag the deletion and tidy it up later. */
2962 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2964 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2965 struct GUID_txt_buf buf;
2966 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2967 ldb_asprintf_errstring(ldb, "Attribute %s already "
2968 "deleted for target GUID %s",
2969 el->name, guid_str);
2970 if (ldb_attr_cmp(el->name, "member") == 0) {
2971 talloc_free(tmp_ctx);
2972 return LDB_ERR_UNWILLING_TO_PERFORM;
2974 talloc_free(tmp_ctx);
2975 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2979 ret = replmd_update_la_val(old_el->values, exact->v,
2980 exact->dsdb_dn, exact->dsdb_dn,
2981 &ac->our_invocation_id,
2982 ac->seq_num, ac->seq_num,
2984 if (ret != LDB_SUCCESS) {
2985 talloc_free(tmp_ctx);
2988 ret = replmd_add_backlink(module, replmd_private,
2993 if (ret != LDB_SUCCESS) {
2994 talloc_free(tmp_ctx);
3001 struct ldb_val *tmp_vals = NULL;
3003 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
3004 old_el->num_values);
3005 if (tmp_vals == NULL) {
3006 talloc_free(tmp_ctx);
3007 return ldb_module_oom(module);
3009 for (i = 0; i < old_el->num_values; i++) {
3010 if (old_dns[i].v == NULL) {
3013 tmp_vals[j] = *old_dns[i].v;
3016 for (i = 0; i < j; i++) {
3017 old_el->values[i] = tmp_vals[i];
3019 old_el->num_values = j;
3022 el->values = talloc_steal(msg->elements, old_el->values);
3023 el->num_values = old_el->num_values;
3025 talloc_free(tmp_ctx);
3027 /* we now tell the backend to replace all existing values
3028 with the one we have constructed */
3029 el->flags = LDB_FLAG_MOD_REPLACE;
3035 handle replacing a linked attribute
3037 static int replmd_modify_la_replace(struct ldb_module *module,
3038 struct replmd_private *replmd_private,
3039 struct replmd_replicated_request *ac,
3040 struct ldb_message *msg,
3041 struct ldb_message_element *el,
3042 struct ldb_message_element *old_el,
3043 const struct dsdb_attribute *schema_attr,
3045 struct ldb_dn *msg_dn,
3046 struct ldb_request *parent)
3048 unsigned int i, old_i, new_i;
3049 struct parsed_dn *dns, *old_dns;
3050 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3052 struct ldb_context *ldb = ldb_module_get_ctx(module);
3053 struct ldb_val *new_values = NULL;
3054 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3055 unsigned int old_num_values;
3056 unsigned int repl_num_values;
3057 unsigned int max_num_values;
3060 unix_to_nt_time(&now, t);
3063 * The replace operation is unlike the replace and delete cases in that
3064 * we need to look at every existing link to see whether it is being
3065 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3067 * As we are trying to combine two sorted lists, the algorithm we use
3068 * is akin to the merge phase of a merge sort. We interleave the two
3069 * lists, doing different things depending on which side the current
3072 * There are three main cases, with some sub-cases.
3074 * - a DN is in the old list but not the new one. It needs to be
3075 * marked as deleted (but left in the list).
3076 * - maybe it is already deleted, and we have less to do.
3078 * - a DN is in both lists. The old data gets replaced by the new,
3079 * and the list doesn't grow. The old link may have been marked as
3080 * deleted, in which case we undelete it.
3082 * - a DN is in the new list only. We add it in the right place.
3085 old_num_values = old_el ? old_el->num_values : 0;
3086 repl_num_values = el->num_values;
3087 max_num_values = old_num_values + repl_num_values;
3089 if (max_num_values == 0) {
3090 /* There is nothing to do! */
3095 * At the successful end of these functions el->values is
3096 * overwritten with new_values. However get_parsed_dns()
3097 * points p->v at the supplied el and it effectively gets used
3098 * as a working area by replmd_build_la_val(). So we must
3099 * duplicate it because our caller only called
3100 * ldb_msg_copy_shallow().
3103 el->values = talloc_memdup(tmp_ctx,
3105 sizeof(el->values[0]) * el->num_values);
3106 if (el->values == NULL) {
3107 ldb_module_oom(module);
3108 talloc_free(tmp_ctx);
3109 return LDB_ERR_OPERATIONS_ERROR;
3112 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3113 if (ret != LDB_SUCCESS) {
3114 talloc_free(tmp_ctx);
3118 ret = check_parsed_dn_duplicates(module, el, dns);
3119 if (ret != LDB_SUCCESS) {
3120 talloc_free(tmp_ctx);
3124 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3126 if (ret != LDB_SUCCESS) {
3127 talloc_free(tmp_ctx);
3131 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3133 if (ret != LDB_SUCCESS) {
3134 talloc_free(tmp_ctx);
3138 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3139 if (new_values == NULL) {
3140 ldb_module_oom(module);
3141 talloc_free(tmp_ctx);
3142 return LDB_ERR_OPERATIONS_ERROR;
3147 for (i = 0; i < max_num_values; i++) {
3149 struct parsed_dn *old_p, *new_p;
3150 if (old_i < old_num_values && new_i < repl_num_values) {
3151 old_p = &old_dns[old_i];
3152 new_p = &dns[new_i];
3153 cmp = parsed_dn_compare(old_p, new_p);
3154 } else if (old_i < old_num_values) {
3155 /* the new list is empty, read the old list */
3156 old_p = &old_dns[old_i];
3159 } else if (new_i < repl_num_values) {
3160 /* the old list is empty, read new list */
3162 new_p = &dns[new_i];
3170 * An old ones that come before the next replacement
3171 * (if any). We mark it as deleted and add it to the
3174 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3175 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3176 ret = replmd_update_la_val(new_values, old_p->v,
3179 &ac->our_invocation_id,
3180 ac->seq_num, ac->seq_num,
3182 if (ret != LDB_SUCCESS) {
3183 talloc_free(tmp_ctx);
3187 ret = replmd_add_backlink(module, replmd_private,
3190 &old_p->guid, false,
3193 if (ret != LDB_SUCCESS) {
3194 talloc_free(tmp_ctx);
3198 new_values[i] = *old_p->v;
3200 } else if (cmp == 0) {
3202 * We are overwriting one. If it was previously
3203 * deleted, we need to add a backlink.
3205 * Note that if any RMD_FLAGs in an extended new DN
3210 ret = replmd_update_la_val(new_values, old_p->v,
3213 &ac->our_invocation_id,
3214 ac->seq_num, ac->seq_num,
3216 if (ret != LDB_SUCCESS) {
3217 talloc_free(tmp_ctx);
3221 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3222 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3223 ret = replmd_add_backlink(module, replmd_private,
3229 if (ret != LDB_SUCCESS) {
3230 talloc_free(tmp_ctx);
3235 new_values[i] = *old_p->v;
3240 * Replacements that don't match an existing one. We
3241 * just add them to the final list.
3243 ret = replmd_build_la_val(new_values,
3246 &ac->our_invocation_id,
3248 if (ret != LDB_SUCCESS) {
3249 talloc_free(tmp_ctx);
3252 ret = replmd_add_backlink(module, replmd_private,
3258 if (ret != LDB_SUCCESS) {
3259 talloc_free(tmp_ctx);
3262 new_values[i] = *new_p->v;
3266 if (old_el != NULL) {
3267 talloc_steal(msg->elements, old_el->values);
3269 el->values = talloc_steal(msg->elements, new_values);
3271 talloc_free(tmp_ctx);
3273 el->flags = LDB_FLAG_MOD_REPLACE;
3280 handle linked attributes in modify requests
3282 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3283 struct replmd_private *replmd_private,
3284 struct replmd_replicated_request *ac,
3285 struct ldb_message *msg,
3287 struct ldb_request *parent)
3289 struct ldb_result *res;
3292 struct ldb_context *ldb = ldb_module_get_ctx(module);
3293 struct ldb_message *old_msg;
3295 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3297 * Nothing special is required for modifying or vanishing links
3298 * in fl2000 since they are just strings in a multi-valued
3301 struct ldb_control *ctrl = ldb_request_get_control(parent,
3302 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3304 ctrl->critical = false;
3312 * We should restrict this to the intersection of the list of
3313 * linked attributes in the schema and the list of attributes
3316 * This will help performance a little, as otherwise we have
3317 * to allocate the entire object value-by-value.
3319 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3320 DSDB_FLAG_NEXT_MODULE |
3321 DSDB_SEARCH_SHOW_RECYCLED |
3322 DSDB_SEARCH_REVEAL_INTERNALS |
3323 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3325 if (ret != LDB_SUCCESS) {
3329 old_msg = res->msgs[0];
3331 for (i=0; i<msg->num_elements; i++) {
3332 struct ldb_message_element *el = &msg->elements[i];
3333 struct ldb_message_element *old_el, *new_el;
3334 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3335 const struct dsdb_attribute *schema_attr
3336 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3338 ldb_asprintf_errstring(ldb,
3339 "%s: attribute %s is not a valid attribute in schema",
3340 __FUNCTION__, el->name);
3341 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3343 if (schema_attr->linkID == 0) {
3346 if ((schema_attr->linkID & 1) == 1) {
3347 struct ldb_control *ctrl;
3349 ctrl = ldb_request_get_control(parent,
3350 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3352 ctrl->critical = false;
3355 ctrl = ldb_request_get_control(parent,
3356 DSDB_CONTROL_DBCHECK);
3361 /* Odd is for the target. Illegal to modify */
3362 ldb_asprintf_errstring(ldb,
3363 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3364 return LDB_ERR_UNWILLING_TO_PERFORM;
3366 old_el = ldb_msg_find_element(old_msg, el->name);
3368 case LDB_FLAG_MOD_REPLACE:
3369 ret = replmd_modify_la_replace(module, replmd_private,
3370 ac, msg, el, old_el,
3375 case LDB_FLAG_MOD_DELETE:
3376 ret = replmd_modify_la_delete(module, replmd_private,
3377 ac, msg, el, old_el,
3382 case LDB_FLAG_MOD_ADD:
3383 ret = replmd_modify_la_add(module, replmd_private,
3384 ac, msg, el, old_el,
3390 ldb_asprintf_errstring(ldb,
3391 "invalid flags 0x%x for %s linked attribute",
3392 el->flags, el->name);
3393 return LDB_ERR_UNWILLING_TO_PERFORM;
3395 if (ret != LDB_SUCCESS) {
3398 ret = dsdb_check_single_valued_link(schema_attr, el);
3399 if (ret != LDB_SUCCESS) {
3400 ldb_asprintf_errstring(ldb,
3401 "Attribute %s is single valued but more than one value has been supplied",
3403 /* Return codes as found on Windows 2012r2 */
3404 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3405 return LDB_ERR_CONSTRAINT_VIOLATION;
3407 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3410 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3414 ldb_msg_remove_attr(old_msg, el->name);
3416 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3417 new_el->num_values = el->num_values;
3418 new_el->values = talloc_steal(msg->elements, el->values);
3420 /* TODO: this relises a bit too heavily on the exact
3421 behaviour of ldb_msg_find_element and
3422 ldb_msg_remove_element */
3423 old_el = ldb_msg_find_element(msg, el->name);
3425 ldb_msg_remove_element(msg, old_el);
3435 static int send_rodc_referral(struct ldb_request *req,
3436 struct ldb_context *ldb,
3439 char *referral = NULL;
3440 struct loadparm_context *lp_ctx = NULL;
3441 struct ldb_dn *fsmo_role_dn = NULL;
3442 struct ldb_dn *role_owner_dn = NULL;
3443 const char *domain = NULL;
3446 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3447 struct loadparm_context);
3449 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3450 &fsmo_role_dn, &role_owner_dn);
3452 if (W_ERROR_IS_OK(werr)) {
3453 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3454 if (server_dn != NULL) {
3455 ldb_dn_remove_child_components(server_dn, 1);
3456 domain = samdb_dn_to_dnshostname(ldb, req,
3461 if (domain == NULL) {
3462 domain = lpcfg_dnsdomain(lp_ctx);
3465 referral = talloc_asprintf(req, "ldap://%s/%s",
3467 ldb_dn_get_linearized(dn));
3468 if (referral == NULL) {
3470 return LDB_ERR_OPERATIONS_ERROR;
3473 return ldb_module_send_referral(req, referral);
3477 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3479 struct ldb_context *ldb;
3480 struct replmd_replicated_request *ac;
3481 struct ldb_request *down_req;
3482 struct ldb_message *msg;
3483 time_t t = time(NULL);
3485 bool is_urgent = false, rodc = false;
3486 bool is_schema_nc = false;
3487 unsigned int functional_level;
3488 const struct ldb_message_element *guid_el = NULL;
3489 struct ldb_control *sd_propagation_control;
3490 struct ldb_control *fix_links_control = NULL;
3491 struct ldb_control *fix_dn_name_control = NULL;
3492 struct ldb_control *fix_dn_sid_control = NULL;
3493 struct replmd_private *replmd_private =
3494 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3496 /* do not manipulate our control entries */
3497 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3498 return ldb_next_request(module, req);
3501 sd_propagation_control = ldb_request_get_control(req,
3502 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3503 if (sd_propagation_control != NULL) {
3504 if (req->op.mod.message->num_elements != 1) {
3505 return ldb_module_operr(module);
3507 ret = strcmp(req->op.mod.message->elements[0].name,
3508 "nTSecurityDescriptor");
3510 return ldb_module_operr(module);
3513 return ldb_next_request(module, req);
3516 ldb = ldb_module_get_ctx(module);
3518 fix_links_control = ldb_request_get_control(req,
3519 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3520 if (fix_links_control != NULL) {
3521 struct dsdb_schema *schema = NULL;
3522 const struct dsdb_attribute *sa = NULL;
3524 if (req->op.mod.message->num_elements != 1) {
3525 return ldb_module_operr(module);
3528 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3529 return ldb_module_operr(module);
3532 schema = dsdb_get_schema(ldb, req);
3533 if (schema == NULL) {
3534 return ldb_module_operr(module);
3537 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3538 req->op.mod.message->elements[0].name);
3540 return ldb_module_operr(module);
3543 if (sa->linkID == 0) {
3544 return ldb_module_operr(module);
3547 fix_links_control->critical = false;
3548 return ldb_next_request(module, req);
3551 fix_dn_name_control = ldb_request_get_control(req,
3552 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3553 if (fix_dn_name_control != NULL) {
3554 struct dsdb_schema *schema = NULL;
3555 const struct dsdb_attribute *sa = NULL;
3557 if (req->op.mod.message->num_elements != 2) {
3558 return ldb_module_operr(module);
3561 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3562 return ldb_module_operr(module);
3565 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3566 return ldb_module_operr(module);
3569 if (req->op.mod.message->elements[0].num_values != 1) {
3570 return ldb_module_operr(module);
3573 if (req->op.mod.message->elements[1].num_values != 1) {
3574 return ldb_module_operr(module);
3577 schema = dsdb_get_schema(ldb, req);
3578 if (schema == NULL) {
3579 return ldb_module_operr(module);
3582 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3583 req->op.mod.message->elements[1].name) != 0) {
3584 return ldb_module_operr(module);
3587 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3588 req->op.mod.message->elements[0].name);
3590 return ldb_module_operr(module);
3593 if (sa->dn_format == DSDB_INVALID_DN) {
3594 return ldb_module_operr(module);
3597 if (sa->linkID != 0) {
3598 return ldb_module_operr(module);
3602 * If we are run from dbcheck and we are not updating
3603 * a link (as these would need to be sorted and so
3604 * can't go via such a simple update, then do not
3605 * trigger replicated updates and a new USN from this
3606 * change, it wasn't a real change, just a new
3607 * (correct) string DN
3610 fix_dn_name_control->critical = false;
3611 return ldb_next_request(module, req);
3614 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3616 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3617 if (guid_el != NULL) {
3618 ldb_set_errstring(ldb,
3619 "replmd_modify: it's not allowed to change the objectGUID!");
3620 return LDB_ERR_CONSTRAINT_VIOLATION;
3623 ac = replmd_ctx_init(module, req);
3625 return ldb_module_oom(module);
3628 functional_level = dsdb_functional_level(ldb);
3630 /* we have to copy the message as the caller might have it as a const */
3631 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3635 return LDB_ERR_OPERATIONS_ERROR;
3638 fix_dn_sid_control = ldb_request_get_control(req,
3639 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3640 if (fix_dn_sid_control != NULL) {
3641 const struct dsdb_attribute *sa = NULL;
3643 if (msg->num_elements != 1) {
3645 return ldb_module_operr(module);
3648 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3650 return ldb_module_operr(module);
3653 if (msg->elements[0].num_values != 1) {
3655 return ldb_module_operr(module);
3658 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3659 msg->elements[0].name);
3662 return ldb_module_operr(module);
3665 if (sa->dn_format != DSDB_NORMAL_DN) {
3667 return ldb_module_operr(module);
3670 fix_dn_sid_control->critical = false;
3671 ac->fix_link_sid = true;
3673 goto handle_linked_attribs;
3676 ldb_msg_remove_attr(msg, "whenChanged");
3677 ldb_msg_remove_attr(msg, "uSNChanged");
3679 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3681 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3682 msg, &ac->seq_num, t, is_schema_nc,
3684 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3685 ret = send_rodc_referral(req, ldb, msg->dn);
3691 if (ret != LDB_SUCCESS) {
3696 handle_linked_attribs:
3697 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3699 if (ret != LDB_SUCCESS) {
3705 * - replace the old object with the newly constructed one
3708 ac->is_urgent = is_urgent;
3710 ret = ldb_build_mod_req(&down_req, ldb, ac,
3713 ac, replmd_op_callback,
3715 LDB_REQ_SET_LOCATION(down_req);
3716 if (ret != LDB_SUCCESS) {
3721 /* current partition control is needed by "replmd_op_callback" */
3722 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3723 ret = ldb_request_add_control(down_req,
3724 DSDB_CONTROL_CURRENT_PARTITION_OID,
3726 if (ret != LDB_SUCCESS) {
3732 /* If we are in functional level 2000, then
3733 * replmd_modify_handle_linked_attribs will have done
3735 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3736 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3737 if (ret != LDB_SUCCESS) {
3743 talloc_steal(down_req, msg);
3745 /* we only change whenChanged and uSNChanged if the seq_num
3747 if (ac->seq_num != 0) {
3748 ret = add_time_element(msg, "whenChanged", t);
3749 if (ret != LDB_SUCCESS) {
3755 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3756 if (ret != LDB_SUCCESS) {
3763 /* go on with the call chain */
3764 return ldb_next_request(module, down_req);
3767 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3770 handle a rename request
3772 On a rename we need to do an extra ldb_modify which sets the
3773 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3775 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3777 struct ldb_context *ldb;
3778 struct ldb_control *fix_dn_name_control = NULL;
3779 struct replmd_replicated_request *ac;
3781 struct ldb_request *down_req;
3783 /* do not manipulate our control entries */
3784 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3785 return ldb_next_request(module, req);
3788 fix_dn_name_control = ldb_request_get_control(req,
3789 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3790 if (fix_dn_name_control != NULL) {
3791 return ldb_next_request(module, req);
3794 ldb = ldb_module_get_ctx(module);
3796 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3798 ac = replmd_ctx_init(module, req);
3800 return ldb_module_oom(module);
3803 ret = ldb_build_rename_req(&down_req, ldb, ac,
3804 ac->req->op.rename.olddn,
3805 ac->req->op.rename.newdn,
3807 ac, replmd_rename_callback,
3809 LDB_REQ_SET_LOCATION(down_req);
3810 if (ret != LDB_SUCCESS) {
3815 /* go on with the call chain */
3816 return ldb_next_request(module, down_req);
3819 /* After the rename is compleated, update the whenchanged etc */
3820 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3822 struct ldb_context *ldb;
3823 struct ldb_request *down_req;
3824 struct ldb_message *msg;
3825 const struct dsdb_attribute *rdn_attr;
3826 const char *rdn_name;
3827 const struct ldb_val *rdn_val;
3828 const char *attrs[5] = { NULL, };
3829 time_t t = time(NULL);
3831 bool is_urgent = false, rodc = false;
3833 struct replmd_replicated_request *ac =
3834 talloc_get_type(req->context, struct replmd_replicated_request);
3835 struct replmd_private *replmd_private =
3836 talloc_get_type(ldb_module_get_private(ac->module),
3837 struct replmd_private);
3839 ldb = ldb_module_get_ctx(ac->module);
3841 if (ares->error != LDB_SUCCESS) {
3842 return ldb_module_done(ac->req, ares->controls,
3843 ares->response, ares->error);
3846 if (ares->type != LDB_REPLY_DONE) {
3847 ldb_set_errstring(ldb,
3848 "invalid reply type in repl_meta_data rename callback");
3850 return ldb_module_done(ac->req, NULL, NULL,
3851 LDB_ERR_OPERATIONS_ERROR);
3855 * - replace the old object with the newly constructed one
3858 msg = ldb_msg_new(ac);
3861 return LDB_ERR_OPERATIONS_ERROR;
3864 msg->dn = ac->req->op.rename.newdn;
3866 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3868 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3869 if (rdn_name == NULL) {
3871 return ldb_module_done(ac->req, NULL, NULL,
3875 /* normalize the rdn attribute name */
3876 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3877 if (rdn_attr == NULL) {
3879 return ldb_module_done(ac->req, NULL, NULL,
3882 rdn_name = rdn_attr->lDAPDisplayName;
3884 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3885 if (rdn_val == NULL) {
3887 return ldb_module_done(ac->req, NULL, NULL,
3891 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3893 return ldb_module_done(ac->req, NULL, NULL,
3896 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3898 return ldb_module_done(ac->req, NULL, NULL,
3901 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3903 return ldb_module_done(ac->req, NULL, NULL,
3906 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3908 return ldb_module_done(ac->req, NULL, NULL,
3913 * here we let replmd_update_rpmd() only search for
3914 * the existing "replPropertyMetaData" and rdn_name attributes.
3916 * We do not want the existing "name" attribute as
3917 * the "name" attribute needs to get the version
3918 * updated on rename even if the rdn value hasn't changed.
3920 * This is the diff of the meta data, for a moved user
3921 * on a w2k8r2 server:
3924 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3925 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3926 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3927 * version : 0x00000001 (1)
3928 * reserved : 0x00000000 (0)
3929 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3930 * local_usn : 0x00000000000037a5 (14245)
3931 * array: struct replPropertyMetaData1
3932 * attid : DRSUAPI_ATTID_name (0x90001)
3933 * - version : 0x00000001 (1)
3934 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3935 * + version : 0x00000002 (2)
3936 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3937 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3938 * - originating_usn : 0x00000000000037a5 (14245)
3939 * - local_usn : 0x00000000000037a5 (14245)
3940 * + originating_usn : 0x0000000000003834 (14388)
3941 * + local_usn : 0x0000000000003834 (14388)
3942 * array: struct replPropertyMetaData1
3943 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3944 * version : 0x00000004 (4)
3946 attrs[0] = "replPropertyMetaData";
3947 attrs[1] = "objectClass";
3948 attrs[2] = "instanceType";
3949 attrs[3] = rdn_name;
3952 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3953 msg, &ac->seq_num, t,
3954 is_schema_nc, &is_urgent, &rodc);
3955 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3956 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3958 return ldb_module_done(req, NULL, NULL, ret);
3961 if (ret != LDB_SUCCESS) {
3963 return ldb_module_done(ac->req, NULL, NULL, ret);
3966 if (ac->seq_num == 0) {
3968 return ldb_module_done(ac->req, NULL, NULL,
3970 "internal error seq_num == 0"));
3972 ac->is_urgent = is_urgent;
3974 ret = ldb_build_mod_req(&down_req, ldb, ac,
3977 ac, replmd_op_callback,
3979 LDB_REQ_SET_LOCATION(down_req);
3980 if (ret != LDB_SUCCESS) {
3985 /* current partition control is needed by "replmd_op_callback" */
3986 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3987 ret = ldb_request_add_control(down_req,
3988 DSDB_CONTROL_CURRENT_PARTITION_OID,
3990 if (ret != LDB_SUCCESS) {
3996 talloc_steal(down_req, msg);
3998 ret = add_time_element(msg, "whenChanged", t);
3999 if (ret != LDB_SUCCESS) {
4005 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
4006 if (ret != LDB_SUCCESS) {
4012 /* go on with the call chain - do the modify after the rename */
4013 return ldb_next_request(ac->module, down_req);
4017 * remove links from objects that point at this object when an object
4018 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
4019 * RemoveObj which states that link removal due to the object being
4020 * deleted is NOT an originating update - they just go away!
4023 static int replmd_delete_remove_link(struct ldb_module *module,
4024 const struct dsdb_schema *schema,
4025 struct replmd_private *replmd_private,
4028 struct ldb_message_element *el,
4029 const struct dsdb_attribute *sa,
4030 struct ldb_request *parent,
4031 bool *caller_should_vanish)
4034 TALLOC_CTX *tmp_ctx = talloc_new(module);
4035 struct ldb_context *ldb = ldb_module_get_ctx(module);
4037 for (i=0; i<el->num_values; i++) {
4038 struct dsdb_dn *dsdb_dn;
4040 struct ldb_message *msg;
4041 const struct dsdb_attribute *target_attr;
4042 struct ldb_message_element *el2;
4044 struct ldb_val dn_val;
4045 uint32_t dsdb_flags = 0;
4046 const char *attrs[] = { NULL, NULL };
4047 struct ldb_result *link_res;
4048 struct ldb_message *link_msg;
4049 struct ldb_message_element *link_el;
4050 struct parsed_dn *link_dns;
4051 struct parsed_dn *p = NULL, *unused = NULL;
4053 if (dsdb_dn_is_deleted_val(&el->values[i])) {
4057 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4059 talloc_free(tmp_ctx);
4060 return LDB_ERR_OPERATIONS_ERROR;
4063 /* remove the link */
4064 msg = ldb_msg_new(tmp_ctx);
4066 ldb_module_oom(module);
4067 talloc_free(tmp_ctx);
4068 return LDB_ERR_OPERATIONS_ERROR;
4071 msg->dn = dsdb_dn->dn;
4073 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4074 if (target_attr == NULL) {
4077 attrs[0] = target_attr->lDAPDisplayName;
4079 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4080 LDB_FLAG_MOD_DELETE, &el2);
4081 if (ret != LDB_SUCCESS) {
4082 ldb_module_oom(module);
4083 talloc_free(tmp_ctx);
4084 return LDB_ERR_OPERATIONS_ERROR;
4087 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4089 DSDB_FLAG_NEXT_MODULE |
4090 DSDB_SEARCH_SHOW_EXTENDED_DN |
4091 DSDB_SEARCH_SHOW_RECYCLED,
4094 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4095 DBG_WARNING("Failed to find forward link object %s "
4096 "to remove backlink %s on %s",
4097 ldb_dn_get_linearized(msg->dn),
4098 sa->lDAPDisplayName,
4099 ldb_dn_get_linearized(dn));
4100 *caller_should_vanish = true;
4104 if (ret != LDB_SUCCESS) {
4105 talloc_free(tmp_ctx);
4109 link_msg = link_res->msgs[0];
4110 link_el = ldb_msg_find_element(link_msg,
4111 target_attr->lDAPDisplayName);
4112 if (link_el == NULL) {
4113 DBG_WARNING("Failed to find forward link on %s "
4114 "as %s to remove backlink %s on %s",
4115 ldb_dn_get_linearized(msg->dn),
4116 target_attr->lDAPDisplayName,
4117 sa->lDAPDisplayName,
4118 ldb_dn_get_linearized(dn));
4119 *caller_should_vanish = true;
4124 * This call 'upgrades' the links in link_dns, but we
4125 * do not commit the result back into the database, so
4126 * this is safe to call in FL2000 or on databases that
4127 * have been run at that level in the past.
4129 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
4132 target_attr->syntax->ldap_oid,
4134 if (ret != LDB_SUCCESS) {
4135 talloc_free(tmp_ctx);
4139 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4143 target_attr->syntax->ldap_oid, false);
4144 if (ret != LDB_SUCCESS) {
4145 talloc_free(tmp_ctx);
4150 DBG_WARNING("Failed to find forward link on %s "
4151 "as %s to remove backlink %s on %s",
4152 ldb_dn_get_linearized(msg->dn),
4153 target_attr->lDAPDisplayName,
4154 sa->lDAPDisplayName,
4155 ldb_dn_get_linearized(dn));
4156 *caller_should_vanish = true;
4161 * If we find a backlink to ourself, we will delete
4162 * the forward link before we get to process that
4163 * properly, so just let the caller process this via
4166 * We do this once we are sure we have the forward
4167 * link (to ourself) in case something is very wrong
4168 * and they are out of sync.
4170 if (ldb_dn_compare(dsdb_dn->dn, dn) == 0) {
4174 /* This needs to get the Binary DN, by first searching */
4175 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4178 dn_val = data_blob_string_const(dn_str);
4179 el2->values = &dn_val;
4180 el2->num_values = 1;
4183 * Ensure that we tell the modification to vanish any linked
4184 * attributes (not simply mark them as isDeleted = TRUE)
4186 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4188 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4189 if (ret != LDB_SUCCESS) {
4190 talloc_free(tmp_ctx);
4194 talloc_free(tmp_ctx);
4200 handle update of replication meta data for deletion of objects
4202 This also handles the mapping of delete to a rename operation
4203 to allow deletes to be replicated.
4205 It also handles the incoming deleted objects, to ensure they are
4206 fully deleted here. In that case re_delete is true, and we do not
4207 use this as a signal to change the deleted state, just reinforce it.
4210 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4212 int ret = LDB_ERR_OTHER;
4213 bool retb, disallow_move_on_delete;
4214 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4215 const char *rdn_name;
4216 const struct ldb_val *rdn_value, *new_rdn_value;
4218 struct ldb_context *ldb = ldb_module_get_ctx(module);
4219 const struct dsdb_schema *schema;
4220 struct ldb_message *msg, *old_msg;
4221 struct ldb_message_element *el;
4222 TALLOC_CTX *tmp_ctx;
4223 struct ldb_result *res, *parent_res;
4224 static const char * const preserved_attrs[] = {
4226 * This list MUST be kept in case-insensitive sorted order,
4227 * as we use it in a binary search with ldb_attr_cmp().
4229 * We get this hard-coded list from
4230 * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4234 "distinguishedName",
4235 "dNReferenceUpdate",
4247 "msDS-LastKnownRDN",
4252 "nTSecurityDescriptor",
4257 "proxiedObjectName",
4258 "replPropertyMetaData",
4260 "securityIdentifier",
4268 "userAccountControl",
4274 * DO NOT JUST APPEND TO THIS LIST.
4276 * In case you missed the note at the top, this list is kept
4277 * in case-insensitive sorted order. In the unlikely event you
4278 * need to add an attrbute, please add it in the RIGHT PLACE.
4281 static const char * const all_attrs[] = {
4282 DSDB_SECRET_ATTRIBUTES,
4286 static const struct ldb_val true_val = {
4287 .data = discard_const_p(uint8_t, "TRUE"),
4292 uint32_t dsdb_flags = 0;
4293 struct replmd_private *replmd_private;
4294 enum deletion_state deletion_state, next_deletion_state;
4296 if (ldb_dn_is_special(req->op.del.dn)) {
4297 return ldb_next_request(module, req);
4301 * We have to allow dbcheck to remove an object that
4302 * is beyond repair, and to do so totally. This could
4303 * mean we we can get a partial object from the other
4304 * DC, causing havoc, so dbcheck suggests
4305 * re-replication first. dbcheck sets both DBCHECK
4306 * and RELAX in this situation.
4308 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4309 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4310 /* really, really remove it */
4311 return ldb_next_request(module, req);
4314 tmp_ctx = talloc_new(ldb);
4317 return LDB_ERR_OPERATIONS_ERROR;
4320 schema = dsdb_get_schema(ldb, tmp_ctx);
4322 talloc_free(tmp_ctx);
4323 return LDB_ERR_OPERATIONS_ERROR;
4326 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4328 /* we need the complete msg off disk, so we can work out which
4329 attributes need to be removed */
4330 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4331 DSDB_FLAG_NEXT_MODULE |
4332 DSDB_SEARCH_SHOW_RECYCLED |
4333 DSDB_SEARCH_REVEAL_INTERNALS |
4334 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4335 if (ret != LDB_SUCCESS) {
4336 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4337 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4338 re_delete ? "re-delete" : "delete",
4339 ldb_dn_get_linearized(old_dn),
4340 ldb_errstring(ldb_module_get_ctx(module)));
4341 talloc_free(tmp_ctx);
4344 old_msg = res->msgs[0];
4346 replmd_deletion_state(module, old_msg,
4348 &next_deletion_state);
4350 /* This supports us noticing an incoming isDeleted and acting on it */
4352 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4353 next_deletion_state = deletion_state;
4356 if (next_deletion_state == OBJECT_REMOVED) {
4358 * We have to prevent objects being deleted, even if
4359 * the administrator really wants them gone, as
4360 * without the tombstone, we can get a partial object
4361 * from the other DC, causing havoc.
4363 * The only other valid case is when the 180 day
4364 * timeout has expired, when relax is specified.
4366 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4367 /* it is already deleted - really remove it this time */
4368 talloc_free(tmp_ctx);
4369 return ldb_next_request(module, req);
4372 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4373 "This check is to prevent corruption of the replicated state.",
4374 ldb_dn_get_linearized(old_msg->dn));
4375 return LDB_ERR_UNWILLING_TO_PERFORM;
4378 rdn_name = ldb_dn_get_rdn_name(old_dn);
4379 rdn_value = ldb_dn_get_rdn_val(old_dn);
4380 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4381 talloc_free(tmp_ctx);
4382 return ldb_operr(ldb);
4385 msg = ldb_msg_new(tmp_ctx);
4387 ldb_module_oom(module);
4388 talloc_free(tmp_ctx);
4389 return LDB_ERR_OPERATIONS_ERROR;
4394 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4395 disallow_move_on_delete =
4396 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4397 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4399 /* work out where we will be renaming this object to */
4400 if (!disallow_move_on_delete) {
4401 struct ldb_dn *deleted_objects_dn;
4402 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4403 &deleted_objects_dn);
4406 * We should not move objects if we can't find the
4407 * deleted objects DN. Not moving (or otherwise
4408 * harming) the Deleted Objects DN itself is handled
4411 if (re_delete && (ret != LDB_SUCCESS)) {
4412 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4413 if (new_dn == NULL) {
4414 ldb_module_oom(module);
4415 talloc_free(tmp_ctx);
4416 return LDB_ERR_OPERATIONS_ERROR;
4418 } else if (ret != LDB_SUCCESS) {
4419 /* this is probably an attempted delete on a partition
4420 * that doesn't allow delete operations, such as the
4421 * schema partition */
4422 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4423 ldb_dn_get_linearized(old_dn));
4424 talloc_free(tmp_ctx);
4425 return LDB_ERR_UNWILLING_TO_PERFORM;
4427 new_dn = deleted_objects_dn;
4430 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4431 if (new_dn == NULL) {
4432 ldb_module_oom(module);
4433 talloc_free(tmp_ctx);
4434 return LDB_ERR_OPERATIONS_ERROR;
4438 /* get the objects GUID from the search we just did */
4439 guid = samdb_result_guid(old_msg, "objectGUID");
4441 if (deletion_state == OBJECT_NOT_DELETED) {
4442 struct ldb_message_element *is_deleted_el;
4444 ret = replmd_make_deleted_child_dn(tmp_ctx,
4447 rdn_name, rdn_value,
4450 if (ret != LDB_SUCCESS) {
4451 talloc_free(tmp_ctx);
4455 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4457 if (ret != LDB_SUCCESS) {
4458 ldb_asprintf_errstring(ldb, __location__
4459 ": Failed to add isDeleted string to the msg");
4460 talloc_free(tmp_ctx);
4463 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4466 * No matter what has happened with other renames etc, try again to
4467 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4470 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4471 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4473 ldb_asprintf_errstring(ldb, __location__
4474 ": Unable to add a prepare rdn of %s",
4475 ldb_dn_get_linearized(rdn));
4476 talloc_free(tmp_ctx);
4477 return LDB_ERR_OPERATIONS_ERROR;
4479 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4481 retb = ldb_dn_add_child(new_dn, rdn);
4483 ldb_asprintf_errstring(ldb, __location__
4484 ": Unable to add rdn %s to base dn: %s",
4485 ldb_dn_get_linearized(rdn),
4486 ldb_dn_get_linearized(new_dn));
4487 talloc_free(tmp_ctx);
4488 return LDB_ERR_OPERATIONS_ERROR;
4493 now we need to modify the object in the following ways:
4495 - add isDeleted=TRUE
4496 - update rDN and name, with new rDN
4497 - remove linked attributes
4498 - remove objectCategory and sAMAccountType
4499 - remove attribs not on the preserved list
4500 - preserved if in above list, or is rDN
4501 - remove all linked attribs from this object
4502 - remove all links from other objects to this object
4503 (note we use the backlinks to do this, so we won't find one-way
4504 links that still point to this object, or deactivated two-way
4505 links, i.e. 'member' after the user has been removed from the
4507 - add lastKnownParent
4508 - update replPropertyMetaData?
4510 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4513 if (deletion_state == OBJECT_NOT_DELETED) {
4514 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4515 char *parent_dn_str = NULL;
4516 struct ldb_message_element *p_el;
4518 /* we need the storage form of the parent GUID */
4519 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4521 DSDB_FLAG_NEXT_MODULE |
4522 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4523 DSDB_SEARCH_REVEAL_INTERNALS|
4524 DSDB_SEARCH_SHOW_RECYCLED, req);
4525 if (ret != LDB_SUCCESS) {
4526 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4527 "repmd_delete: Failed to %s %s, "
4528 "because we failed to find it's parent (%s): %s",
4529 re_delete ? "re-delete" : "delete",
4530 ldb_dn_get_linearized(old_dn),
4531 ldb_dn_get_linearized(parent_dn),
4532 ldb_errstring(ldb_module_get_ctx(module)));
4533 talloc_free(tmp_ctx);
4538 * Now we can use the DB version,
4539 * it will have the extended DN info in it
4541 parent_dn = parent_res->msgs[0]->dn;
4542 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4545 if (parent_dn_str == NULL) {
4546 talloc_free(tmp_ctx);
4547 return ldb_module_oom(module);
4550 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4552 if (ret != LDB_SUCCESS) {
4553 ldb_asprintf_errstring(ldb, __location__
4554 ": Failed to add lastKnownParent "
4555 "string when deleting %s",
4556 ldb_dn_get_linearized(old_dn));
4557 talloc_free(tmp_ctx);
4560 p_el = ldb_msg_find_element(msg,
4563 talloc_free(tmp_ctx);
4564 return ldb_module_operr(module);
4566 p_el->flags = LDB_FLAG_MOD_REPLACE;
4568 if (next_deletion_state == OBJECT_DELETED) {
4569 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4570 if (ret != LDB_SUCCESS) {
4571 ldb_asprintf_errstring(ldb, __location__
4572 ": Failed to add msDS-LastKnownRDN "
4573 "string when deleting %s",
4574 ldb_dn_get_linearized(old_dn));
4575 talloc_free(tmp_ctx);
4578 p_el = ldb_msg_find_element(msg,
4579 "msDS-LastKnownRDN");
4581 talloc_free(tmp_ctx);
4582 return ldb_module_operr(module);
4584 p_el->flags = LDB_FLAG_MOD_ADD;
4588 switch (next_deletion_state) {
4590 case OBJECT_RECYCLED:
4591 case OBJECT_TOMBSTONE:
4594 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4595 * describes what must be removed from a tombstone
4598 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4599 * describes what must be removed from a recycled
4605 * we also mark it as recycled, meaning this object can't be
4606 * recovered (we are stripping its attributes).
4607 * This is done only if we have this schema object of course ...
4608 * This behavior is identical to the one of Windows 2008R2 which
4609 * always set the isRecycled attribute, even if the recycle-bin is
4610 * not activated and what ever the forest level is.
4612 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4613 struct ldb_message_element *is_recycled_el;
4615 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4617 if (ret != LDB_SUCCESS) {
4618 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4619 ldb_module_oom(module);
4620 talloc_free(tmp_ctx);
4623 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4626 replmd_private = talloc_get_type(ldb_module_get_private(module),
4627 struct replmd_private);
4628 /* work out which of the old attributes we will be removing */
4629 for (i=0; i<old_msg->num_elements; i++) {
4630 const struct dsdb_attribute *sa;
4631 el = &old_msg->elements[i];
4632 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4634 const char *old_dn_str
4635 = ldb_dn_get_linearized(old_dn);
4637 ldb_asprintf_errstring(ldb,
4640 "not found in schema "
4641 "when deleting %s. "
4642 "Existing record is invalid",
4645 talloc_free(tmp_ctx);
4646 return LDB_ERR_OPERATIONS_ERROR;
4648 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4649 /* don't remove the rDN */
4653 if (sa->linkID & 1) {
4654 bool caller_should_vanish = false;
4656 * we have a backlink in this object
4657 * that needs to be removed. We're not
4658 * allowed to remove it directly
4659 * however, so we instead setup a
4660 * modify to delete the corresponding
4663 ret = replmd_delete_remove_link(module, schema,
4667 &caller_should_vanish);
4668 if (ret != LDB_SUCCESS) {
4669 const char *old_dn_str
4670 = ldb_dn_get_linearized(old_dn);
4671 ldb_asprintf_errstring(ldb,
4673 ": Failed to remove backlink of "
4674 "%s when deleting %s: %s",
4677 ldb_errstring(ldb));
4678 talloc_free(tmp_ctx);
4679 return LDB_ERR_OPERATIONS_ERROR;
4682 if (caller_should_vanish == false) {
4684 * now we continue, which means we
4685 * won't remove this backlink
4692 * Otherwise vanish the link, we are
4693 * out of sync and the controlling
4694 * object does not have the source
4698 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4700 } else if (sa->linkID == 0) {
4701 const char * const *attr = NULL;
4702 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4705 BINARY_ARRAY_SEARCH_V(preserved_attrs,
4706 ARRAY_SIZE(preserved_attrs),
4711 * If we are preserving, do not do the
4712 * ldb_msg_add_empty() below, continue
4713 * to the next element
4720 * Ensure that we tell the modification to vanish any linked
4721 * attributes (not simply mark them as isDeleted = TRUE)
4723 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4725 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4726 if (ret != LDB_SUCCESS) {
4727 talloc_free(tmp_ctx);
4728 ldb_module_oom(module);
4735 case OBJECT_DELETED:
4737 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4738 * describes what must be removed from a deleted
4742 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4743 if (ret != LDB_SUCCESS) {
4744 talloc_free(tmp_ctx);
4745 ldb_module_oom(module);
4749 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4750 if (ret != LDB_SUCCESS) {
4751 talloc_free(tmp_ctx);
4752 ldb_module_oom(module);
4762 if (deletion_state == OBJECT_NOT_DELETED) {
4763 const struct dsdb_attribute *sa;
4765 /* work out what the new rdn value is, for updating the
4766 rDN and name fields */
4767 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4768 if (new_rdn_value == NULL) {
4769 talloc_free(tmp_ctx);
4770 return ldb_operr(ldb);
4773 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4775 talloc_free(tmp_ctx);
4776 return LDB_ERR_OPERATIONS_ERROR;
4779 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4781 if (ret != LDB_SUCCESS) {
4782 talloc_free(tmp_ctx);
4785 el->flags = LDB_FLAG_MOD_REPLACE;
4787 el = ldb_msg_find_element(old_msg, "name");
4789 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4790 if (ret != LDB_SUCCESS) {
4791 talloc_free(tmp_ctx);
4794 el->flags = LDB_FLAG_MOD_REPLACE;
4799 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4804 * No matter what has happned with other renames, try again to
4805 * get this to be under the deleted DN.
4807 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4808 /* now rename onto the new DN */
4809 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4810 if (ret != LDB_SUCCESS){
4811 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4812 ldb_dn_get_linearized(old_dn),
4813 ldb_dn_get_linearized(new_dn),
4814 ldb_errstring(ldb)));
4815 talloc_free(tmp_ctx);
4821 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4822 if (ret != LDB_SUCCESS) {
4825 * This should not fail, so be quite verbose in the
4826 * error handling if it fails
4828 if (strcmp(ldb_dn_get_linearized(old_dn),
4829 ldb_dn_get_linearized(new_dn)) != 0) {
4830 DBG_NOTICE("Failure to handle '%s' of object %s "
4831 "after successful rename to %s. "
4832 "Error during tombstone modificaton was: %s\n",
4833 re_delete ? "re-delete" : "delete",
4834 ldb_dn_get_linearized(new_dn),
4835 ldb_dn_get_linearized(old_dn),
4836 ldb_errstring(ldb));
4838 DBG_NOTICE("Failure to handle '%s' of object %s. "
4839 "Error during tombstone modificaton was: %s\n",
4840 re_delete ? "re-delete" : "delete",
4841 ldb_dn_get_linearized(new_dn),
4842 ldb_errstring(ldb));
4844 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(module),
4846 LDB_CHANGETYPE_MODIFY,
4849 DBG_INFO("Failed tombstone modify%s was:\n%s\n",
4850 (dsdb_flags & DSDB_REPLMD_VANISH_LINKS) ?
4851 " with VANISH_LINKS" : "",
4853 ldb_asprintf_errstring(ldb,
4854 "replmd_delete: Failed to modify"
4855 " object %s in '%s' - %s",
4856 ldb_dn_get_linearized(old_dn),
4857 re_delete ? "re-delete" : "delete",
4858 ldb_errstring(ldb));
4859 talloc_free(tmp_ctx);
4863 talloc_free(tmp_ctx);
4865 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4868 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4870 return replmd_delete_internals(module, req, false);
4874 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4879 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4881 int ret = LDB_ERR_OTHER;
4882 /* TODO: do some error mapping */
4884 /* Let the caller know the full WERROR */
4885 ar->objs->error = status;
4891 static struct replPropertyMetaData1 *
4892 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4893 enum drsuapi_DsAttributeId attid)
4896 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4898 for (i = 0; i < rpmd_ctr->count; i++) {
4899 if (rpmd_ctr->array[i].attid == attid) {
4900 return &rpmd_ctr->array[i];
4908 return true if an update is newer than an existing entry
4909 see section 5.11 of MS-ADTS
4911 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4912 const struct GUID *update_invocation_id,
4913 uint32_t current_version,
4914 uint32_t update_version,
4915 NTTIME current_change_time,
4916 NTTIME update_change_time)
4918 if (update_version != current_version) {
4919 return update_version > current_version;
4921 if (update_change_time != current_change_time) {
4922 return update_change_time > current_change_time;
4924 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4927 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4928 struct replPropertyMetaData1 *new_m)
4930 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4931 &new_m->originating_invocation_id,
4934 cur_m->originating_change_time,
4935 new_m->originating_change_time);
4938 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4939 struct replPropertyMetaData1 *cur_m,
4940 struct replPropertyMetaData1 *new_m)
4945 * If the new replPropertyMetaData entry for this attribute is
4946 * not provided (this happens in the case where we look for
4947 * ATTID_name, but the name was not changed), then the local
4948 * state is clearly still current, as the remote
4949 * server didn't send it due to being older the high watermark
4952 if (new_m == NULL) {
4956 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4958 * if we compare equal then do an
4959 * update. This is used when a client
4960 * asks for a FULL_SYNC, and can be
4961 * used to recover a corrupt
4964 * This call is a bit tricky, what we
4965 * are doing it turning the 'is_newer'
4966 * call into a 'not is older' by
4967 * swapping cur_m and new_m, and negating the
4970 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4973 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4981 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4983 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4984 struct ldb_context *ldb,
4986 const char *four_char_prefix,
4987 const char *rdn_name,
4988 const struct ldb_val *rdn_value,
4991 struct ldb_val deleted_child_rdn_val;
4992 struct GUID_txt_buf guid_str;
4996 GUID_buf_string(&guid, &guid_str);
4998 retb = ldb_dn_add_child_fmt(dn, "X=Y");
5000 ldb_asprintf_errstring(ldb, __location__
5001 ": Unable to add a formatted child to dn: %s",
5002 ldb_dn_get_linearized(dn));
5003 return LDB_ERR_OPERATIONS_ERROR;
5007 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
5008 * we should truncate this value to ensure the RDN is not more than 255 chars.
5010 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
5012 * "Naming constraints are not enforced for replicated
5013 * updates." so this is safe and we don't have to work out not
5014 * splitting a UTF8 char right now.
5016 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
5019 * sizeof(guid_str.buf) will always be longer than
5020 * strlen(guid_str.buf) but we allocate using this and
5021 * waste the trailing bytes to avoid scaring folks
5022 * with memcpy() using strlen() below
5025 deleted_child_rdn_val.data
5026 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
5028 rdn_value->length + 5
5029 + sizeof(guid_str.buf));
5030 if (!deleted_child_rdn_val.data) {
5031 ldb_asprintf_errstring(ldb, __location__
5032 ": Unable to add a formatted child to dn: %s",
5033 ldb_dn_get_linearized(dn));
5034 return LDB_ERR_OPERATIONS_ERROR;
5037 deleted_child_rdn_val.length =
5038 rdn_value->length + 5
5039 + strlen(guid_str.buf);
5041 SMB_ASSERT(deleted_child_rdn_val.length <
5042 talloc_get_size(deleted_child_rdn_val.data));
5045 * talloc won't allocate more than 256MB so we can't
5046 * overflow but just to be sure
5048 if (deleted_child_rdn_val.length < rdn_value->length) {
5049 return LDB_ERR_OPERATIONS_ERROR;
5052 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
5053 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
5054 four_char_prefix, 4);
5055 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
5057 sizeof(guid_str.buf));
5059 /* Now set the value into the RDN, without parsing it */
5060 ret = ldb_dn_set_component(
5064 deleted_child_rdn_val);
5073 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
5074 struct ldb_context *ldb,
5078 const struct ldb_val *rdn_val;
5079 const char *rdn_name;
5080 struct ldb_dn *new_dn;
5083 rdn_val = ldb_dn_get_rdn_val(dn);
5084 rdn_name = ldb_dn_get_rdn_name(dn);
5085 if (!rdn_val || !rdn_name) {
5089 new_dn = ldb_dn_get_parent(mem_ctx, dn);
5094 ret = replmd_make_prefix_child_dn(mem_ctx,
5100 if (ret != LDB_SUCCESS) {
5109 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
5110 struct ldb_context *ldb,
5112 const char *rdn_name,
5113 const struct ldb_val *rdn_value,
5116 return replmd_make_prefix_child_dn(tmp_ctx,
5126 perform a modify operation which sets the rDN and name attributes to
5127 their current values. This has the effect of changing these
5128 attributes to have been last updated by the current DC. This is
5129 needed to ensure that renames performed as part of conflict
5130 resolution are propagated to other DCs
5132 static int replmd_name_modify(struct replmd_replicated_request *ar,
5133 struct ldb_request *req, struct ldb_dn *dn)
5135 struct ldb_message *msg;
5136 const char *rdn_name;
5137 const struct ldb_val *rdn_val;
5138 const struct dsdb_attribute *rdn_attr;
5141 msg = ldb_msg_new(req);
5147 rdn_name = ldb_dn_get_rdn_name(dn);
5148 if (rdn_name == NULL) {
5152 /* normalize the rdn attribute name */
5153 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5154 if (rdn_attr == NULL) {
5157 rdn_name = rdn_attr->lDAPDisplayName;
5159 rdn_val = ldb_dn_get_rdn_val(dn);
5160 if (rdn_val == NULL) {
5164 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5167 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
5170 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5173 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
5178 * We have to mark this as a replicated update otherwise
5179 * schema_data may reject a rename in the schema partition
5182 ret = dsdb_module_modify(ar->module, msg,
5183 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5185 if (ret != LDB_SUCCESS) {
5186 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5187 ldb_dn_get_linearized(dn),
5188 ldb_errstring(ldb_module_get_ctx(ar->module))));
5198 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5199 ldb_dn_get_linearized(dn)));
5200 return LDB_ERR_OPERATIONS_ERROR;
5205 callback for conflict DN handling where we have renamed the incoming
5206 record. After renaming it, we need to ensure the change of name and
5207 rDN for the incoming record is seen as an originating update by this DC.
5209 This also handles updating lastKnownParent for entries sent to lostAndFound
5211 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5213 struct replmd_replicated_request *ar =
5214 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5215 struct ldb_dn *conflict_dn = NULL;
5218 if (ares->error != LDB_SUCCESS) {
5219 /* call the normal callback for everything except success */
5220 return replmd_op_callback(req, ares);
5223 switch (req->operation) {
5225 conflict_dn = req->op.add.message->dn;
5228 conflict_dn = req->op.mod.message->dn;
5231 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5234 /* perform a modify of the rDN and name of the record */
5235 ret = replmd_name_modify(ar, req, conflict_dn);
5236 if (ret != LDB_SUCCESS) {
5238 return replmd_op_callback(req, ares);
5241 if (ar->objs->objects[ar->index_current].last_known_parent) {
5242 struct ldb_message *msg = ldb_msg_new(req);
5244 ldb_module_oom(ar->module);
5245 return LDB_ERR_OPERATIONS_ERROR;
5248 msg->dn = req->op.add.message->dn;
5250 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5251 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5252 if (ret != LDB_SUCCESS) {
5253 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5254 ldb_module_oom(ar->module);
5257 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5259 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5260 if (ret != LDB_SUCCESS) {
5261 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5262 ldb_dn_get_linearized(msg->dn),
5263 ldb_errstring(ldb_module_get_ctx(ar->module))));
5269 return replmd_op_callback(req, ares);
5275 * A helper for replmd_op_possible_conflict_callback() and
5276 * replmd_replicated_handle_rename()
5278 static int incoming_dn_should_be_renamed(TALLOC_CTX *mem_ctx,
5279 struct replmd_replicated_request *ar,
5280 struct ldb_dn *conflict_dn,
5281 struct ldb_result **res,
5282 bool *rename_incoming_record)
5286 enum ndr_err_code ndr_err;
5287 const struct ldb_val *omd_value = NULL;
5288 struct replPropertyMetaDataBlob omd, *rmd = NULL;
5289 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5290 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5291 struct replPropertyMetaData1 *omd_name = NULL;
5292 struct replPropertyMetaData1 *rmd_name = NULL;
5293 struct ldb_message *msg = NULL;
5295 ret = samdb_rodc(ldb, &rodc);
5296 if (ret != LDB_SUCCESS) {
5297 ldb_asprintf_errstring(
5299 "Failed to determine if we are an RODC when attempting "
5300 "to form conflict DN: %s",
5301 ldb_errstring(ldb));
5302 return LDB_ERR_OPERATIONS_ERROR;
5307 * We are on an RODC, or were a GC for this
5308 * partition, so we have to fail this until
5309 * someone who owns the partition sorts it
5312 ldb_asprintf_errstring(
5314 "Conflict adding object '%s' from incoming replication "
5315 "but we are read only for the partition. \n"
5316 " - We must fail the operation until a master for this "
5317 "partition resolves the conflict",
5318 ldb_dn_get_linearized(conflict_dn));
5319 return LDB_ERR_OPERATIONS_ERROR;
5323 * first we need the replPropertyMetaData attribute from the
5326 ret = dsdb_module_search_dn(ar->module, mem_ctx, res, conflict_dn,
5328 DSDB_FLAG_NEXT_MODULE |
5329 DSDB_SEARCH_SHOW_DELETED |
5330 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5331 if (ret != LDB_SUCCESS) {
5332 DBG_ERR(__location__
5333 ": Unable to find object for conflicting record '%s'\n",
5334 ldb_dn_get_linearized(conflict_dn));
5335 return LDB_ERR_OPERATIONS_ERROR;
5338 msg = (*res)->msgs[0];
5339 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
5340 if (omd_value == NULL) {
5341 DBG_ERR(__location__
5342 ": Unable to find replPropertyMetaData for conflicting "
5344 ldb_dn_get_linearized(conflict_dn));
5345 return LDB_ERR_OPERATIONS_ERROR;
5348 ndr_err = ndr_pull_struct_blob(
5349 omd_value, msg, &omd,
5350 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5351 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5352 DBG_ERR(__location__
5353 ": Failed to parse old replPropertyMetaData for %s\n",
5354 ldb_dn_get_linearized(conflict_dn));
5355 return LDB_ERR_OPERATIONS_ERROR;
5358 rmd = ar->objs->objects[ar->index_current].meta_data;
5361 * we decide which is newer based on the RPMD on the name
5362 * attribute. See [MS-DRSR] ResolveNameConflict.
5364 * We expect omd_name to be present, as this is from a local
5365 * search, but while rmd_name should have been given to us by
5366 * the remote server, if it is missing we just prefer the
5368 * replmd_replPropertyMetaData1_new_should_be_taken()
5370 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd,
5371 DRSUAPI_ATTID_name);
5372 omd_name = replmd_replPropertyMetaData1_find_attid(&omd,
5373 DRSUAPI_ATTID_name);
5375 DBG_ERR(__location__
5376 ": Failed to find name attribute in "
5377 "local LDB replPropertyMetaData for %s\n",
5378 ldb_dn_get_linearized(conflict_dn));
5379 return LDB_ERR_OPERATIONS_ERROR;
5383 * Should we preserve the current record, and so rename the
5384 * incoming record to be a conflict?
5386 *rename_incoming_record =
5387 !replmd_replPropertyMetaData1_new_should_be_taken(
5388 (ar->objs->dsdb_repl_flags &
5389 DSDB_REPL_FLAG_PRIORITISE_INCOMING),
5390 omd_name, rmd_name);
5397 callback for replmd_replicated_apply_add()
5398 This copes with the creation of conflict records in the case where
5399 the DN exists, but with a different objectGUID
5401 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))
5403 struct ldb_dn *conflict_dn;
5404 struct replmd_replicated_request *ar =
5405 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5406 struct ldb_result *res;
5408 bool rename_incoming_record;
5409 struct ldb_message *msg;
5410 struct ldb_request *down_req = NULL;
5412 /* call the normal callback for success */
5413 if (ares->error == LDB_SUCCESS) {
5414 return callback(req, ares);
5418 * we have a conflict, and need to decide if we will keep the
5419 * new record or the old record
5422 msg = ar->objs->objects[ar->index_current].msg;
5423 conflict_dn = msg->dn;
5425 /* For failures other than conflicts, fail the whole operation here */
5426 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5427 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5428 ldb_dn_get_linearized(conflict_dn),
5429 ldb_errstring(ldb_module_get_ctx(ar->module)));
5431 return ldb_module_done(ar->req, NULL, NULL,
5432 LDB_ERR_OPERATIONS_ERROR);
5436 ret = incoming_dn_should_be_renamed(req, ar, conflict_dn, &res,
5437 &rename_incoming_record);
5438 if (ret != LDB_SUCCESS) {
5442 if (rename_incoming_record) {
5444 struct ldb_dn *new_dn;
5446 guid = samdb_result_guid(msg, "objectGUID");
5447 if (GUID_all_zero(&guid)) {
5448 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5449 ldb_dn_get_linearized(conflict_dn)));
5452 new_dn = replmd_conflict_dn(req,
5453 ldb_module_get_ctx(ar->module),
5454 conflict_dn, &guid);
5455 if (new_dn == NULL) {
5456 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5457 ldb_dn_get_linearized(conflict_dn)));
5461 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5462 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5464 /* re-submit the request, but with the new DN */
5465 callback = replmd_op_name_modify_callback;
5468 /* we are renaming the existing record */
5470 struct ldb_dn *new_dn;
5472 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5473 if (GUID_all_zero(&guid)) {
5474 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5475 ldb_dn_get_linearized(conflict_dn)));
5479 new_dn = replmd_conflict_dn(req,
5480 ldb_module_get_ctx(ar->module),
5481 conflict_dn, &guid);
5482 if (new_dn == NULL) {
5483 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5484 ldb_dn_get_linearized(conflict_dn)));
5488 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5489 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5491 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5492 DSDB_FLAG_OWN_MODULE, req);
5493 if (ret != LDB_SUCCESS) {
5494 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5495 ldb_dn_get_linearized(conflict_dn),
5496 ldb_dn_get_linearized(new_dn),
5497 ldb_errstring(ldb_module_get_ctx(ar->module))));
5502 * now we need to ensure that the rename is seen as an
5503 * originating update. We do that with a modify.
5505 ret = replmd_name_modify(ar, req, new_dn);
5506 if (ret != LDB_SUCCESS) {
5510 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5511 ldb_dn_get_linearized(req->op.add.message->dn)));
5514 ret = ldb_build_add_req(&down_req,
5515 ldb_module_get_ctx(ar->module),
5522 if (ret != LDB_SUCCESS) {
5525 LDB_REQ_SET_LOCATION(down_req);
5527 /* current partition control needed by "repmd_op_callback" */
5528 ret = ldb_request_add_control(down_req,
5529 DSDB_CONTROL_CURRENT_PARTITION_OID,
5531 if (ret != LDB_SUCCESS) {
5532 return replmd_replicated_request_error(ar, ret);
5535 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5536 /* this tells the partition module to make it a
5537 partial replica if creating an NC */
5538 ret = ldb_request_add_control(down_req,
5539 DSDB_CONTROL_PARTIAL_REPLICA,
5541 if (ret != LDB_SUCCESS) {
5542 return replmd_replicated_request_error(ar, ret);
5547 * Finally we re-run the add, otherwise the new record won't
5548 * exist, as we are here because of that exact failure!
5550 return ldb_next_request(ar->module, down_req);
5553 /* on failure make the caller get the error. This means
5554 * replication will stop with an error, but there is not much
5557 if (ret == LDB_SUCCESS) {
5558 ret = LDB_ERR_OPERATIONS_ERROR;
5560 return ldb_module_done(ar->req, NULL, NULL,
5565 callback for replmd_replicated_apply_add()
5566 This copes with the creation of conflict records in the case where
5567 the DN exists, but with a different objectGUID
5569 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5571 struct replmd_replicated_request *ar =
5572 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5574 if (ar->objs->objects[ar->index_current].last_known_parent) {
5575 /* This is like a conflict DN, where we put the object in LostAndFound
5576 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5577 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5580 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5584 this is called when a new object comes in over DRS
5586 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5588 struct ldb_context *ldb;
5589 struct ldb_request *change_req;
5590 enum ndr_err_code ndr_err;
5591 struct ldb_message *msg;
5592 struct replPropertyMetaDataBlob *md;
5593 struct ldb_val md_value;
5596 bool remote_isDeleted = false;
5599 time_t t = time(NULL);
5600 const struct ldb_val *rdn_val;
5601 struct replmd_private *replmd_private =
5602 talloc_get_type(ldb_module_get_private(ar->module),
5603 struct replmd_private);
5604 unix_to_nt_time(&now, t);
5606 ldb = ldb_module_get_ctx(ar->module);
5607 msg = ar->objs->objects[ar->index_current].msg;
5608 md = ar->objs->objects[ar->index_current].meta_data;
5609 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5611 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5612 if (ret != LDB_SUCCESS) {
5613 return replmd_replicated_request_error(ar, ret);
5616 ret = dsdb_msg_add_guid(msg,
5617 &ar->objs->objects[ar->index_current].object_guid,
5619 if (ret != LDB_SUCCESS) {
5620 return replmd_replicated_request_error(ar, ret);
5623 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5624 if (ret != LDB_SUCCESS) {
5625 return replmd_replicated_request_error(ar, ret);
5628 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5629 if (ret != LDB_SUCCESS) {
5630 return replmd_replicated_request_error(ar, ret);
5633 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5634 if (ret != LDB_SUCCESS) {
5635 return replmd_replicated_request_error(ar, ret);
5638 /* remove any message elements that have zero values */
5639 for (i=0; i<msg->num_elements; i++) {
5640 struct ldb_message_element *el = &msg->elements[i];
5642 if (el->num_values == 0) {
5643 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5644 ldb_asprintf_errstring(ldb, __location__
5645 ": empty objectClass sent on %s, aborting replication\n",
5646 ldb_dn_get_linearized(msg->dn));
5647 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5650 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5652 ldb_msg_remove_element(msg, &msg->elements[i]);
5659 struct GUID_txt_buf guid_txt;
5661 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5664 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5665 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5668 } else if (DEBUGLVL(4)) {
5669 struct GUID_txt_buf guid_txt;
5670 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5671 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5672 ldb_dn_get_linearized(msg->dn)));
5674 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5675 "isDeleted", false);
5678 * the meta data array is already sorted by the caller, except
5679 * for the RDN, which needs to be added.
5683 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5684 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5685 md, ar, now, is_schema_nc,
5687 if (ret != LDB_SUCCESS) {
5688 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5689 return replmd_replicated_request_error(ar, ret);
5692 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5693 if (ret != LDB_SUCCESS) {
5694 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5695 return replmd_replicated_request_error(ar, ret);
5698 for (i=0; i < md->ctr.ctr1.count; i++) {
5699 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5701 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5702 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5703 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5704 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5705 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5707 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5708 if (ret != LDB_SUCCESS) {
5709 return replmd_replicated_request_error(ar, ret);
5712 replmd_ldb_message_sort(msg, ar->schema);
5714 if (!remote_isDeleted) {
5716 * Ensure any local ACL inheritence is applied from
5717 * the parent object.
5719 * This is needed because descriptor is above
5720 * repl_meta_data in the module stack, so this will
5721 * not be trigered 'naturally' by the flow of
5724 ret = dsdb_module_schedule_sd_propagation(ar->module,
5725 ar->objs->partition_dn,
5726 ar->objs->objects[ar->index_current].object_guid,
5728 if (ret != LDB_SUCCESS) {
5729 return replmd_replicated_request_error(ar, ret);
5733 ar->isDeleted = remote_isDeleted;
5735 ret = ldb_build_add_req(&change_req,
5741 replmd_op_add_callback,
5743 LDB_REQ_SET_LOCATION(change_req);
5744 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5746 /* current partition control needed by "repmd_op_callback" */
5747 ret = ldb_request_add_control(change_req,
5748 DSDB_CONTROL_CURRENT_PARTITION_OID,
5750 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5752 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5753 /* this tells the partition module to make it a
5754 partial replica if creating an NC */
5755 ret = ldb_request_add_control(change_req,
5756 DSDB_CONTROL_PARTIAL_REPLICA,
5758 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5761 return ldb_next_request(ar->module, change_req);
5764 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5765 struct ldb_reply *ares)
5767 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5768 struct replmd_replicated_request);
5772 return ldb_module_done(ar->req, NULL, NULL,
5773 LDB_ERR_OPERATIONS_ERROR);
5777 * The error NO_SUCH_OBJECT is not expected, unless the search
5778 * base is the partition DN, and that case doesn't happen here
5779 * because then we wouldn't get a parent_guid_value in any
5782 if (ares->error != LDB_SUCCESS) {
5783 return ldb_module_done(ar->req, ares->controls,
5784 ares->response, ares->error);
5787 switch (ares->type) {
5788 case LDB_REPLY_ENTRY:
5790 struct ldb_message *parent_msg = ares->message;
5791 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5792 struct ldb_dn *parent_dn = NULL;
5795 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5796 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5797 /* Per MS-DRSR 4.1.10.6.10
5798 * FindBestParentObject we need to move this
5799 * new object under a deleted object to
5801 struct ldb_dn *nc_root;
5803 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5804 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5805 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5806 "No suitable NC root found for %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_dn_get_linearized(parent_msg->dn));
5811 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5812 } else if (ret != LDB_SUCCESS) {
5813 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5814 "Unable to find NC root for %s: %s. "
5815 "We need to move this object because parent object %s "
5816 "is deleted, but this object is not.",
5817 ldb_dn_get_linearized(msg->dn),
5818 ldb_errstring(ldb_module_get_ctx(ar->module)),
5819 ldb_dn_get_linearized(parent_msg->dn));
5820 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5823 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5825 DS_GUID_LOSTANDFOUND_CONTAINER,
5827 if (ret != LDB_SUCCESS) {
5828 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5829 "Unable to find LostAndFound Container for %s "
5830 "in partition %s: %s. "
5831 "We need to move this object because parent object %s "
5832 "is deleted, but this object is not.",
5833 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5834 ldb_errstring(ldb_module_get_ctx(ar->module)),
5835 ldb_dn_get_linearized(parent_msg->dn));
5836 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5838 ar->objs->objects[ar->index_current].last_known_parent
5839 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5843 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5846 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5848 comp_num = ldb_dn_get_comp_num(msg->dn);
5850 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5852 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5855 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5857 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5861 case LDB_REPLY_REFERRAL:
5862 /* we ignore referrals */
5865 case LDB_REPLY_DONE:
5867 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5868 struct GUID_txt_buf str_buf;
5869 if (ar->search_msg != NULL) {
5870 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5871 "No parent with GUID %s found for object locally known as %s",
5872 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5873 ldb_dn_get_linearized(ar->search_msg->dn));
5875 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5876 "No parent with GUID %s found for object remotely known as %s",
5877 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5878 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5882 * This error code is really important, as it
5883 * is the flag back to the callers to retry
5884 * this with DRSUAPI_DRS_GET_ANC, and so get
5885 * the parent objects before the child
5888 return ldb_module_done(ar->req, NULL, NULL,
5889 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5892 if (ar->search_msg != NULL) {
5893 ret = replmd_replicated_apply_merge(ar);
5895 ret = replmd_replicated_apply_add(ar);
5897 if (ret != LDB_SUCCESS) {
5898 return ldb_module_done(ar->req, NULL, NULL, ret);
5907 * Look for the parent object, so we put the new object in the right
5908 * place This is akin to NameObject in MS-DRSR - this routine and the
5909 * callbacks find the right parent name, and correct name for this
5913 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5915 struct ldb_context *ldb;
5919 struct ldb_request *search_req;
5920 static const char *attrs[] = {"isDeleted", NULL};
5921 struct GUID_txt_buf guid_str_buf;
5923 ldb = ldb_module_get_ctx(ar->module);
5925 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5926 if (ar->search_msg != NULL) {
5927 return replmd_replicated_apply_merge(ar);
5929 return replmd_replicated_apply_add(ar);
5933 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5936 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5937 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5939 ret = ldb_build_search_req(&search_req,
5942 ar->objs->partition_dn,
5948 replmd_replicated_apply_search_for_parent_callback,
5950 LDB_REQ_SET_LOCATION(search_req);
5952 ret = dsdb_request_add_controls(search_req,
5953 DSDB_SEARCH_SHOW_RECYCLED|
5954 DSDB_SEARCH_SHOW_DELETED|
5955 DSDB_SEARCH_SHOW_EXTENDED_DN);
5956 if (ret != LDB_SUCCESS) {
5960 return ldb_next_request(ar->module, search_req);
5964 handle renames that come in over DRS replication
5966 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5967 struct ldb_message *msg,
5968 struct ldb_request *parent,
5969 bool *renamed_to_conflict)
5972 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5973 struct ldb_result *res;
5974 struct ldb_dn *conflict_dn;
5975 bool rename_incoming_record;
5976 struct ldb_dn *new_dn;
5979 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5980 ldb_dn_get_linearized(ar->search_msg->dn),
5981 ldb_dn_get_linearized(msg->dn)));
5984 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5985 DSDB_FLAG_NEXT_MODULE, ar->req);
5986 if (ret == LDB_SUCCESS) {
5987 talloc_free(tmp_ctx);
5991 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5992 talloc_free(tmp_ctx);
5993 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5994 ldb_dn_get_linearized(ar->search_msg->dn),
5995 ldb_dn_get_linearized(msg->dn),
5996 ldb_errstring(ldb_module_get_ctx(ar->module)));
6000 conflict_dn = msg->dn;
6003 ret = incoming_dn_should_be_renamed(tmp_ctx, ar, conflict_dn, &res,
6004 &rename_incoming_record);
6005 if (ret != LDB_SUCCESS) {
6009 if (rename_incoming_record) {
6011 new_dn = replmd_conflict_dn(msg,
6012 ldb_module_get_ctx(ar->module),
6014 &ar->objs->objects[ar->index_current].object_guid);
6015 if (new_dn == NULL) {
6016 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6017 "Failed to form conflict DN for %s\n",
6018 ldb_dn_get_linearized(msg->dn));
6020 talloc_free(tmp_ctx);
6021 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6024 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
6025 DSDB_FLAG_NEXT_MODULE, ar->req);
6026 if (ret != LDB_SUCCESS) {
6027 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6028 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
6029 ldb_dn_get_linearized(conflict_dn),
6030 ldb_dn_get_linearized(ar->search_msg->dn),
6031 ldb_dn_get_linearized(new_dn),
6032 ldb_errstring(ldb_module_get_ctx(ar->module)));
6033 talloc_free(tmp_ctx);
6034 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6038 *renamed_to_conflict = true;
6039 talloc_free(tmp_ctx);
6043 /* we are renaming the existing record */
6045 guid = samdb_result_guid(res->msgs[0], "objectGUID");
6046 if (GUID_all_zero(&guid)) {
6047 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
6048 ldb_dn_get_linearized(conflict_dn)));
6052 new_dn = replmd_conflict_dn(tmp_ctx,
6053 ldb_module_get_ctx(ar->module),
6054 conflict_dn, &guid);
6055 if (new_dn == NULL) {
6056 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
6057 ldb_dn_get_linearized(conflict_dn)));
6061 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
6062 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
6064 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
6065 DSDB_FLAG_OWN_MODULE, ar->req);
6066 if (ret != LDB_SUCCESS) {
6067 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
6068 ldb_dn_get_linearized(conflict_dn),
6069 ldb_dn_get_linearized(new_dn),
6070 ldb_errstring(ldb_module_get_ctx(ar->module))));
6075 * now we need to ensure that the rename is seen as an
6076 * originating update. We do that with a modify.
6078 ret = replmd_name_modify(ar, ar->req, new_dn);
6079 if (ret != LDB_SUCCESS) {
6083 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
6084 ldb_dn_get_linearized(ar->search_msg->dn),
6085 ldb_dn_get_linearized(msg->dn)));
6088 * With the other record out of the way, do the rename we had
6091 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6092 DSDB_FLAG_NEXT_MODULE, ar->req);
6093 if (ret != LDB_SUCCESS) {
6094 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6095 ldb_dn_get_linearized(ar->search_msg->dn),
6096 ldb_dn_get_linearized(msg->dn),
6097 ldb_errstring(ldb_module_get_ctx(ar->module))));
6101 talloc_free(tmp_ctx);
6105 * On failure make the caller get the error
6106 * This means replication will stop with an error,
6107 * but there is not much else we can do. In the
6108 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6111 if (ret == LDB_SUCCESS) {
6112 ret = LDB_ERR_OPERATIONS_ERROR;
6115 talloc_free(tmp_ctx);
6120 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6122 struct ldb_context *ldb;
6123 struct ldb_request *change_req;
6124 enum ndr_err_code ndr_err;
6125 struct ldb_message *msg;
6126 struct replPropertyMetaDataBlob *rmd;
6127 struct replPropertyMetaDataBlob omd;
6128 const struct ldb_val *omd_value;
6129 struct replPropertyMetaDataBlob nmd;
6130 struct ldb_val nmd_value;
6131 struct GUID remote_parent_guid;
6134 unsigned int removed_attrs = 0;
6136 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6137 bool isDeleted = false;
6138 bool local_isDeleted = false;
6139 bool remote_isDeleted = false;
6140 bool take_remote_isDeleted = false;
6141 bool sd_updated = false;
6142 bool renamed = false;
6143 bool renamed_to_conflict = false;
6144 bool is_schema_nc = false;
6146 const struct ldb_val *old_rdn, *new_rdn;
6147 struct replmd_private *replmd_private =
6148 talloc_get_type(ldb_module_get_private(ar->module),
6149 struct replmd_private);
6151 time_t t = time(NULL);
6152 unix_to_nt_time(&now, t);
6154 ldb = ldb_module_get_ctx(ar->module);
6155 msg = ar->objs->objects[ar->index_current].msg;
6157 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6159 rmd = ar->objs->objects[ar->index_current].meta_data;
6163 /* find existing meta data */
6164 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6166 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6167 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6168 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6169 nt_status = ndr_map_error2ntstatus(ndr_err);
6170 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6173 if (omd.version != 1) {
6174 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6179 struct GUID_txt_buf guid_txt;
6181 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6182 LDB_CHANGETYPE_MODIFY, msg);
6183 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6186 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6188 ndr_print_struct_string(s,
6189 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6190 "existing replPropertyMetaData",
6192 ndr_print_struct_string(s,
6193 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6194 "incoming replPropertyMetaData",
6197 } else if (DEBUGLVL(4)) {
6198 struct GUID_txt_buf guid_txt;
6200 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6201 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6203 ldb_dn_get_linearized(msg->dn)));
6206 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6207 "isDeleted", false);
6208 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6209 "isDeleted", false);
6212 * Fill in the remote_parent_guid with the GUID or an all-zero
6215 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6216 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6218 remote_parent_guid = GUID_zero();
6222 * To ensure we follow a complex rename chain around, we have
6223 * to confirm that the DN is the same (mostly to confirm the
6224 * RDN) and the parentGUID is the same.
6226 * This ensures we keep things under the correct parent, which
6227 * replmd_replicated_handle_rename() will do.
6230 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6231 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6235 * handle renames, even just by case that come in over
6236 * DRS. Changes in the parent DN don't hit us here,
6237 * because the search for a parent will clean up those
6240 * We also have already filtered out the case where
6241 * the peer has an older name to what we have (see
6242 * replmd_replicated_apply_search_callback())
6244 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed_to_conflict);
6247 * This looks strange, but we must set this after any
6248 * rename, otherwise the SD propegation will not
6249 * happen (which might matter if we have a new parent)
6251 * The additional case of calling
6252 * replmd_op_name_modify_callback (below) is
6253 * controlled by renamed_to_conflict.
6258 if (ret != LDB_SUCCESS) {
6259 ldb_debug(ldb, LDB_DEBUG_FATAL,
6260 "replmd_replicated_request rename %s => %s failed - %s\n",
6261 ldb_dn_get_linearized(ar->search_msg->dn),
6262 ldb_dn_get_linearized(msg->dn),
6263 ldb_errstring(ldb));
6264 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6267 if (renamed_to_conflict == true) {
6269 * Set the callback to one that will fix up the name
6270 * metadata on the new conflict DN
6272 callback = replmd_op_name_modify_callback;
6277 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6278 nmd.ctr.ctr1.array = talloc_array(ar,
6279 struct replPropertyMetaData1,
6280 nmd.ctr.ctr1.count);
6281 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6283 /* first copy the old meta data */
6284 for (i=0; i < omd.ctr.ctr1.count; i++) {
6285 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6290 /* now merge in the new meta data */
6291 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6294 for (j=0; j < ni; j++) {
6297 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6301 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6302 ar->objs->dsdb_repl_flags,
6303 &nmd.ctr.ctr1.array[j],
6304 &rmd->ctr.ctr1.array[i]);
6306 /* replace the entry */
6307 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6308 if (ar->seq_num == 0) {
6309 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6310 if (ret != LDB_SUCCESS) {
6311 return replmd_replicated_request_error(ar, ret);
6314 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6315 switch (nmd.ctr.ctr1.array[j].attid) {
6316 case DRSUAPI_ATTID_ntSecurityDescriptor:
6319 case DRSUAPI_ATTID_isDeleted:
6320 take_remote_isDeleted = true;
6329 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6330 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6331 msg->elements[i-removed_attrs].name,
6332 ldb_dn_get_linearized(msg->dn),
6333 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6336 /* we don't want to apply this change so remove the attribute */
6337 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6344 if (found) continue;
6346 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6347 if (ar->seq_num == 0) {
6348 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6349 if (ret != LDB_SUCCESS) {
6350 return replmd_replicated_request_error(ar, ret);
6353 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6354 switch (nmd.ctr.ctr1.array[ni].attid) {
6355 case DRSUAPI_ATTID_ntSecurityDescriptor:
6358 case DRSUAPI_ATTID_isDeleted:
6359 take_remote_isDeleted = true;
6368 * finally correct the size of the meta_data array
6370 nmd.ctr.ctr1.count = ni;
6372 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6373 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6376 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6377 &nmd, ar, now, is_schema_nc,
6379 if (ret != LDB_SUCCESS) {
6380 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6381 return replmd_replicated_request_error(ar, ret);
6385 * sort the new meta data array
6387 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6388 if (ret != LDB_SUCCESS) {
6389 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6394 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6397 * This also controls SD propagation below
6399 if (take_remote_isDeleted) {
6400 isDeleted = remote_isDeleted;
6402 isDeleted = local_isDeleted;
6405 ar->isDeleted = isDeleted;
6408 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6410 if (msg->num_elements == 0) {
6411 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6414 return replmd_replicated_apply_isDeleted(ar);
6417 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6418 ar->index_current, msg->num_elements);
6422 * This is an new name for this object, so we must
6423 * inherit from the parent
6425 * This is needed because descriptor is above
6426 * repl_meta_data in the module stack, so this will
6427 * not be trigered 'naturally' by the flow of
6430 ret = dsdb_module_schedule_sd_propagation(ar->module,
6431 ar->objs->partition_dn,
6432 ar->objs->objects[ar->index_current].object_guid,
6434 if (ret != LDB_SUCCESS) {
6435 return ldb_operr(ldb);
6439 if (sd_updated && !isDeleted) {
6441 * This is an existing object, so there is no need to
6442 * inherit from the parent, but we must inherit any
6443 * incoming changes to our child objects.
6445 * This is needed because descriptor is above
6446 * repl_meta_data in the module stack, so this will
6447 * not be trigered 'naturally' by the flow of
6450 ret = dsdb_module_schedule_sd_propagation(ar->module,
6451 ar->objs->partition_dn,
6452 ar->objs->objects[ar->index_current].object_guid,
6454 if (ret != LDB_SUCCESS) {
6455 return ldb_operr(ldb);
6459 /* create the meta data value */
6460 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6461 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6462 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6463 nt_status = ndr_map_error2ntstatus(ndr_err);
6464 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6468 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6469 * and replPopertyMetaData attributes
6471 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6472 if (ret != LDB_SUCCESS) {
6473 return replmd_replicated_request_error(ar, ret);
6475 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6476 if (ret != LDB_SUCCESS) {
6477 return replmd_replicated_request_error(ar, ret);
6479 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6480 if (ret != LDB_SUCCESS) {
6481 return replmd_replicated_request_error(ar, ret);
6484 replmd_ldb_message_sort(msg, ar->schema);
6486 /* we want to replace the old values */
6487 for (i=0; i < msg->num_elements; i++) {
6488 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6489 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6490 if (msg->elements[i].num_values == 0) {
6491 ldb_asprintf_errstring(ldb, __location__
6492 ": objectClass removed on %s, aborting replication\n",
6493 ldb_dn_get_linearized(msg->dn));
6494 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6500 struct GUID_txt_buf guid_txt;
6502 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6503 LDB_CHANGETYPE_MODIFY,
6505 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6506 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6510 } else if (DEBUGLVL(4)) {
6511 struct GUID_txt_buf guid_txt;
6513 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6514 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6516 ldb_dn_get_linearized(msg->dn)));
6519 ret = ldb_build_mod_req(&change_req,
6527 LDB_REQ_SET_LOCATION(change_req);
6528 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6530 /* current partition control needed by "repmd_op_callback" */
6531 ret = ldb_request_add_control(change_req,
6532 DSDB_CONTROL_CURRENT_PARTITION_OID,
6534 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6536 return ldb_next_request(ar->module, change_req);
6539 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6540 struct ldb_reply *ares)
6542 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6543 struct replmd_replicated_request);
6547 return ldb_module_done(ar->req, NULL, NULL,
6548 LDB_ERR_OPERATIONS_ERROR);
6550 if (ares->error != LDB_SUCCESS &&
6551 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6552 return ldb_module_done(ar->req, ares->controls,
6553 ares->response, ares->error);
6556 switch (ares->type) {
6557 case LDB_REPLY_ENTRY:
6558 ar->search_msg = talloc_steal(ar, ares->message);
6561 case LDB_REPLY_REFERRAL:
6562 /* we ignore referrals */
6565 case LDB_REPLY_DONE:
6567 struct replPropertyMetaData1 *md_remote;
6568 struct replPropertyMetaData1 *md_local;
6570 struct replPropertyMetaDataBlob omd;
6571 const struct ldb_val *omd_value;
6572 struct replPropertyMetaDataBlob *rmd;
6573 struct ldb_message *msg;
6575 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6576 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6579 * This is the ADD case, find the appropriate parent,
6580 * as this object doesn't exist locally:
6582 if (ar->search_msg == NULL) {
6583 ret = replmd_replicated_apply_search_for_parent(ar);
6584 if (ret != LDB_SUCCESS) {
6585 return ldb_module_done(ar->req, NULL, NULL, ret);
6592 * Otherwise, in the MERGE case, work out if we are
6593 * attempting a rename, and if so find the parent the
6594 * newly renamed object wants to belong under (which
6595 * may not be the parent in it's attached string DN
6597 rmd = ar->objs->objects[ar->index_current].meta_data;
6601 /* find existing meta data */
6602 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6604 enum ndr_err_code ndr_err;
6605 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6606 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6607 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6608 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6609 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6612 if (omd.version != 1) {
6613 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6617 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6619 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6620 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6621 && GUID_all_zero(&ar->local_parent_guid)) {
6622 DEBUG(0, ("Refusing to replicate new version of %s "
6623 "as local object has an all-zero parentGUID attribute, "
6624 "despite not being an NC root\n",
6625 ldb_dn_get_linearized(ar->search_msg->dn)));
6626 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6630 * now we need to check for double renames. We could have a
6631 * local rename pending which our replication partner hasn't
6632 * received yet. We choose which one wins by looking at the
6633 * attribute stamps on the two objects, the newer one wins.
6635 * This also simply applies the correct algorithms for
6636 * determining if a change was made to name at all, or
6637 * if the object has just been renamed under the same
6640 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6641 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6643 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6644 ldb_dn_get_linearized(ar->search_msg->dn)));
6645 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6649 * if there is no name attribute given then we have to assume the
6650 * object we've received has the older name
6652 if (replmd_replPropertyMetaData1_new_should_be_taken(
6653 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6654 md_local, md_remote)) {
6655 struct GUID_txt_buf p_guid_local;
6656 struct GUID_txt_buf p_guid_remote;
6657 msg = ar->objs->objects[ar->index_current].msg;
6659 /* Merge on the existing object, with rename */
6661 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6662 "as incoming object changing to %s under %s\n",
6663 ldb_dn_get_linearized(ar->search_msg->dn),
6664 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6665 ldb_dn_get_linearized(msg->dn),
6666 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6668 ret = replmd_replicated_apply_search_for_parent(ar);
6670 struct GUID_txt_buf p_guid_local;
6671 struct GUID_txt_buf p_guid_remote;
6672 msg = ar->objs->objects[ar->index_current].msg;
6675 * Merge on the existing object, force no
6676 * rename (code below just to explain why in
6680 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6681 ldb_dn_get_linearized(msg->dn)) == 0) {
6682 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6683 GUID_equal(&ar->local_parent_guid,
6684 ar->objs->objects[ar->index_current].parent_guid)
6686 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6687 "despite incoming object changing parent to %s\n",
6688 ldb_dn_get_linearized(ar->search_msg->dn),
6689 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6690 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6694 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6695 " and rejecting older rename to %s under %s\n",
6696 ldb_dn_get_linearized(ar->search_msg->dn),
6697 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6698 ldb_dn_get_linearized(msg->dn),
6699 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6703 * This assignment ensures that the strcmp()
6704 * and GUID_equal() calls in
6705 * replmd_replicated_apply_merge() avoids the
6708 ar->objs->objects[ar->index_current].parent_guid =
6709 &ar->local_parent_guid;
6711 msg->dn = ar->search_msg->dn;
6712 ret = replmd_replicated_apply_merge(ar);
6714 if (ret != LDB_SUCCESS) {
6715 return ldb_module_done(ar->req, NULL, NULL, ret);
6725 * Returns true if we can group together processing this link attribute,
6726 * i.e. it has the same source-object and attribute ID as other links
6727 * already in the group
6729 static bool la_entry_matches_group(struct la_entry *la_entry,
6730 struct la_group *la_group)
6732 struct la_entry *prev = la_group->la_entries;
6734 return (la_entry->la->attid == prev->la->attid &&
6735 GUID_equal(&la_entry->la->identifier->guid,
6736 &prev->la->identifier->guid));
6740 * Creates a new la_entry to store replication info for a single
6743 static struct la_entry *
6744 create_la_entry(struct replmd_private *replmd_private,
6745 struct drsuapi_DsReplicaLinkedAttribute *la,
6746 uint32_t dsdb_repl_flags)
6748 struct la_entry *la_entry;
6750 if (replmd_private->la_ctx == NULL) {
6751 replmd_private->la_ctx = talloc_new(replmd_private);
6753 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6754 if (la_entry == NULL) {
6757 la_entry->la = talloc(la_entry,
6758 struct drsuapi_DsReplicaLinkedAttribute);
6759 if (la_entry->la == NULL) {
6760 talloc_free(la_entry);
6763 *la_entry->la = *la;
6764 la_entry->dsdb_repl_flags = dsdb_repl_flags;
6767 * we need to steal the non-scalars so they stay
6768 * around until the end of the transaction
6770 talloc_steal(la_entry->la, la_entry->la->identifier);
6771 talloc_steal(la_entry->la, la_entry->la->value.blob);
6777 * Stores the linked attributes received in the replication chunk - these get
6778 * applied at the end of the transaction. We also check that each linked
6779 * attribute is valid, i.e. source and target objects are known.
6781 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6783 int ret = LDB_SUCCESS;
6785 struct ldb_module *module = ar->module;
6786 struct replmd_private *replmd_private =
6787 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6788 struct la_group *la_group = NULL;
6789 struct ldb_context *ldb;
6790 TALLOC_CTX *tmp_ctx = NULL;
6791 struct ldb_message *src_msg = NULL;
6792 const struct dsdb_attribute *attr = NULL;
6794 ldb = ldb_module_get_ctx(module);
6796 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6798 /* save away the linked attributes for the end of the transaction */
6799 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6800 struct la_entry *la_entry;
6803 /* create an entry to store the received link attribute info */
6804 la_entry = create_la_entry(replmd_private,
6805 &ar->objs->linked_attributes[i],
6806 ar->objs->dsdb_repl_flags);
6807 if (la_entry == NULL) {
6809 return LDB_ERR_OPERATIONS_ERROR;
6813 * check if we're still dealing with the same source object
6816 new_srcobj = (la_group == NULL ||
6817 !la_entry_matches_group(la_entry, la_group));
6821 /* get a new mem_ctx to lookup the source object */
6822 TALLOC_FREE(tmp_ctx);
6823 tmp_ctx = talloc_new(ar);
6824 if (tmp_ctx == NULL) {
6826 return LDB_ERR_OPERATIONS_ERROR;
6829 /* verify the link source exists */
6830 ret = replmd_get_la_entry_source(module, la_entry,
6835 * When we fail to find the source object, the error
6836 * code we pass back here is really important. It flags
6837 * back to the callers to retry this request with
6838 * DRSUAPI_DRS_GET_ANC. This case should never happen
6839 * if we're replicating from a Samba DC, but it is
6840 * needed to talk to a Windows DC
6842 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6843 WERROR err = WERR_DS_DRA_MISSING_PARENT;
6844 ret = replmd_replicated_request_werror(ar,
6850 ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6852 if (ret != LDB_SUCCESS) {
6856 /* group the links together by source-object for efficiency */
6858 la_group = talloc_zero(replmd_private->la_ctx,
6860 if (la_group == NULL) {
6862 return LDB_ERR_OPERATIONS_ERROR;
6864 DLIST_ADD(replmd_private->la_list, la_group);
6866 DLIST_ADD(la_group->la_entries, la_entry);
6867 replmd_private->total_links++;
6870 TALLOC_FREE(tmp_ctx);
6874 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6876 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6878 struct ldb_context *ldb;
6882 struct ldb_request *search_req;
6883 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6884 "parentGUID", "instanceType",
6885 "replPropertyMetaData", "nTSecurityDescriptor",
6886 "isDeleted", NULL };
6887 struct GUID_txt_buf guid_str_buf;
6889 if (ar->index_current >= ar->objs->num_objects) {
6892 * Now that we've applied all the objects, check the new linked
6893 * attributes and store them (we apply them in .prepare_commit)
6895 ret = replmd_store_linked_attributes(ar);
6897 if (ret != LDB_SUCCESS) {
6901 /* done applying objects, move on to the next stage */
6902 return replmd_replicated_uptodate_vector(ar);
6905 ldb = ldb_module_get_ctx(ar->module);
6906 ar->search_msg = NULL;
6907 ar->isDeleted = false;
6909 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6912 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6913 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6915 ret = ldb_build_search_req(&search_req,
6918 ar->objs->partition_dn,
6924 replmd_replicated_apply_search_callback,
6926 LDB_REQ_SET_LOCATION(search_req);
6928 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6930 if (ret != LDB_SUCCESS) {
6934 return ldb_next_request(ar->module, search_req);
6938 * Returns true if we need to do extra processing to handle deleted object
6939 * changes received via replication
6941 static bool replmd_should_apply_isDeleted(struct replmd_replicated_request *ar,
6942 struct ldb_message *msg)
6944 struct ldb_dn *deleted_objects_dn;
6947 if (!ar->isDeleted) {
6949 /* not a deleted object, so don't set isDeleted */
6953 ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module),
6955 &deleted_objects_dn);
6958 * if the Deleted Object container lookup failed, then just apply
6959 * isDeleted (note that it doesn't exist for the Schema partition)
6961 if (ret != LDB_SUCCESS) {
6966 * the Deleted Objects container has isDeleted set but is not entirely
6967 * a deleted object, so DON'T re-apply isDeleted to it
6969 if (ldb_dn_compare(msg->dn, deleted_objects_dn) == 0) {
6977 * This is essentially a wrapper for replmd_replicated_apply_next()
6979 * This is needed to ensure that both codepaths call this handler.
6981 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6983 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6985 bool apply_isDeleted;
6986 struct ldb_request *del_req = NULL;
6987 struct ldb_result *res = NULL;
6988 TALLOC_CTX *tmp_ctx = NULL;
6990 apply_isDeleted = replmd_should_apply_isDeleted(ar, msg);
6992 if (!apply_isDeleted) {
6995 ar->index_current++;
6996 return replmd_replicated_apply_next(ar);
7000 * Do a delete here again, so that if there is
7001 * anything local that conflicts with this
7002 * object being deleted, it is removed. This
7003 * includes links. See MS-DRSR 4.1.10.6.9
7006 * If the object is already deleted, and there
7007 * is no more work required, it doesn't do
7011 /* This has been updated to point to the DN we eventually did the modify on */
7013 tmp_ctx = talloc_new(ar);
7015 ret = ldb_oom(ldb_module_get_ctx(ar->module));
7019 res = talloc_zero(tmp_ctx, struct ldb_result);
7021 ret = ldb_oom(ldb_module_get_ctx(ar->module));
7022 talloc_free(tmp_ctx);
7026 /* Build a delete request, which hopefully will artually turn into nothing */
7027 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
7031 ldb_modify_default_callback,
7033 LDB_REQ_SET_LOCATION(del_req);
7034 if (ret != LDB_SUCCESS) {
7035 talloc_free(tmp_ctx);
7040 * This is the guts of the call, call back
7041 * into our delete code, but setting the
7042 * re_delete flag so we delete anything that
7043 * shouldn't be there on a deleted or recycled
7046 ret = replmd_delete_internals(ar->module, del_req, true);
7047 if (ret == LDB_SUCCESS) {
7048 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
7051 talloc_free(tmp_ctx);
7052 if (ret != LDB_SUCCESS) {
7056 ar->index_current++;
7057 return replmd_replicated_apply_next(ar);
7060 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
7061 struct ldb_reply *ares)
7063 struct ldb_context *ldb;
7064 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7065 struct replmd_replicated_request);
7066 ldb = ldb_module_get_ctx(ar->module);
7069 return ldb_module_done(ar->req, NULL, NULL,
7070 LDB_ERR_OPERATIONS_ERROR);
7072 if (ares->error != LDB_SUCCESS) {
7073 return ldb_module_done(ar->req, ares->controls,
7074 ares->response, ares->error);
7077 if (ares->type != LDB_REPLY_DONE) {
7078 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
7079 return ldb_module_done(ar->req, NULL, NULL,
7080 LDB_ERR_OPERATIONS_ERROR);
7085 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7088 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
7090 struct ldb_context *ldb;
7091 struct ldb_request *change_req;
7092 enum ndr_err_code ndr_err;
7093 struct ldb_message *msg;
7094 struct replUpToDateVectorBlob ouv;
7095 const struct ldb_val *ouv_value;
7096 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
7097 struct replUpToDateVectorBlob nuv;
7098 struct ldb_val nuv_value;
7099 struct ldb_message_element *nuv_el = NULL;
7100 struct ldb_message_element *orf_el = NULL;
7101 struct repsFromToBlob nrf;
7102 struct ldb_val *nrf_value = NULL;
7103 struct ldb_message_element *nrf_el = NULL;
7107 time_t t = time(NULL);
7110 uint32_t instanceType;
7112 ldb = ldb_module_get_ctx(ar->module);
7113 ruv = ar->objs->uptodateness_vector;
7119 unix_to_nt_time(&now, t);
7121 if (ar->search_msg == NULL) {
7122 /* this happens for a REPL_OBJ call where we are
7123 creating the target object by replicating it. The
7124 subdomain join code does this for the partition DN
7126 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
7127 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7130 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
7131 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
7132 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
7133 ldb_dn_get_linearized(ar->search_msg->dn)));
7134 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7138 * first create the new replUpToDateVector
7140 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
7142 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
7143 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
7144 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7145 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7146 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7149 if (ouv.version != 2) {
7150 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7155 * the new uptodateness vector will at least
7156 * contain 1 entry, one for the source_dsa
7158 * plus optional values from our old vector and the one from the source_dsa
7160 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
7161 if (ruv) nuv.ctr.ctr2.count += ruv->count;
7162 nuv.ctr.ctr2.cursors = talloc_array(ar,
7163 struct drsuapi_DsReplicaCursor2,
7164 nuv.ctr.ctr2.count);
7165 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7167 /* first copy the old vector */
7168 for (i=0; i < ouv.ctr.ctr2.count; i++) {
7169 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
7173 /* merge in the source_dsa vector is available */
7174 for (i=0; (ruv && i < ruv->count); i++) {
7177 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7178 &ar->our_invocation_id)) {
7182 for (j=0; j < ni; j++) {
7183 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7184 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7190 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7191 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7196 if (found) continue;
7198 /* if it's not there yet, add it */
7199 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7204 * finally correct the size of the cursors array
7206 nuv.ctr.ctr2.count = ni;
7211 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7214 * create the change ldb_message
7216 msg = ldb_msg_new(ar);
7217 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7218 msg->dn = ar->search_msg->dn;
7220 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7221 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7222 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7223 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7224 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7226 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7227 if (ret != LDB_SUCCESS) {
7228 return replmd_replicated_request_error(ar, ret);
7230 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7233 * now create the new repsFrom value from the given repsFromTo1 structure
7237 nrf.ctr.ctr1 = *ar->objs->source_dsa;
7238 nrf.ctr.ctr1.last_attempt = now;
7239 nrf.ctr.ctr1.last_success = now;
7240 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7243 * first see if we already have a repsFrom value for the current source dsa
7244 * if so we'll later replace this value
7246 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7248 for (i=0; i < orf_el->num_values; i++) {
7249 struct repsFromToBlob *trf;
7251 trf = talloc(ar, struct repsFromToBlob);
7252 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7254 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7255 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7256 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7257 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7258 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7261 if (trf->version != 1) {
7262 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7266 * we compare the source dsa objectGUID not the invocation_id
7267 * because we want only one repsFrom value per source dsa
7268 * and when the invocation_id of the source dsa has changed we don't need
7269 * the old repsFrom with the old invocation_id
7271 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7272 &ar->objs->source_dsa->source_dsa_obj_guid)) {
7278 nrf_value = &orf_el->values[i];
7283 * copy over all old values to the new ldb_message
7285 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7286 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7291 * if we haven't found an old repsFrom value for the current source dsa
7292 * we'll add a new value
7295 struct ldb_val zero_value;
7296 ZERO_STRUCT(zero_value);
7297 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7298 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7300 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7303 /* we now fill the value which is already attached to ldb_message */
7304 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7306 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7307 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7308 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7309 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7313 * the ldb_message_element for the attribute, has all the old values and the new one
7314 * so we'll replace the whole attribute with all values
7316 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7318 if (CHECK_DEBUGLVL(4)) {
7319 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7320 LDB_CHANGETYPE_MODIFY,
7322 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7326 /* prepare the ldb_modify() request */
7327 ret = ldb_build_mod_req(&change_req,
7333 replmd_replicated_uptodate_modify_callback,
7335 LDB_REQ_SET_LOCATION(change_req);
7336 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7338 return ldb_next_request(ar->module, change_req);
7341 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7342 struct ldb_reply *ares)
7344 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7345 struct replmd_replicated_request);
7349 return ldb_module_done(ar->req, NULL, NULL,
7350 LDB_ERR_OPERATIONS_ERROR);
7352 if (ares->error != LDB_SUCCESS &&
7353 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7354 return ldb_module_done(ar->req, ares->controls,
7355 ares->response, ares->error);
7358 switch (ares->type) {
7359 case LDB_REPLY_ENTRY:
7360 ar->search_msg = talloc_steal(ar, ares->message);
7363 case LDB_REPLY_REFERRAL:
7364 /* we ignore referrals */
7367 case LDB_REPLY_DONE:
7368 ret = replmd_replicated_uptodate_modify(ar);
7369 if (ret != LDB_SUCCESS) {
7370 return ldb_module_done(ar->req, NULL, NULL, ret);
7379 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7381 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7382 struct replmd_private *replmd_private =
7383 talloc_get_type_abort(ldb_module_get_private(ar->module),
7384 struct replmd_private);
7386 static const char *attrs[] = {
7387 "replUpToDateVector",
7392 struct ldb_request *search_req;
7394 ar->search_msg = NULL;
7397 * Let the caller know that we did an originating updates
7399 ar->objs->originating_updates = replmd_private->originating_updates;
7401 ret = ldb_build_search_req(&search_req,
7404 ar->objs->partition_dn,
7410 replmd_replicated_uptodate_search_callback,
7412 LDB_REQ_SET_LOCATION(search_req);
7413 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7415 return ldb_next_request(ar->module, search_req);
7420 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7422 struct ldb_context *ldb;
7423 struct dsdb_extended_replicated_objects *objs;
7424 struct replmd_replicated_request *ar;
7425 struct ldb_control **ctrls;
7428 ldb = ldb_module_get_ctx(module);
7430 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7432 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7434 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7435 return LDB_ERR_PROTOCOL_ERROR;
7438 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7439 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7440 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7441 return LDB_ERR_PROTOCOL_ERROR;
7444 ar = replmd_ctx_init(module, req);
7446 return LDB_ERR_OPERATIONS_ERROR;
7448 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7449 ar->apply_mode = true;
7451 ar->schema = dsdb_get_schema(ldb, ar);
7453 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7455 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7456 return LDB_ERR_CONSTRAINT_VIOLATION;
7459 ctrls = req->controls;
7461 if (req->controls) {
7462 req->controls = talloc_memdup(ar, req->controls,
7463 talloc_get_size(req->controls));
7464 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7467 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7468 if (ret != LDB_SUCCESS) {
7472 /* If this change contained linked attributes in the body
7473 * (rather than in the links section) we need to update
7474 * backlinks in linked_attributes */
7475 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7476 if (ret != LDB_SUCCESS) {
7480 ar->controls = req->controls;
7481 req->controls = ctrls;
7483 return replmd_replicated_apply_next(ar);
7487 * Checks how to handle an missing target - either we need to fail the
7488 * replication and retry with GET_TGT, ignore the link and continue, or try to
7489 * add a partial link to an unknown target.
7491 static int replmd_allow_missing_target(struct ldb_module *module,
7492 TALLOC_CTX *mem_ctx,
7493 struct ldb_dn *target_dn,
7494 struct ldb_dn *source_dn,
7497 uint32_t dsdb_repl_flags,
7499 const char * missing_str)
7501 struct ldb_context *ldb = ldb_module_get_ctx(module);
7505 * we may not be able to resolve link targets properly when
7506 * dealing with subsets of objects, e.g. the source is a
7507 * critical object and the target isn't
7510 * When we implement Trusted Domains we need to consider
7511 * whether they get treated as an incomplete replica here or not
7513 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7516 * Ignore the link. We don't increase the highwater-mark in
7517 * the object subset cases, so subsequent replications should
7518 * resolve any missing links
7520 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7521 ldb_dn_get_linearized(target_dn),
7522 ldb_dn_get_linearized(source_dn)));
7523 *ignore_link = true;
7527 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7531 if (is_in_same_nc) {
7534 * if the target is already be up-to-date there's no point in
7535 * retrying. This could be due to bad timing, or if a target
7536 * on a one-way link was deleted. We ignore the link rather
7537 * than failing the replication cycle completely
7539 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7540 *ignore_link = true;
7541 DBG_WARNING("%s is %s "
7542 "but up to date. Ignoring link from %s\n",
7543 ldb_dn_get_linearized(target_dn), missing_str,
7544 ldb_dn_get_linearized(source_dn));
7548 /* otherwise fail the replication and retry with GET_TGT */
7549 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7551 ldb_dn_get_linearized(target_dn),
7552 GUID_string(mem_ctx, guid),
7553 ldb_dn_get_linearized(source_dn));
7554 return LDB_ERR_NO_SUCH_OBJECT;
7558 * The target of the cross-partition link is missing. Continue
7559 * and try to at least add the forward-link. This isn't great,
7560 * but a partial link can be fixed by dbcheck, so it's better
7561 * than dropping the link completely.
7563 *ignore_link = false;
7565 if (is_obj_commit) {
7568 * Only log this when we're actually committing the objects.
7569 * This avoids spurious logs, i.e. if we're just verifying the
7570 * received link during a join.
7572 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7573 missing_str, ldb_dn_get_linearized(target_dn),
7574 ldb_dn_get_linearized(source_dn));
7581 * Checks that the target object for a linked attribute exists.
7582 * @param guid returns the target object's GUID (is returned)if it exists)
7583 * @param ignore_link set to true if the linked attribute should be ignored
7584 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7586 static int replmd_check_target_exists(struct ldb_module *module,
7587 struct dsdb_dn *dsdb_dn,
7588 struct la_entry *la_entry,
7589 struct ldb_dn *source_dn,
7594 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7595 struct ldb_context *ldb = ldb_module_get_ctx(module);
7596 struct ldb_result *target_res;
7597 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7598 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7601 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7602 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7604 *ignore_link = false;
7605 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7607 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7610 * This strange behaviour (allowing a NULL/missing
7611 * GUID) originally comes from:
7613 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7614 * Author: Andrew Tridgell <tridge@samba.org>
7615 * Date: Mon Dec 21 21:21:55 2009 +1100
7617 * s4-drs: cope better with NULL GUIDS from DRS
7619 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7620 * need to match by DN if possible when seeing if we should update an
7623 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7625 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7627 DSDB_FLAG_NEXT_MODULE |
7628 DSDB_SEARCH_SHOW_RECYCLED |
7629 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7630 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7632 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7633 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7635 ldb_dn_get_linearized(dsdb_dn->dn),
7636 ldb_dn_get_linearized(source_dn));
7637 talloc_free(tmp_ctx);
7638 return LDB_ERR_OPERATIONS_ERROR;
7640 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7641 NULL, LDB_SCOPE_SUBTREE,
7643 DSDB_FLAG_NEXT_MODULE |
7644 DSDB_SEARCH_SHOW_RECYCLED |
7645 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7646 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7649 GUID_string(tmp_ctx, guid));
7652 if (ret != LDB_SUCCESS) {
7653 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7654 GUID_string(tmp_ctx, guid),
7655 ldb_errstring(ldb));
7656 talloc_free(tmp_ctx);
7660 if (target_res->count == 0) {
7663 * target object is unknown. Check whether to ignore the link,
7664 * fail the replication, or add a partial link
7666 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7667 source_dn, is_obj_commit, guid,
7668 la_entry->dsdb_repl_flags,
7669 ignore_link, "Unknown");
7671 } else if (target_res->count != 1) {
7672 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7673 GUID_string(tmp_ctx, guid));
7674 ret = LDB_ERR_OPERATIONS_ERROR;
7676 struct ldb_message *target_msg = target_res->msgs[0];
7678 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7680 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7681 replmd_deletion_state(module, target_msg,
7682 &target_deletion_state, NULL);
7685 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7686 * ProcessLinkValue(). Link updates should not be sent for
7687 * recycled and tombstone objects (deleting the links should
7688 * happen when we delete the object). This probably means our
7689 * copy of the target object isn't up to date.
7691 if (target_deletion_state >= OBJECT_RECYCLED) {
7694 * target object is deleted. Check whether to ignore the
7695 * link, fail the replication, or add a partial link
7697 ret = replmd_allow_missing_target(module, tmp_ctx,
7698 dsdb_dn->dn, source_dn,
7699 is_obj_commit, guid,
7700 la_entry->dsdb_repl_flags,
7701 ignore_link, "Deleted");
7705 talloc_free(tmp_ctx);
7710 * Extracts the key details about the source object for a
7711 * linked-attribute entry.
7712 * This returns the following details:
7713 * @param ret_attr the schema details for the linked attribute
7714 * @param source_msg the search result for the source object
7716 static int replmd_get_la_entry_source(struct ldb_module *module,
7717 struct la_entry *la_entry,
7718 TALLOC_CTX *mem_ctx,
7719 const struct dsdb_attribute **ret_attr,
7720 struct ldb_message **source_msg)
7722 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7723 struct ldb_context *ldb = ldb_module_get_ctx(module);
7724 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7726 const struct dsdb_attribute *attr;
7727 struct ldb_result *res;
7728 const char *attrs[4];
7731 linked_attributes[0]:
7732 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7734 identifier: struct drsuapi_DsReplicaObjectIdentifier
7735 __ndr_size : 0x0000003a (58)
7736 __ndr_size_sid : 0x00000000 (0)
7737 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7739 __ndr_size_dn : 0x00000000 (0)
7741 attid : DRSUAPI_ATTID_member (0x1F)
7742 value: struct drsuapi_DsAttributeValue
7743 __ndr_size : 0x0000007e (126)
7745 blob : DATA_BLOB length=126
7746 flags : 0x00000001 (1)
7747 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7748 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7749 meta_data: struct drsuapi_DsReplicaMetaData
7750 version : 0x00000015 (21)
7751 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7752 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7753 originating_usn : 0x000000000001e19c (123292)
7755 (for cases where the link is to a normal DN)
7756 &target: struct drsuapi_DsReplicaObjectIdentifier3
7757 __ndr_size : 0x0000007e (126)
7758 __ndr_size_sid : 0x0000001c (28)
7759 guid : 7639e594-db75-4086-b0d4-67890ae46031
7760 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7761 __ndr_size_dn : 0x00000022 (34)
7762 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7765 /* find the attribute being modified */
7766 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7768 struct GUID_txt_buf guid_str;
7769 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7771 GUID_buf_string(&la->identifier->guid,
7773 return LDB_ERR_OPERATIONS_ERROR;
7777 * All attributes listed here must be dealt with in some way
7778 * by replmd_process_linked_attribute() otherwise in the case
7779 * of isDeleted: FALSE the modify will fail with:
7781 * Failed to apply linked attribute change 'attribute 'isDeleted':
7782 * invalid modify flags on
7783 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7786 * This is becaue isDeleted is a Boolean, so FALSE is a
7787 * legitimate value (set by Samba's deletetest.py)
7789 attrs[0] = attr->lDAPDisplayName;
7790 attrs[1] = "isDeleted";
7791 attrs[2] = "isRecycled";
7795 * get the existing message from the db for the object with
7796 * this GUID, returning attribute being modified. We will then
7797 * use this msg as the basis for a modify call
7799 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7800 DSDB_FLAG_NEXT_MODULE |
7801 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7802 DSDB_SEARCH_SHOW_RECYCLED |
7803 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7804 DSDB_SEARCH_REVEAL_INTERNALS,
7806 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7807 if (ret != LDB_SUCCESS) {
7810 if (res->count != 1) {
7811 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7812 GUID_string(mem_ctx, &la->identifier->guid));
7813 return LDB_ERR_NO_SUCH_OBJECT;
7816 *source_msg = res->msgs[0];
7823 * Verifies the target object is known for a linked attribute
7825 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7826 TALLOC_CTX *mem_ctx,
7827 struct la_entry *la_entry,
7828 struct ldb_dn *src_dn,
7829 const struct dsdb_attribute *attr)
7831 int ret = LDB_SUCCESS;
7832 struct ldb_module *module = ar->module;
7833 struct dsdb_dn *tgt_dsdb_dn = NULL;
7834 struct GUID guid = GUID_zero();
7837 struct ldb_context *ldb = ldb_module_get_ctx(module);
7838 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7839 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7841 /* the value blob for the attribute holds the target object DN */
7842 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7843 la->value.blob, &tgt_dsdb_dn);
7844 if (!W_ERROR_IS_OK(status)) {
7845 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7846 attr->lDAPDisplayName,
7847 ldb_dn_get_linearized(src_dn),
7848 win_errstr(status));
7849 return LDB_ERR_OPERATIONS_ERROR;
7853 * We can skip the target object checks if we're only syncing critical
7854 * objects, or we know the target is up-to-date. If either case, we
7855 * still continue even if the target doesn't exist
7857 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7858 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7860 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7861 src_dn, false, &guid, &dummy);
7865 * When we fail to find the target object, the error code we pass
7866 * back here is really important. It flags back to the callers to
7867 * retry this request with DRSUAPI_DRS_GET_TGT
7869 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7870 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7877 * Finds the current active Parsed-DN value for a single-valued linked
7878 * attribute, if one exists.
7879 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7880 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7883 static int replmd_get_active_singleval_link(struct ldb_module *module,
7884 TALLOC_CTX *mem_ctx,
7885 struct parsed_dn pdn_list[],
7887 const struct dsdb_attribute *attr,
7888 struct parsed_dn **ret_pdn)
7894 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7896 /* nothing to do for multi-valued linked attributes */
7900 for (i = 0; i < count; i++) {
7901 int ret = LDB_SUCCESS;
7902 struct parsed_dn *pdn = &pdn_list[i];
7904 /* skip any inactive links */
7905 if (dsdb_dn_is_deleted_val(pdn->v)) {
7909 /* we've found an active value for this attribute */
7912 if (pdn->dsdb_dn == NULL) {
7913 struct ldb_context *ldb = ldb_module_get_ctx(module);
7915 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7916 attr->syntax->ldap_oid);
7922 /* no active link found */
7927 * @returns true if the replication linked attribute info is newer than we
7928 * already have in our DB
7929 * @param pdn the existing linked attribute info in our DB
7930 * @param la the new linked attribute info received during replication
7932 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7933 struct drsuapi_DsReplicaLinkedAttribute *la)
7935 /* see if this update is newer than what we have already */
7936 struct GUID invocation_id = GUID_zero();
7937 uint32_t version = 0;
7938 NTTIME change_time = 0;
7942 /* no existing info so update is newer */
7946 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7947 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7948 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7950 return replmd_update_is_newer(&invocation_id,
7951 &la->meta_data.originating_invocation_id,
7953 la->meta_data.version,
7955 la->meta_data.originating_change_time);
7959 * Marks an existing linked attribute value as deleted in the DB
7960 * @param pdn the parsed-DN of the target-value to delete
7962 static int replmd_delete_link_value(struct ldb_module *module,
7963 struct replmd_private *replmd_private,
7964 TALLOC_CTX *mem_ctx,
7965 struct ldb_dn *src_obj_dn,
7966 const struct dsdb_schema *schema,
7967 const struct dsdb_attribute *attr,
7970 struct GUID *target_guid,
7971 struct dsdb_dn *target_dsdb_dn,
7972 struct ldb_val *output_val)
7974 struct ldb_context *ldb = ldb_module_get_ctx(module);
7977 const struct GUID *invocation_id = NULL;
7981 unix_to_nt_time(&now, t);
7983 invocation_id = samdb_ntds_invocation_id(ldb);
7984 if (invocation_id == NULL) {
7985 return LDB_ERR_OPERATIONS_ERROR;
7988 /* if the existing link is active, remove its backlink */
7992 * NOTE WELL: After this we will never (at runtime) be
7993 * able to find this forward link (for instant
7994 * removal) if/when the link target is deleted.
7996 * We have dbcheck rules to cover this and cope otherwise
7997 * by filtering at runtime (i.e. in the extended_dn module).
7999 ret = replmd_add_backlink(module, replmd_private, schema,
8000 src_obj_dn, target_guid, false,
8002 if (ret != LDB_SUCCESS) {
8007 /* mark the existing value as deleted */
8008 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
8009 target_dsdb_dn, invocation_id, seq_num,
8010 seq_num, now, true);
8015 * Checks for a conflict in single-valued link attributes, and tries to
8016 * resolve the problem if possible.
8018 * Single-valued links should only ever have one active value. If we already
8019 * have an active link value, and during replication we receive an active link
8020 * value for a different target DN, then we need to resolve this inconsistency
8021 * and determine which value should be active. If the received info is better/
8022 * newer than the existing link attribute, then we need to set our existing
8023 * link as deleted. If the received info is worse/older, then we should continue
8024 * to add it, but set it as an inactive link.
8026 * Note that this is a corner-case that is unlikely to happen (but if it does
8027 * happen, we don't want it to break replication completely).
8029 * @param pdn_being_modified the parsed DN corresponding to the received link
8030 * target (note this is NULL if the link does not already exist in our DB)
8031 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
8032 * any existing active or inactive values for the attribute in our DB.
8033 * @param dsdb_dn the target DN for the received link attribute
8034 * @param add_as_inactive gets set to true if the received link is worse than
8035 * the existing link - it should still be added, but as an inactive link.
8037 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
8038 struct replmd_private *replmd_private,
8039 TALLOC_CTX *mem_ctx,
8040 struct ldb_dn *src_obj_dn,
8041 struct drsuapi_DsReplicaLinkedAttribute *la,
8042 struct dsdb_dn *dsdb_dn,
8043 struct parsed_dn *pdn_being_modified,
8044 struct parsed_dn *pdn_list,
8045 struct ldb_message_element *old_el,
8046 const struct dsdb_schema *schema,
8047 const struct dsdb_attribute *attr,
8049 bool *add_as_inactive)
8051 struct parsed_dn *active_pdn = NULL;
8052 bool update_is_newer = false;
8056 * check if there's a conflict for single-valued links, i.e. an active
8057 * linked attribute already exists, but it has a different target value
8059 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
8060 old_el->num_values, attr,
8063 if (ret != LDB_SUCCESS) {
8068 * If no active value exists (or the received info is for the currently
8069 * active value), then no conflict exists
8071 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
8075 DBG_WARNING("Link conflict for %s attribute on %s\n",
8076 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
8078 /* Work out how to resolve the conflict based on which info is better */
8079 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
8081 if (update_is_newer) {
8082 DBG_WARNING("Using received value %s, over existing target %s\n",
8083 ldb_dn_get_linearized(dsdb_dn->dn),
8084 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
8087 * Delete our existing active link. The received info will then
8088 * be added (through normal link processing) as the active value
8090 ret = replmd_delete_link_value(module, replmd_private, old_el,
8091 src_obj_dn, schema, attr,
8092 seq_num, true, &active_pdn->guid,
8093 active_pdn->dsdb_dn,
8096 if (ret != LDB_SUCCESS) {
8100 DBG_WARNING("Using existing target %s, over received value %s\n",
8101 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
8102 ldb_dn_get_linearized(dsdb_dn->dn));
8105 * we want to keep our existing active link and add the
8106 * received link as inactive
8108 *add_as_inactive = true;
8115 * Processes one linked attribute received via replication.
8116 * @param src_dn the DN of the source object for the link
8117 * @param attr schema info for the linked attribute
8118 * @param la_entry the linked attribute info received via DRS
8119 * @param element_ctx mem context for msg->element[] (when adding a new value
8120 * we need to realloc old_el->values)
8121 * @param old_el the corresponding msg->element[] for the linked attribute
8122 * @param pdn_list a (binary-searchable) parsed DN array for the existing link
8123 * values in the msg. E.g. for a group, this is the existing members.
8124 * @param change what got modified: either nothing, an existing link value was
8125 * modified, or a new link value was added.
8126 * @returns LDB_SUCCESS if OK, an error otherwise
8128 static int replmd_process_linked_attribute(struct ldb_module *module,
8129 TALLOC_CTX *mem_ctx,
8130 struct replmd_private *replmd_private,
8131 struct ldb_dn *src_dn,
8132 const struct dsdb_attribute *attr,
8133 struct la_entry *la_entry,
8134 struct ldb_request *parent,
8135 struct ldb_message_element *old_el,
8136 TALLOC_CTX *element_ctx,
8137 struct parsed_dn *pdn_list,
8138 replmd_link_changed *change)
8140 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
8141 struct ldb_context *ldb = ldb_module_get_ctx(module);
8142 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
8144 struct dsdb_dn *dsdb_dn = NULL;
8145 uint64_t seq_num = 0;
8146 struct parsed_dn *pdn, *next;
8147 struct GUID guid = GUID_zero();
8148 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
8150 struct dsdb_dn *old_dsdb_dn = NULL;
8151 struct ldb_val *val_to_update = NULL;
8152 bool add_as_inactive = false;
8155 *change = LINK_CHANGE_NONE;
8157 /* the value blob for the attribute holds the target object DN */
8158 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
8159 la->value.blob, &dsdb_dn);
8160 if (!W_ERROR_IS_OK(status)) {
8161 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8162 attr->lDAPDisplayName,
8163 ldb_dn_get_linearized(src_dn),
8164 win_errstr(status));
8165 return LDB_ERR_OPERATIONS_ERROR;
8168 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
8169 true, &guid, &ignore_link);
8171 if (ret != LDB_SUCCESS) {
8176 * there are some cases where the target object doesn't exist, but it's
8177 * OK to ignore the linked attribute
8183 /* see if this link already exists */
8184 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8187 dsdb_dn->extra_part, 0,
8189 attr->syntax->ldap_oid,
8191 if (ret != LDB_SUCCESS) {
8195 if (!replmd_link_update_is_newer(pdn, la)) {
8196 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8197 old_el->name, ldb_dn_get_linearized(src_dn),
8198 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8202 /* get a seq_num for this change */
8203 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8204 if (ret != LDB_SUCCESS) {
8209 * check for single-valued link conflicts, i.e. an active linked
8210 * attribute already exists, but it has a different target value
8213 ret = replmd_check_singleval_la_conflict(module, replmd_private,
8214 mem_ctx, src_dn, la,
8215 dsdb_dn, pdn, pdn_list,
8216 old_el, schema, attr,
8219 if (ret != LDB_SUCCESS) {
8225 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8227 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8228 /* remove the existing backlink */
8229 ret = replmd_add_backlink(module, replmd_private,
8232 &pdn->guid, false, attr,
8234 if (ret != LDB_SUCCESS) {
8239 val_to_update = pdn->v;
8240 old_dsdb_dn = pdn->dsdb_dn;
8241 *change = LINK_CHANGE_MODIFIED;
8247 * We know where the new one needs to be, from the *next
8248 * pointer into pdn_list.
8251 offset = old_el->num_values;
8253 if (next->dsdb_dn == NULL) {
8254 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8255 attr->syntax->ldap_oid);
8256 if (ret != LDB_SUCCESS) {
8260 offset = next - pdn_list;
8261 if (offset > old_el->num_values) {
8262 return LDB_ERR_OPERATIONS_ERROR;
8266 old_el->values = talloc_realloc(element_ctx, old_el->values,
8267 struct ldb_val, old_el->num_values+1);
8268 if (!old_el->values) {
8269 ldb_module_oom(module);
8270 return LDB_ERR_OPERATIONS_ERROR;
8273 if (offset != old_el->num_values) {
8274 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8275 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8278 old_el->num_values++;
8280 val_to_update = &old_el->values[offset];
8282 *change = LINK_CHANGE_ADDED;
8285 /* set the link attribute's value to the info that was received */
8286 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8287 &la->meta_data.originating_invocation_id,
8288 la->meta_data.originating_usn, seq_num,
8289 la->meta_data.originating_change_time,
8290 la->meta_data.version,
8292 if (ret != LDB_SUCCESS) {
8296 if (add_as_inactive) {
8298 /* Set the new link as inactive/deleted to avoid conflicts */
8299 ret = replmd_delete_link_value(module, replmd_private, old_el,
8300 src_dn, schema, attr, seq_num,
8301 false, &guid, dsdb_dn,
8304 if (ret != LDB_SUCCESS) {
8308 } else if (active) {
8310 /* if the new link is active, then add the new backlink */
8311 ret = replmd_add_backlink(module, replmd_private,
8316 if (ret != LDB_SUCCESS) {
8321 ret = dsdb_check_single_valued_link(attr, old_el);
8322 if (ret != LDB_SUCCESS) {
8326 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8331 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8333 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8334 return replmd_extended_replicated_objects(module, req);
8337 return ldb_next_request(module, req);
8342 we hook into the transaction operations to allow us to
8343 perform the linked attribute updates at the end of the whole
8344 transaction. This allows a forward linked attribute to be created
8345 before the object is created. During a vampire, w2k8 sends us linked
8346 attributes before the objects they are part of.
8348 static int replmd_start_transaction(struct ldb_module *module)
8350 /* create our private structure for this transaction */
8351 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8352 struct replmd_private);
8353 replmd_txn_cleanup(replmd_private);
8355 /* free any leftover mod_usn records from cancelled
8357 while (replmd_private->ncs) {
8358 struct nc_entry *e = replmd_private->ncs;
8359 DLIST_REMOVE(replmd_private->ncs, e);
8363 replmd_private->originating_updates = false;
8365 return ldb_next_start_trans(module);
8369 * Processes a group of linked attributes that apply to the same source-object
8370 * and attribute-ID (and were received in the same replication chunk).
8372 static int replmd_process_la_group(struct ldb_module *module,
8373 struct replmd_private *replmd_private,
8374 struct la_group *la_group)
8376 struct la_entry *la = NULL;
8377 struct la_entry *prev = NULL;
8379 TALLOC_CTX *tmp_ctx = NULL;
8380 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8381 struct ldb_message *msg = NULL;
8382 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8383 struct ldb_context *ldb = ldb_module_get_ctx(module);
8384 const struct dsdb_attribute *attr = NULL;
8385 struct ldb_message_element *old_el = NULL;
8386 struct parsed_dn *pdn_list = NULL;
8387 replmd_link_changed change_type;
8388 uint32_t num_changes = 0;
8390 uint64_t seq_num = 0;
8392 tmp_ctx = talloc_new(la_group);
8393 if (tmp_ctx == NULL) {
8394 return ldb_oom(ldb);
8398 * get the attribute being modified and the search result for the
8401 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8404 if (ret != LDB_SUCCESS) {
8409 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8410 * ProcessLinkValue, because link updates are not applied to
8411 * recycled and tombstone objects. We don't have to delete
8412 * any existing link, that should have happened when the
8413 * object deletion was replicated or initiated.
8415 * This needs isDeleted and isRecycled to be included as
8416 * attributes in the search and so in msg if set.
8418 replmd_deletion_state(module, msg, &deletion_state, NULL);
8420 if (deletion_state >= OBJECT_RECYCLED) {
8421 TALLOC_FREE(tmp_ctx);
8426 * Now that we know the deletion_state, remove the extra
8427 * attributes added for that purpose. We need to do this
8428 * otherwise in the case of isDeleted: FALSE the modify will
8431 * Failed to apply linked attribute change 'attribute 'isDeleted':
8432 * invalid modify flags on
8433 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8436 * This is becaue isDeleted is a Boolean, so FALSE is a
8437 * legitimate value (set by Samba's deletetest.py)
8439 ldb_msg_remove_attr(msg, "isDeleted");
8440 ldb_msg_remove_attr(msg, "isRecycled");
8442 /* get the msg->element[] for the link attribute being processed */
8443 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8444 if (old_el == NULL) {
8445 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
8446 LDB_FLAG_MOD_REPLACE, &old_el);
8447 if (ret != LDB_SUCCESS) {
8448 ldb_module_oom(module);
8449 return LDB_ERR_OPERATIONS_ERROR;
8452 old_el->flags = LDB_FLAG_MOD_REPLACE;
8456 * go through and process the link target value(s) for this particular
8457 * source object and attribute. For optimization, the same msg is used
8458 * across multiple calls to replmd_process_linked_attribute().
8459 * Note that we should not add or remove any msg attributes inside the
8460 * loop (we should only add/modify *values* for the attribute being
8461 * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8462 * pointers will be invalidated
8464 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8465 prev = DLIST_PREV(la);
8466 DLIST_REMOVE(la_group->la_entries, la);
8469 * parse the existing links (this can be costly for a large
8470 * group, so we try to minimize the times we do it)
8472 if (pdn_list == NULL) {
8473 ret = get_parsed_dns_trusted_fallback(module,
8477 attr->syntax->ldap_oid,
8480 if (ret != LDB_SUCCESS) {
8484 ret = replmd_process_linked_attribute(module, tmp_ctx,
8486 msg->dn, attr, la, NULL,
8487 msg->elements, old_el,
8488 pdn_list, &change_type);
8489 if (ret != LDB_SUCCESS) {
8490 replmd_txn_cleanup(replmd_private);
8495 * Adding a link reallocs memory, and so invalidates all the
8496 * pointers in pdn_list. Reparse the PDNs on the next loop
8498 if (change_type == LINK_CHANGE_ADDED) {
8499 TALLOC_FREE(pdn_list);
8502 if (change_type != LINK_CHANGE_NONE) {
8506 if ((++replmd_private->num_processed % 8192) == 0) {
8507 DBG_NOTICE("Processed %u/%u linked attributes\n",
8508 replmd_private->num_processed,
8509 replmd_private->total_links);
8514 * it's possible we're already up-to-date and so don't need to modify
8515 * the object at all (e.g. doing a 'drs replicate --full-sync')
8517 if (num_changes == 0) {
8518 TALLOC_FREE(tmp_ctx);
8523 * Note that adding the whenChanged/etc attributes below will realloc
8524 * msg->elements, invalidating the existing element/parsed-DN pointers
8527 TALLOC_FREE(pdn_list);
8529 /* update whenChanged/uSNChanged as the object has changed */
8531 ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
8533 if (ret != LDB_SUCCESS) {
8537 ret = add_time_element(msg, "whenChanged", t);
8538 if (ret != LDB_SUCCESS) {
8543 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8544 if (ret != LDB_SUCCESS) {
8549 /* apply the link changes to the source object */
8550 ret = linked_attr_modify(module, msg, NULL);
8551 if (ret != LDB_SUCCESS) {
8552 ldb_debug(ldb, LDB_DEBUG_WARNING,
8553 "Failed to apply linked attribute change "
8554 "Error: '%s' DN: '%s' Attribute: '%s'\n",
8556 ldb_dn_get_linearized(msg->dn),
8557 attr->lDAPDisplayName);
8558 TALLOC_FREE(tmp_ctx);
8562 TALLOC_FREE(tmp_ctx);
8567 on prepare commit we loop over our queued la_context structures and
8570 static int replmd_prepare_commit(struct ldb_module *module)
8572 struct replmd_private *replmd_private =
8573 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8574 struct la_group *la_group, *prev;
8577 if (replmd_private->la_list != NULL) {
8578 DBG_NOTICE("Processing linked attributes\n");
8582 * Walk the list of linked attributes from DRS replication.
8584 * We walk backwards, to do the first entry first, as we
8585 * added the entries with DLIST_ADD() which puts them at the
8588 * Links are grouped together so we process links for the same
8589 * source object in one go.
8591 for (la_group = DLIST_TAIL(replmd_private->la_list);
8595 prev = DLIST_PREV(la_group);
8596 DLIST_REMOVE(replmd_private->la_list, la_group);
8597 ret = replmd_process_la_group(module, replmd_private,
8599 if (ret != LDB_SUCCESS) {
8600 replmd_txn_cleanup(replmd_private);
8605 replmd_txn_cleanup(replmd_private);
8607 /* possibly change @REPLCHANGED */
8608 ret = replmd_notify_store(module, NULL);
8609 if (ret != LDB_SUCCESS) {
8613 return ldb_next_prepare_commit(module);
8616 static int replmd_del_transaction(struct ldb_module *module)
8618 struct replmd_private *replmd_private =
8619 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8620 replmd_txn_cleanup(replmd_private);
8622 return ldb_next_del_trans(module);
8626 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8627 .name = "repl_meta_data",
8628 .init_context = replmd_init,
8630 .modify = replmd_modify,
8631 .rename = replmd_rename,
8632 .del = replmd_delete,
8633 .extended = replmd_extended,
8634 .start_transaction = replmd_start_transaction,
8635 .prepare_commit = replmd_prepare_commit,
8636 .del_transaction = replmd_del_transaction,
8639 int ldb_repl_meta_data_module_init(const char *version)
8641 LDB_MODULE_CHECK_VERSION(version);
8642 return ldb_register_module(&ldb_repl_meta_data_module_ops);