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"
56 #define DBGC_CLASS DBGC_DRS_REPL
58 /* the RMD_VERSION for linked attributes starts from 1 */
59 #define RMD_VERSION_INITIAL 1
62 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
63 * Deleted Objects Container
65 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
67 struct replmd_private {
69 struct la_group *la_list;
71 struct nc_entry *prev, *next;
74 uint64_t mod_usn_urgent;
76 struct ldb_dn *schema_dn;
77 bool originating_updates;
80 uint32_t num_processed;
81 bool recyclebin_enabled;
82 bool recyclebin_state_known;
86 * groups link attributes together by source-object and attribute-ID,
87 * to improve processing efficiency (i.e. for 'member' attribute, which
88 * could have 100s or 1000s of links).
89 * Note this grouping is best effort - the same source object could still
90 * correspond to several la_groups (a lot depends on the order DRS sends
91 * the links in). The groups currently don't span replication chunks (which
92 * caps the size to ~1500 links by default).
95 struct la_group *next, *prev;
96 struct la_entry *la_entries;
100 struct la_entry *next, *prev;
101 struct drsuapi_DsReplicaLinkedAttribute *la;
102 uint32_t dsdb_repl_flags;
105 struct replmd_replicated_request {
106 struct ldb_module *module;
107 struct ldb_request *req;
109 const struct dsdb_schema *schema;
110 struct GUID our_invocation_id;
112 /* the controls we pass down */
113 struct ldb_control **controls;
116 * Backlinks for the replmd_add() case (we want to create
117 * backlinks after creating the user, but before the end of
120 struct la_backlink *la_backlinks;
122 /* details for the mode where we apply a bunch of inbound replication meessages */
124 uint32_t index_current;
125 struct dsdb_extended_replicated_objects *objs;
127 struct ldb_message *search_msg;
128 struct GUID local_parent_guid;
139 * the result of replmd_process_linked_attribute(): either there was no change
140 * (update was ignored), a new link was added (either inactive or active), or
141 * an existing link was modified (active/inactive status may have changed).
146 LINK_CHANGE_MODIFIED,
147 } replmd_link_changed;
149 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
150 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
151 static int replmd_check_upgrade_links(struct ldb_context *ldb,
152 struct parsed_dn *dns, uint32_t count,
153 struct ldb_message_element *el,
154 const char *ldap_oid);
155 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
157 struct la_entry *la_entry,
158 struct ldb_dn *src_dn,
159 const struct dsdb_attribute *attr);
160 static int replmd_get_la_entry_source(struct ldb_module *module,
161 struct la_entry *la_entry,
163 const struct dsdb_attribute **ret_attr,
164 struct ldb_message **source_msg);
165 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
166 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
167 uint64_t usn, uint64_t local_usn, NTTIME nttime,
168 uint32_t version, bool deleted);
170 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
171 struct ldb_context *ldb,
173 const char *rdn_name,
174 const struct ldb_val *rdn_value,
177 enum urgent_situation {
178 REPL_URGENT_ON_CREATE = 1,
179 REPL_URGENT_ON_UPDATE = 2,
180 REPL_URGENT_ON_DELETE = 4
183 enum deletion_state {
184 OBJECT_NOT_DELETED=1,
191 static bool replmd_recyclebin_enabled(struct ldb_module *module)
193 bool enabled = false;
194 struct replmd_private *replmd_private =
195 talloc_get_type_abort(ldb_module_get_private(module),
196 struct replmd_private);
199 * only lookup the recycle-bin state once per replication, then cache
200 * the result. This can save us 1000s of DB searches
202 if (!replmd_private->recyclebin_state_known) {
203 int ret = dsdb_recyclebin_enabled(module, &enabled);
204 if (ret != LDB_SUCCESS) {
208 replmd_private->recyclebin_enabled = enabled;
209 replmd_private->recyclebin_state_known = true;
212 return replmd_private->recyclebin_enabled;
215 static void replmd_deletion_state(struct ldb_module *module,
216 const struct ldb_message *msg,
217 enum deletion_state *current_state,
218 enum deletion_state *next_state)
220 bool enabled = false;
223 *current_state = OBJECT_REMOVED;
224 if (next_state != NULL) {
225 *next_state = OBJECT_REMOVED;
230 enabled = replmd_recyclebin_enabled(module);
232 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
234 *current_state = OBJECT_TOMBSTONE;
235 if (next_state != NULL) {
236 *next_state = OBJECT_REMOVED;
241 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
242 *current_state = OBJECT_RECYCLED;
243 if (next_state != NULL) {
244 *next_state = OBJECT_REMOVED;
249 *current_state = OBJECT_DELETED;
250 if (next_state != NULL) {
251 *next_state = OBJECT_RECYCLED;
256 *current_state = OBJECT_NOT_DELETED;
257 if (next_state == NULL) {
262 *next_state = OBJECT_DELETED;
264 *next_state = OBJECT_TOMBSTONE;
268 static const struct {
269 const char *update_name;
270 enum urgent_situation repl_situation;
271 } urgent_objects[] = {
272 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
273 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
274 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
275 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
276 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
277 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
281 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
282 static const char *urgent_attrs[] = {
285 "userAccountControl",
290 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
291 enum urgent_situation situation)
294 for (i=0; urgent_objects[i].update_name; i++) {
296 if ((situation & urgent_objects[i].repl_situation) == 0) {
300 for (j=0; j<objectclass_el->num_values; j++) {
301 const struct ldb_val *v = &objectclass_el->values[j];
302 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
310 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
312 if (ldb_attr_in_list(urgent_attrs, el->name)) {
318 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
321 initialise the module
322 allocate the private structure and build the list
323 of partition DNs for use by replmd_notify()
325 static int replmd_init(struct ldb_module *module)
327 struct replmd_private *replmd_private;
328 struct ldb_context *ldb = ldb_module_get_ctx(module);
331 replmd_private = talloc_zero(module, struct replmd_private);
332 if (replmd_private == NULL) {
334 return LDB_ERR_OPERATIONS_ERROR;
337 ret = dsdb_check_samba_compatible_feature(module,
338 SAMBA_SORTED_LINKS_FEATURE,
339 &replmd_private->sorted_links);
340 if (ret != LDB_SUCCESS) {
341 talloc_free(replmd_private);
345 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
346 ldb_module_set_private(module, replmd_private);
347 return ldb_next_init(module);
351 cleanup our per-transaction contexts
353 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
355 talloc_free(replmd_private->la_ctx);
356 replmd_private->la_list = NULL;
357 replmd_private->la_ctx = NULL;
358 replmd_private->recyclebin_state_known = false;
363 struct la_backlink *next, *prev;
364 const char *attr_name;
365 struct ldb_dn *forward_dn;
366 struct GUID target_guid;
371 a ldb_modify request operating on modules below the
374 static int linked_attr_modify(struct ldb_module *module,
375 const struct ldb_message *message,
376 struct ldb_request *parent)
378 struct ldb_request *mod_req;
380 struct ldb_context *ldb = ldb_module_get_ctx(module);
381 TALLOC_CTX *tmp_ctx = talloc_new(module);
382 struct ldb_result *res;
384 res = talloc_zero(tmp_ctx, struct ldb_result);
386 talloc_free(tmp_ctx);
387 return ldb_oom(ldb_module_get_ctx(module));
390 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
394 ldb_modify_default_callback,
396 LDB_REQ_SET_LOCATION(mod_req);
397 if (ret != LDB_SUCCESS) {
398 talloc_free(tmp_ctx);
402 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
404 if (ret != LDB_SUCCESS) {
408 /* Run the new request */
409 ret = ldb_next_request(module, mod_req);
411 if (ret == LDB_SUCCESS) {
412 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
415 talloc_free(tmp_ctx);
420 process a backlinks we accumulated during a transaction, adding and
421 deleting the backlinks from the target objects
423 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
425 struct ldb_dn *target_dn, *source_dn;
427 struct ldb_context *ldb = ldb_module_get_ctx(module);
428 struct ldb_message *msg;
429 TALLOC_CTX *frame = talloc_stackframe();
435 - construct ldb_message
436 - either an add or a delete
438 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
439 if (ret != LDB_SUCCESS) {
440 struct GUID_txt_buf guid_str;
441 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
442 GUID_buf_string(&bl->target_guid, &guid_str));
443 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
448 msg = ldb_msg_new(frame);
450 ldb_module_oom(module);
452 return LDB_ERR_OPERATIONS_ERROR;
455 source_dn = ldb_dn_copy(frame, bl->forward_dn);
457 ldb_module_oom(module);
459 return LDB_ERR_OPERATIONS_ERROR;
461 /* Filter down to the attributes we want in the backlink */
462 const char *accept[] = { "GUID", "SID", NULL };
463 ldb_dn_extended_filter(source_dn, accept);
466 /* construct a ldb_message for adding/deleting the backlink */
468 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
470 ldb_module_oom(module);
472 return LDB_ERR_OPERATIONS_ERROR;
474 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
475 if (ret != LDB_SUCCESS) {
479 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
481 /* a backlink should never be single valued. Unfortunately the
482 exchange schema has a attribute
483 msExchBridgeheadedLocalConnectorsDNBL which is single
484 valued and a backlink. We need to cope with that by
485 ignoring the single value flag */
486 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
488 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
489 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
490 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
491 cope with possible corruption where the backlink has
492 already been removed */
493 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
494 ldb_dn_get_linearized(target_dn),
495 ldb_dn_get_linearized(source_dn),
496 ldb_errstring(ldb)));
498 } else if (ret != LDB_SUCCESS) {
499 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
500 bl->active?"add":"remove",
501 ldb_dn_get_linearized(source_dn),
502 ldb_dn_get_linearized(target_dn),
512 add a backlink to the list of backlinks to add/delete in the prepare
515 forward_dn is stolen onto the defereed context
517 static int replmd_defer_add_backlink(struct ldb_module *module,
518 struct replmd_private *replmd_private,
519 const struct dsdb_schema *schema,
520 struct replmd_replicated_request *ac,
521 struct ldb_dn *forward_dn,
522 struct GUID *target_guid, bool active,
523 const struct dsdb_attribute *schema_attr,
524 struct ldb_request *parent)
526 const struct dsdb_attribute *target_attr;
527 struct la_backlink *bl;
529 bl = talloc(ac, struct la_backlink);
531 ldb_module_oom(module);
532 return LDB_ERR_OPERATIONS_ERROR;
535 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
538 * windows 2003 has a broken schema where the
539 * definition of msDS-IsDomainFor is missing (which is
540 * supposed to be the backlink of the
541 * msDS-HasDomainNCs attribute
546 bl->attr_name = target_attr->lDAPDisplayName;
547 bl->forward_dn = talloc_steal(bl, forward_dn);
548 bl->target_guid = *target_guid;
551 DLIST_ADD(ac->la_backlinks, bl);
557 add a backlink to the list of backlinks to add/delete in the prepare
560 static int replmd_add_backlink(struct ldb_module *module,
561 struct replmd_private *replmd_private,
562 const struct dsdb_schema *schema,
563 struct ldb_dn *forward_dn,
564 struct GUID *target_guid, bool active,
565 const struct dsdb_attribute *schema_attr,
566 struct ldb_request *parent)
568 const struct dsdb_attribute *target_attr;
569 struct la_backlink bl;
572 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
575 * windows 2003 has a broken schema where the
576 * definition of msDS-IsDomainFor is missing (which is
577 * supposed to be the backlink of the
578 * msDS-HasDomainNCs attribute
583 bl.attr_name = target_attr->lDAPDisplayName;
584 bl.forward_dn = forward_dn;
585 bl.target_guid = *target_guid;
588 ret = replmd_process_backlink(module, &bl, parent);
594 * Callback for most write operations in this module:
596 * notify the repl task that a object has changed. The notifies are
597 * gathered up in the replmd_private structure then written to the
598 * @REPLCHANGED object in each partition during the prepare_commit
600 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
603 struct replmd_replicated_request *ac =
604 talloc_get_type_abort(req->context, struct replmd_replicated_request);
605 struct replmd_private *replmd_private =
606 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
607 struct nc_entry *modified_partition;
608 struct ldb_control *partition_ctrl;
609 const struct dsdb_control_current_partition *partition;
611 struct ldb_control **controls;
613 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
615 controls = ares->controls;
616 if (ldb_request_get_control(ac->req,
617 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
619 * Remove the current partition control from what we pass up
620 * the chain if it hasn't been requested manually.
622 controls = ldb_controls_except_specified(ares->controls, ares,
626 if (ares->error != LDB_SUCCESS) {
627 struct GUID_txt_buf guid_txt;
628 struct ldb_message *msg = NULL;
631 if (ac->apply_mode == false) {
632 DBG_NOTICE("Originating update failure. Error is: %s\n",
633 ldb_strerror(ares->error));
634 return ldb_module_done(ac->req, controls,
635 ares->response, ares->error);
638 msg = ac->objs->objects[ac->index_current].msg;
640 * Set at DBG_NOTICE as once these start to happe, they
641 * will happen a lot until resolved, due to repeated
642 * replication. The caller will probably print the
643 * ldb error string anyway.
645 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
646 ldb_dn_get_linearized(msg->dn),
647 ldb_strerror(ares->error));
649 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
654 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
655 ac->search_msg == NULL ? "ADD" : "MODIFY",
656 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
660 return ldb_module_done(ac->req, controls,
661 ares->response, ares->error);
664 if (ares->type != LDB_REPLY_DONE) {
665 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
666 return ldb_module_done(ac->req, NULL,
667 NULL, LDB_ERR_OPERATIONS_ERROR);
670 if (ac->apply_mode == false) {
671 struct la_backlink *bl;
673 * process our backlink list after an replmd_add(),
674 * creating and deleting backlinks as necessary (this
675 * code is sync). The other cases are handled inline
678 for (bl=ac->la_backlinks; bl; bl=bl->next) {
679 ret = replmd_process_backlink(ac->module, bl, ac->req);
680 if (ret != LDB_SUCCESS) {
681 return ldb_module_done(ac->req, NULL,
687 if (!partition_ctrl) {
688 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
689 return ldb_module_done(ac->req, NULL,
690 NULL, LDB_ERR_OPERATIONS_ERROR);
693 partition = talloc_get_type_abort(partition_ctrl->data,
694 struct dsdb_control_current_partition);
696 if (ac->seq_num > 0) {
697 for (modified_partition = replmd_private->ncs; modified_partition;
698 modified_partition = modified_partition->next) {
699 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
704 if (modified_partition == NULL) {
705 modified_partition = talloc_zero(replmd_private, struct nc_entry);
706 if (!modified_partition) {
707 ldb_oom(ldb_module_get_ctx(ac->module));
708 return ldb_module_done(ac->req, NULL,
709 NULL, LDB_ERR_OPERATIONS_ERROR);
711 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
712 if (!modified_partition->dn) {
713 ldb_oom(ldb_module_get_ctx(ac->module));
714 return ldb_module_done(ac->req, NULL,
715 NULL, LDB_ERR_OPERATIONS_ERROR);
717 DLIST_ADD(replmd_private->ncs, modified_partition);
720 if (ac->seq_num > modified_partition->mod_usn) {
721 modified_partition->mod_usn = ac->seq_num;
723 modified_partition->mod_usn_urgent = ac->seq_num;
726 if (!ac->apply_mode) {
727 replmd_private->originating_updates = true;
731 if (ac->apply_mode) {
732 ret = replmd_replicated_apply_isDeleted(ac);
733 if (ret != LDB_SUCCESS) {
734 return ldb_module_done(ac->req, NULL, NULL, ret);
738 /* free the partition control container here, for the
739 * common path. Other cases will have it cleaned up
740 * eventually with the ares */
741 talloc_free(partition_ctrl);
742 return ldb_module_done(ac->req, controls,
743 ares->response, LDB_SUCCESS);
749 * update a @REPLCHANGED record in each partition if there have been
750 * any writes of replicated data in the partition
752 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
754 struct replmd_private *replmd_private =
755 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
757 while (replmd_private->ncs) {
759 struct nc_entry *modified_partition = replmd_private->ncs;
761 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
762 modified_partition->mod_usn,
763 modified_partition->mod_usn_urgent, parent);
764 if (ret != LDB_SUCCESS) {
765 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
766 ldb_dn_get_linearized(modified_partition->dn)));
770 if (ldb_dn_compare(modified_partition->dn,
771 replmd_private->schema_dn) == 0) {
772 struct ldb_result *ext_res;
773 ret = dsdb_module_extended(module,
774 replmd_private->schema_dn,
776 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
778 DSDB_FLAG_NEXT_MODULE,
780 if (ret != LDB_SUCCESS) {
783 talloc_free(ext_res);
786 DLIST_REMOVE(replmd_private->ncs, modified_partition);
787 talloc_free(modified_partition);
795 created a replmd_replicated_request context
797 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
798 struct ldb_request *req)
800 struct ldb_context *ldb;
801 struct replmd_replicated_request *ac;
802 const struct GUID *our_invocation_id;
804 ldb = ldb_module_get_ctx(module);
806 ac = talloc_zero(req, struct replmd_replicated_request);
815 ac->schema = dsdb_get_schema(ldb, ac);
817 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
818 "replmd_modify: no dsdb_schema loaded");
819 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
824 /* get our invocationId */
825 our_invocation_id = samdb_ntds_invocation_id(ldb);
826 if (!our_invocation_id) {
827 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
828 "replmd_add: unable to find invocationId\n");
832 ac->our_invocation_id = *our_invocation_id;
838 add a time element to a record
840 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
842 struct ldb_message_element *el;
846 if (ldb_msg_find_element(msg, attr) != NULL) {
850 s = ldb_timestring(msg, t);
852 return LDB_ERR_OPERATIONS_ERROR;
855 ret = ldb_msg_add_string(msg, attr, s);
856 if (ret != LDB_SUCCESS) {
860 el = ldb_msg_find_element(msg, attr);
861 /* always set as replace. This works because on add ops, the flag
863 el->flags = LDB_FLAG_MOD_REPLACE;
869 add a uint64_t element to a record
871 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
872 const char *attr, uint64_t v)
874 struct ldb_message_element *el;
877 if (ldb_msg_find_element(msg, attr) != NULL) {
881 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
882 if (ret != LDB_SUCCESS) {
886 el = ldb_msg_find_element(msg, attr);
887 /* always set as replace. This works because on add ops, the flag
889 el->flags = LDB_FLAG_MOD_REPLACE;
894 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
895 const struct replPropertyMetaData1 *m2,
896 const uint32_t *rdn_attid)
899 * This assignment seems inoccous, but it is critical for the
900 * system, as we need to do the comparisons as a unsigned
901 * quantity, not signed (enums are signed integers)
903 uint32_t attid_1 = m1->attid;
904 uint32_t attid_2 = m2->attid;
906 if (attid_1 == attid_2) {
911 * See above regarding this being an unsigned comparison.
912 * Otherwise when the high bit is set on non-standard
913 * attributes, they would end up first, before objectClass
916 return attid_1 > attid_2 ? 1 : -1;
919 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
920 struct replPropertyMetaDataCtr1 *ctr1,
923 if (ctr1->count == 0) {
924 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
925 "No elements found in replPropertyMetaData for %s!\n",
926 ldb_dn_get_linearized(dn));
927 return LDB_ERR_CONSTRAINT_VIOLATION;
930 /* the objectClass attribute is value 0x00000000, so must be first */
931 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
932 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
933 "No objectClass found in replPropertyMetaData for %s!\n",
934 ldb_dn_get_linearized(dn));
935 return LDB_ERR_OBJECT_CLASS_VIOLATION;
941 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
942 struct replPropertyMetaDataCtr1 *ctr1,
945 /* Note this is O(n^2) for the almost-sorted case, which this is */
946 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
947 replmd_replPropertyMetaData1_attid_sort);
948 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
951 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
952 const struct ldb_message_element *e2,
953 const struct dsdb_schema *schema)
955 const struct dsdb_attribute *a1;
956 const struct dsdb_attribute *a2;
959 * TODO: make this faster by caching the dsdb_attribute pointer
960 * on the ldb_messag_element
963 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
964 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
967 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
971 return strcasecmp(e1->name, e2->name);
973 if (a1->attributeID_id == a2->attributeID_id) {
976 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
979 static void replmd_ldb_message_sort(struct ldb_message *msg,
980 const struct dsdb_schema *schema)
982 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
985 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
986 const struct GUID *invocation_id,
987 uint64_t local_usn, NTTIME nttime);
989 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
991 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
992 struct ldb_message_element *el, struct parsed_dn **pdn,
993 const char *ldap_oid, struct ldb_request *parent);
995 static int check_parsed_dn_duplicates(struct ldb_module *module,
996 struct ldb_message_element *el,
997 struct parsed_dn *pdn);
1000 fix up linked attributes in replmd_add.
1001 This involves setting up the right meta-data in extended DN
1002 components, and creating backlinks to the object
1004 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1005 struct replmd_private *replmd_private,
1006 struct ldb_message_element *el,
1007 struct replmd_replicated_request *ac,
1009 struct ldb_dn *forward_dn,
1010 const struct dsdb_attribute *sa,
1011 struct ldb_request *parent)
1014 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1015 struct ldb_context *ldb = ldb_module_get_ctx(module);
1016 struct parsed_dn *pdn;
1017 /* We will take a reference to the schema in replmd_add_backlink */
1018 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
1019 struct ldb_val *new_values = NULL;
1022 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1023 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1025 ldb_asprintf_errstring(ldb,
1026 "Attribute %s is single valued but "
1027 "more than one value has been supplied",
1029 talloc_free(tmp_ctx);
1030 return LDB_ERR_CONSTRAINT_VIOLATION;
1033 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1034 sa->syntax->ldap_oid, parent);
1035 if (ret != LDB_SUCCESS) {
1036 talloc_free(tmp_ctx);
1040 ret = check_parsed_dn_duplicates(module, el, pdn);
1041 if (ret != LDB_SUCCESS) {
1042 talloc_free(tmp_ctx);
1046 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1047 if (new_values == NULL) {
1048 ldb_module_oom(module);
1049 talloc_free(tmp_ctx);
1050 return LDB_ERR_OPERATIONS_ERROR;
1053 for (i = 0; i < el->num_values; i++) {
1054 struct parsed_dn *p = &pdn[i];
1055 ret = replmd_build_la_val(new_values, p->v, p->dsdb_dn,
1056 &ac->our_invocation_id,
1058 if (ret != LDB_SUCCESS) {
1059 talloc_free(tmp_ctx);
1063 ret = replmd_defer_add_backlink(module, replmd_private,
1065 forward_dn, &p->guid, true, sa,
1067 if (ret != LDB_SUCCESS) {
1068 talloc_free(tmp_ctx);
1072 new_values[i] = *p->v;
1074 el->values = talloc_steal(mem_ctx, new_values);
1076 talloc_free(tmp_ctx);
1080 static int replmd_add_make_extended_dn(struct ldb_request *req,
1081 const DATA_BLOB *guid_blob,
1082 struct ldb_dn **_extended_dn)
1085 const DATA_BLOB *sid_blob;
1086 /* Calculate an extended DN for any linked attributes */
1087 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1089 return LDB_ERR_OPERATIONS_ERROR;
1091 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1092 if (ret != LDB_SUCCESS) {
1096 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1097 if (sid_blob != NULL) {
1098 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1099 if (ret != LDB_SUCCESS) {
1103 *_extended_dn = extended_dn;
1108 intercept add requests
1110 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1112 struct ldb_context *ldb;
1113 struct ldb_control *control;
1114 struct replmd_replicated_request *ac;
1115 enum ndr_err_code ndr_err;
1116 struct ldb_request *down_req;
1117 struct ldb_message *msg;
1118 const DATA_BLOB *guid_blob;
1119 DATA_BLOB guid_blob_stack;
1121 uint8_t guid_data[16];
1122 struct replPropertyMetaDataBlob nmd;
1123 struct ldb_val nmd_value;
1124 struct ldb_dn *extended_dn = NULL;
1127 * The use of a time_t here seems odd, but as the NTTIME
1128 * elements are actually declared as NTTIME_1sec in the IDL,
1129 * getting a higher resolution timestamp is not required.
1131 time_t t = time(NULL);
1136 unsigned int functional_level;
1138 bool allow_add_guid = false;
1139 bool remove_current_guid = false;
1140 bool is_urgent = false;
1141 bool is_schema_nc = false;
1142 struct ldb_message_element *objectclass_el;
1143 struct replmd_private *replmd_private =
1144 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1146 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1147 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1149 allow_add_guid = true;
1152 /* do not manipulate our control entries */
1153 if (ldb_dn_is_special(req->op.add.message->dn)) {
1154 return ldb_next_request(module, req);
1157 ldb = ldb_module_get_ctx(module);
1159 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1161 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1162 if (guid_blob != NULL) {
1163 if (!allow_add_guid) {
1164 ldb_set_errstring(ldb,
1165 "replmd_add: it's not allowed to add an object with objectGUID!");
1166 return LDB_ERR_UNWILLING_TO_PERFORM;
1168 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 ldb_set_errstring(ldb,
1171 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1172 return LDB_ERR_UNWILLING_TO_PERFORM;
1174 /* we remove this attribute as it can be a string and
1175 * will not be treated correctly and then we will re-add
1176 * it later on in the good format */
1177 remove_current_guid = true;
1181 guid = GUID_random();
1183 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1185 /* This can't fail */
1186 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1187 (ndr_push_flags_fn_t)ndr_push_GUID);
1188 guid_blob = &guid_blob_stack;
1191 ac = replmd_ctx_init(module, req);
1193 return ldb_module_oom(module);
1196 functional_level = dsdb_functional_level(ldb);
1198 /* Get a sequence number from the backend */
1199 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1200 if (ret != LDB_SUCCESS) {
1205 /* we have to copy the message as the caller might have it as a const */
1206 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1210 return LDB_ERR_OPERATIONS_ERROR;
1213 /* generated times */
1214 unix_to_nt_time(&now, t);
1215 time_str = ldb_timestring(msg, t);
1219 return LDB_ERR_OPERATIONS_ERROR;
1221 if (remove_current_guid) {
1222 ldb_msg_remove_attr(msg,"objectGUID");
1226 * remove autogenerated attributes
1228 ldb_msg_remove_attr(msg, "whenCreated");
1229 ldb_msg_remove_attr(msg, "whenChanged");
1230 ldb_msg_remove_attr(msg, "uSNCreated");
1231 ldb_msg_remove_attr(msg, "uSNChanged");
1232 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1235 * readd replicated attributes
1237 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1238 if (ret != LDB_SUCCESS) {
1244 /* build the replication meta_data */
1247 nmd.ctr.ctr1.count = msg->num_elements;
1248 nmd.ctr.ctr1.array = talloc_array(msg,
1249 struct replPropertyMetaData1,
1250 nmd.ctr.ctr1.count);
1251 if (!nmd.ctr.ctr1.array) {
1254 return LDB_ERR_OPERATIONS_ERROR;
1257 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1259 for (i=0; i < msg->num_elements;) {
1260 struct ldb_message_element *e = &msg->elements[i];
1261 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1262 const struct dsdb_attribute *sa;
1264 if (e->name[0] == '@') {
1269 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1271 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1272 "replmd_add: attribute '%s' not defined in schema\n",
1275 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1278 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1279 /* if the attribute is not replicated (0x00000001)
1280 * or constructed (0x00000004) it has no metadata
1286 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1287 if (extended_dn == NULL) {
1288 ret = replmd_add_make_extended_dn(req,
1291 if (ret != LDB_SUCCESS) {
1298 * Prepare the context for the backlinks and
1299 * create metadata for the forward links. The
1300 * backlinks are created in
1301 * replmd_op_callback() after the successful
1302 * ADD of the object.
1304 ret = replmd_add_fix_la(module, msg->elements,
1309 if (ret != LDB_SUCCESS) {
1313 /* linked attributes are not stored in
1314 replPropertyMetaData in FL above w2k */
1319 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1321 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1322 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1325 if (rdn_val == NULL) {
1328 return LDB_ERR_OPERATIONS_ERROR;
1331 rdn = (const char*)rdn_val->data;
1332 if (strcmp(rdn, "Deleted Objects") == 0) {
1334 * Set the originating_change_time to 29/12/9999 at 23:59:59
1335 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1337 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1339 m->originating_change_time = now;
1342 m->originating_change_time = now;
1344 m->originating_invocation_id = ac->our_invocation_id;
1345 m->originating_usn = ac->seq_num;
1346 m->local_usn = ac->seq_num;
1349 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1354 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1356 if (e->num_values != 0) {
1361 ldb_msg_remove_element(msg, e);
1364 /* fix meta data count */
1365 nmd.ctr.ctr1.count = ni;
1368 * sort meta data array
1370 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1371 if (ret != LDB_SUCCESS) {
1372 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1377 /* generated NDR encoded values */
1378 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1380 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1381 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1384 return LDB_ERR_OPERATIONS_ERROR;
1388 * add the autogenerated values
1390 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1391 if (ret != LDB_SUCCESS) {
1396 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1397 if (ret != LDB_SUCCESS) {
1402 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1403 if (ret != LDB_SUCCESS) {
1408 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1409 if (ret != LDB_SUCCESS) {
1414 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1415 if (ret != LDB_SUCCESS) {
1422 * sort the attributes by attid before storing the object
1424 replmd_ldb_message_sort(msg, ac->schema);
1427 * Assert that we do have an objectClass
1429 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1430 if (objectclass_el == NULL) {
1431 ldb_asprintf_errstring(ldb, __location__
1432 ": objectClass missing on %s\n",
1433 ldb_dn_get_linearized(msg->dn));
1435 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1437 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1438 REPL_URGENT_ON_CREATE);
1440 ac->is_urgent = is_urgent;
1441 ret = ldb_build_add_req(&down_req, ldb, ac,
1444 ac, replmd_op_callback,
1447 LDB_REQ_SET_LOCATION(down_req);
1448 if (ret != LDB_SUCCESS) {
1453 /* current partition control is needed by "replmd_op_callback" */
1454 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1455 ret = ldb_request_add_control(down_req,
1456 DSDB_CONTROL_CURRENT_PARTITION_OID,
1458 if (ret != LDB_SUCCESS) {
1464 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1465 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1466 if (ret != LDB_SUCCESS) {
1472 /* mark the control done */
1474 control->critical = 0;
1476 /* go on with the call chain */
1477 return ldb_next_request(module, down_req);
1482 * update the replPropertyMetaData for one element
1484 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1485 struct ldb_message *msg,
1486 struct ldb_message_element *el,
1487 struct ldb_message_element *old_el,
1488 struct replPropertyMetaDataBlob *omd,
1489 const struct dsdb_schema *schema,
1491 const struct GUID *our_invocation_id,
1494 bool is_forced_rodc,
1495 struct ldb_request *req)
1498 const struct dsdb_attribute *a;
1499 struct replPropertyMetaData1 *md1;
1500 bool may_skip = false;
1503 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1505 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1506 /* allow this to make it possible for dbcheck
1507 to remove bad attributes */
1511 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1513 return LDB_ERR_OPERATIONS_ERROR;
1516 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1518 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1523 * if the attribute's value haven't changed, and this isn't
1524 * just a delete of everything then return LDB_SUCCESS Unless
1525 * we have the provision control or if the attribute is
1526 * interSiteTopologyGenerator as this page explain:
1527 * http://support.microsoft.com/kb/224815 this attribute is
1528 * periodicaly written by the DC responsible for the intersite
1529 * generation in a given site
1531 * Unchanged could be deleting or replacing an already-gone
1532 * thing with an unconstrained delete/empty replace or a
1533 * replace with the same value, but not an add with the same
1534 * value because that could be about adding a duplicate (which
1535 * is for someone else to error out on).
1537 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1538 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1541 } else if (old_el == NULL && el->num_values == 0) {
1542 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1544 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1547 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1548 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1550 * We intentionally skip the version bump when attempting to
1553 * The control is set by dbcheck and expunge-tombstones which
1554 * both attempt to be non-replicating. Otherwise, making an
1555 * alteration to the replication state would trigger a
1556 * broadcast of all expunged objects.
1561 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1563 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1567 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1568 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1570 * allow this to make it possible for dbcheck
1571 * to rebuild broken metadata
1577 for (i=0; i<omd->ctr.ctr1.count; i++) {
1579 * First check if we find it under the msDS-IntID,
1580 * then check if we find it under the OID and
1583 * This allows the administrator to simply re-write
1584 * the attributes and so restore replication, which is
1585 * likely what they will try to do.
1587 if (attid == omd->ctr.ctr1.array[i].attid) {
1591 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1596 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1597 /* linked attributes are not stored in
1598 replPropertyMetaData in FL above w2k, but we do
1599 raise the seqnum for the object */
1600 if (*seq_num == 0 &&
1601 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1602 return LDB_ERR_OPERATIONS_ERROR;
1607 if (i == omd->ctr.ctr1.count) {
1608 /* we need to add a new one */
1609 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1610 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1611 if (omd->ctr.ctr1.array == NULL) {
1613 return LDB_ERR_OPERATIONS_ERROR;
1615 omd->ctr.ctr1.count++;
1616 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1619 /* Get a new sequence number from the backend. We only do this
1620 * if we have a change that requires a new
1621 * replPropertyMetaData element
1623 if (*seq_num == 0) {
1624 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1625 if (ret != LDB_SUCCESS) {
1626 return LDB_ERR_OPERATIONS_ERROR;
1630 md1 = &omd->ctr.ctr1.array[i];
1634 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1635 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1638 if (rdn_val == NULL) {
1640 return LDB_ERR_OPERATIONS_ERROR;
1643 rdn = (const char*)rdn_val->data;
1644 if (strcmp(rdn, "Deleted Objects") == 0) {
1646 * Set the originating_change_time to 29/12/9999 at 23:59:59
1647 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1649 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1651 md1->originating_change_time = now;
1654 md1->originating_change_time = now;
1656 md1->originating_invocation_id = *our_invocation_id;
1657 md1->originating_usn = *seq_num;
1658 md1->local_usn = *seq_num;
1660 if (is_forced_rodc) {
1661 /* Force version to 0 to be overriden later via replication */
1669 * Bump the replPropertyMetaData version on an attribute, and if it
1670 * has changed (or forced by leaving rdn_old NULL), update the value
1673 * This is important, as calling a modify operation may not change the
1674 * version number if the values appear unchanged, but a rename between
1675 * parents bumps this value.
1678 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1679 struct ldb_message *msg,
1680 const struct ldb_val *rdn_new,
1681 const struct ldb_val *rdn_old,
1682 struct replPropertyMetaDataBlob *omd,
1683 struct replmd_replicated_request *ar,
1686 bool is_forced_rodc)
1688 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1689 const struct dsdb_attribute *rdn_attr =
1690 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1691 const char *attr_name = rdn_attr != NULL ?
1692 rdn_attr->lDAPDisplayName :
1694 struct ldb_message_element new_el = {
1695 .flags = LDB_FLAG_MOD_REPLACE,
1698 .values = discard_const_p(struct ldb_val, rdn_new)
1700 struct ldb_message_element old_el = {
1701 .flags = LDB_FLAG_MOD_REPLACE,
1703 .num_values = rdn_old ? 1 : 0,
1704 .values = discard_const_p(struct ldb_val, rdn_old)
1707 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1708 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1709 if (ret != LDB_SUCCESS) {
1710 return ldb_oom(ldb);
1714 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1715 omd, ar->schema, &ar->seq_num,
1716 &ar->our_invocation_id,
1717 now, is_schema_nc, is_forced_rodc,
1722 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1724 uint32_t count = omd.ctr.ctr1.count;
1727 for (i=0; i < count; i++) {
1728 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1729 if (max < m.local_usn) {
1737 * update the replPropertyMetaData object each time we modify an
1738 * object. This is needed for DRS replication, as the merge on the
1739 * client is based on this object
1741 static int replmd_update_rpmd(struct ldb_module *module,
1742 const struct dsdb_schema *schema,
1743 struct ldb_request *req,
1744 const char * const *rename_attrs,
1745 struct ldb_message *msg, uint64_t *seq_num,
1746 time_t t, bool is_schema_nc,
1747 bool *is_urgent, bool *rodc)
1749 const struct ldb_val *omd_value;
1750 enum ndr_err_code ndr_err;
1751 struct replPropertyMetaDataBlob omd;
1754 const struct GUID *our_invocation_id;
1756 const char * const *attrs = NULL;
1757 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1758 struct ldb_result *res;
1759 struct ldb_context *ldb;
1760 struct ldb_message_element *objectclass_el;
1761 enum urgent_situation situation;
1762 bool rmd_is_provided;
1763 bool rmd_is_just_resorted = false;
1764 const char *not_rename_attrs[4 + msg->num_elements];
1765 bool is_forced_rodc = false;
1768 attrs = rename_attrs;
1770 for (i = 0; i < msg->num_elements; i++) {
1771 not_rename_attrs[i] = msg->elements[i].name;
1773 not_rename_attrs[i] = "replPropertyMetaData";
1774 not_rename_attrs[i+1] = "objectClass";
1775 not_rename_attrs[i+2] = "instanceType";
1776 not_rename_attrs[i+3] = NULL;
1777 attrs = not_rename_attrs;
1780 ldb = ldb_module_get_ctx(module);
1782 ret = samdb_rodc(ldb, rodc);
1783 if (ret != LDB_SUCCESS) {
1784 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1789 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1790 is_forced_rodc = true;
1793 our_invocation_id = samdb_ntds_invocation_id(ldb);
1794 if (!our_invocation_id) {
1795 /* this happens during an initial vampire while
1796 updating the schema */
1797 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1801 unix_to_nt_time(&now, t);
1803 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1804 rmd_is_provided = true;
1805 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1806 rmd_is_just_resorted = true;
1809 rmd_is_provided = false;
1812 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1813 * otherwise we consider we are updating */
1814 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1815 situation = REPL_URGENT_ON_DELETE;
1816 } else if (rename_attrs) {
1817 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1819 situation = REPL_URGENT_ON_UPDATE;
1822 if (rmd_is_provided) {
1823 /* In this case the change_replmetadata control was supplied */
1824 /* We check that it's the only attribute that is provided
1825 * (it's a rare case so it's better to keep the code simplier)
1826 * We also check that the highest local_usn is bigger or the same as
1829 if( msg->num_elements != 1 ||
1830 strncmp(msg->elements[0].name,
1831 "replPropertyMetaData", 20) ) {
1832 DEBUG(0,(__location__ ": changereplmetada control called without "\
1833 "a specified replPropertyMetaData attribute or with others\n"));
1834 return LDB_ERR_OPERATIONS_ERROR;
1836 if (situation != REPL_URGENT_ON_UPDATE) {
1837 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1838 return LDB_ERR_OPERATIONS_ERROR;
1840 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1842 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1843 ldb_dn_get_linearized(msg->dn)));
1844 return LDB_ERR_OPERATIONS_ERROR;
1846 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1847 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1848 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1849 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1850 ldb_dn_get_linearized(msg->dn)));
1851 return LDB_ERR_OPERATIONS_ERROR;
1854 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1855 DSDB_FLAG_NEXT_MODULE |
1856 DSDB_SEARCH_SHOW_RECYCLED |
1857 DSDB_SEARCH_SHOW_EXTENDED_DN |
1858 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1859 DSDB_SEARCH_REVEAL_INTERNALS, req);
1861 if (ret != LDB_SUCCESS) {
1865 if (rmd_is_just_resorted == false) {
1866 *seq_num = find_max_local_usn(omd);
1868 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1871 * The test here now allows for a new
1872 * replPropertyMetaData with no change, if was
1873 * just dbcheck re-sorting the values.
1875 if (*seq_num <= db_seq) {
1876 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1877 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1878 (long long)*seq_num, (long long)db_seq));
1879 return LDB_ERR_OPERATIONS_ERROR;
1884 /* search for the existing replPropertyMetaDataBlob. We need
1885 * to use REVEAL and ask for DNs in storage format to support
1886 * the check for values being the same in
1887 * replmd_update_rpmd_element()
1889 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1890 DSDB_FLAG_NEXT_MODULE |
1891 DSDB_SEARCH_SHOW_RECYCLED |
1892 DSDB_SEARCH_SHOW_EXTENDED_DN |
1893 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1894 DSDB_SEARCH_REVEAL_INTERNALS, req);
1895 if (ret != LDB_SUCCESS) {
1899 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1901 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1902 ldb_dn_get_linearized(msg->dn)));
1903 return LDB_ERR_OPERATIONS_ERROR;
1906 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1907 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1908 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1909 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1910 ldb_dn_get_linearized(msg->dn)));
1911 return LDB_ERR_OPERATIONS_ERROR;
1914 if (omd.version != 1) {
1915 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1916 omd.version, ldb_dn_get_linearized(msg->dn)));
1917 return LDB_ERR_OPERATIONS_ERROR;
1920 for (i=0; i<msg->num_elements;) {
1921 struct ldb_message_element *el = &msg->elements[i];
1922 struct ldb_message_element *old_el;
1924 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1925 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1926 &omd, schema, seq_num,
1931 if (ret != LDB_SUCCESS) {
1935 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1936 *is_urgent = replmd_check_urgent_attribute(el);
1939 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1944 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1946 if (el->num_values != 0) {
1951 ldb_msg_remove_element(msg, el);
1956 * Assert that we have an objectClass attribute - this is major
1957 * corruption if we don't have this!
1959 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1960 if (objectclass_el != NULL) {
1962 * Now check if this objectClass means we need to do urgent replication
1964 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1968 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1969 ldb_asprintf_errstring(ldb, __location__
1970 ": objectClass missing on %s\n",
1971 ldb_dn_get_linearized(msg->dn));
1972 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1976 * replmd_update_rpmd_element has done an update if the
1979 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1980 struct ldb_val *md_value;
1981 struct ldb_message_element *el;
1983 /*if we are RODC and this is a DRSR update then its ok*/
1984 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1985 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1986 && !is_forced_rodc) {
1987 unsigned instanceType;
1990 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1991 return LDB_ERR_REFERRAL;
1994 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1995 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1996 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1997 "cannot change replicated attribute on partial replica");
2001 md_value = talloc(msg, struct ldb_val);
2002 if (md_value == NULL) {
2004 return LDB_ERR_OPERATIONS_ERROR;
2007 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2008 if (ret != LDB_SUCCESS) {
2009 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2013 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2014 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2015 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2016 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2017 ldb_dn_get_linearized(msg->dn)));
2018 return LDB_ERR_OPERATIONS_ERROR;
2021 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2022 if (ret != LDB_SUCCESS) {
2023 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2024 ldb_dn_get_linearized(msg->dn)));
2029 el->values = md_value;
2035 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2037 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2039 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2040 &pdn2->dsdb_dn->extra_part);
2046 get a series of message element values as an array of DNs and GUIDs
2047 the result is sorted by GUID
2049 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2050 struct ldb_message_element *el, struct parsed_dn **pdn,
2051 const char *ldap_oid, struct ldb_request *parent)
2054 bool values_are_sorted = true;
2055 struct ldb_context *ldb = ldb_module_get_ctx(module);
2062 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2064 ldb_module_oom(module);
2065 return LDB_ERR_OPERATIONS_ERROR;
2068 for (i=0; i<el->num_values; i++) {
2069 struct ldb_val *v = &el->values[i];
2072 struct parsed_dn *p;
2076 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2077 if (p->dsdb_dn == NULL) {
2078 return LDB_ERR_INVALID_DN_SYNTAX;
2081 dn = p->dsdb_dn->dn;
2083 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2084 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2085 unlikely(GUID_all_zero(&p->guid))) {
2086 /* we got a DN without a GUID - go find the GUID */
2087 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2088 if (ret != LDB_SUCCESS) {
2089 char *dn_str = NULL;
2090 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2092 ldb_asprintf_errstring(ldb,
2093 "Unable to find GUID for DN %s\n",
2095 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2096 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2097 ldb_attr_cmp(el->name, "member") == 0) {
2098 return LDB_ERR_UNWILLING_TO_PERFORM;
2102 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2103 if (ret != LDB_SUCCESS) {
2106 } else if (!NT_STATUS_IS_OK(status)) {
2107 return LDB_ERR_OPERATIONS_ERROR;
2109 if (i > 0 && values_are_sorted) {
2110 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2112 values_are_sorted = false;
2115 /* keep a pointer to the original ldb_val */
2118 if (! values_are_sorted) {
2119 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2125 * Get a series of trusted message element values. The result is sorted by
2126 * GUID, even though the GUIDs might not be known. That works because we trust
2127 * the database to give us the elements like that if the
2128 * replmd_private->sorted_links flag is set.
2130 * We also ensure that the links are in the Functional Level 2003
2131 * linked attributes format.
2133 static int get_parsed_dns_trusted_fallback(struct ldb_module *module,
2134 struct replmd_private *replmd_private,
2135 TALLOC_CTX *mem_ctx,
2136 struct ldb_message_element *el,
2137 struct parsed_dn **pdn,
2138 const char *ldap_oid,
2139 struct ldb_request *parent)
2147 if (!replmd_private->sorted_links) {
2148 /* We need to sort the list. This is the slow old path we want
2151 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2153 if (ret != LDB_SUCCESS) {
2157 ret = get_parsed_dns_trusted(mem_ctx, el, pdn);
2158 if (ret != LDB_SUCCESS) {
2159 ldb_module_oom(module);
2160 return LDB_ERR_OPERATIONS_ERROR;
2165 * This upgrades links to FL2003 style, and sorts the result
2166 * if that was needed.
2168 * TODO: Add a database feature that asserts we have no FL2000
2169 * style links to avoid this check or add a feature that
2170 * uses a similar check to find sorted/unsorted links
2171 * for an on-the-fly upgrade.
2174 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2175 *pdn, el->num_values,
2178 if (ret != LDB_SUCCESS) {
2186 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2187 otherwise an error code. For compatibility the error code differs depending
2188 on whether or not the attribute is "member".
2190 As always, the parsed_dn list is assumed to be sorted.
2192 static int check_parsed_dn_duplicates(struct ldb_module *module,
2193 struct ldb_message_element *el,
2194 struct parsed_dn *pdn)
2197 struct ldb_context *ldb = ldb_module_get_ctx(module);
2199 for (i = 1; i < el->num_values; i++) {
2200 struct parsed_dn *p = &pdn[i];
2201 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2202 ldb_asprintf_errstring(ldb,
2203 "Linked attribute %s has "
2204 "multiple identical values",
2206 if (ldb_attr_cmp(el->name, "member") == 0) {
2207 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2209 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2217 build a new extended DN, including all meta data fields
2219 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2220 RMD_ADDTIME = originating_add_time
2221 RMD_INVOCID = originating_invocation_id
2222 RMD_CHANGETIME = originating_change_time
2223 RMD_ORIGINATING_USN = originating_usn
2224 RMD_LOCAL_USN = local_usn
2225 RMD_VERSION = version
2227 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2228 struct dsdb_dn *dsdb_dn,
2229 const struct GUID *invocation_id,
2230 uint64_t local_usn, NTTIME nttime)
2232 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2233 local_usn, local_usn, nttime,
2234 RMD_VERSION_INITIAL, false);
2237 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2238 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2239 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2243 check if any links need upgrading from w2k format
2245 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2246 struct parsed_dn *dns, uint32_t count,
2247 struct ldb_message_element *el,
2248 const char *ldap_oid)
2251 const struct GUID *invocation_id = NULL;
2252 for (i=0; i<count; i++) {
2256 if (dns[i].dsdb_dn == NULL) {
2257 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2259 if (ret != LDB_SUCCESS) {
2260 return LDB_ERR_INVALID_DN_SYNTAX;
2264 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2265 &version, "RMD_VERSION");
2266 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2268 * We optimistically assume they are all the same; if
2269 * the first one is fixed, they are all fixed.
2271 * If the first one was *not* fixed and we find a
2272 * later one that is, that is an occasion to shout
2278 DEBUG(0, ("Mixed w2k and fixed format "
2279 "linked attributes\n"));
2283 if (invocation_id == NULL) {
2284 invocation_id = samdb_ntds_invocation_id(ldb);
2285 if (invocation_id == NULL) {
2286 return LDB_ERR_OPERATIONS_ERROR;
2291 /* it's an old one that needs upgrading */
2292 ret = replmd_update_la_val(el->values, dns[i].v,
2293 dns[i].dsdb_dn, dns[i].dsdb_dn,
2294 invocation_id, 1, 1, 0, false);
2295 if (ret != LDB_SUCCESS) {
2301 * This sort() is critical for the operation of
2302 * get_parsed_dns_trusted_fallback() because callers of this function
2303 * expect a sorted list, and FL2000 style links are not
2304 * sorted. In particular, as well as the upgrade case,
2305 * get_parsed_dns_trusted_fallback() is called from
2306 * replmd_delete_remove_link() even in FL2000 mode
2308 * We do not normally pay the cost of the qsort() due to the
2309 * early return in the RMD_VERSION found case.
2311 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2316 Sets the value for a linked attribute, including all meta data fields
2318 see replmd_build_la_val for value names
2320 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2321 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2322 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2323 uint32_t version, bool deleted)
2325 struct ldb_dn *dn = dsdb_dn->dn;
2326 const char *tstring, *usn_string, *flags_string;
2327 struct ldb_val tval;
2329 struct ldb_val usnv, local_usnv;
2330 struct ldb_val vers, flagsv;
2331 const struct ldb_val *old_addtime = NULL;
2334 const char *dnstring;
2336 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2338 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2340 return LDB_ERR_OPERATIONS_ERROR;
2342 tval = data_blob_string_const(tstring);
2344 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2346 return LDB_ERR_OPERATIONS_ERROR;
2348 usnv = data_blob_string_const(usn_string);
2350 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2352 return LDB_ERR_OPERATIONS_ERROR;
2354 local_usnv = data_blob_string_const(usn_string);
2356 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2357 if (!NT_STATUS_IS_OK(status)) {
2358 return LDB_ERR_OPERATIONS_ERROR;
2361 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2362 if (!flags_string) {
2363 return LDB_ERR_OPERATIONS_ERROR;
2365 flagsv = data_blob_string_const(flags_string);
2367 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2368 if (ret != LDB_SUCCESS) return ret;
2370 /* get the ADDTIME from the original */
2371 if (old_dsdb_dn != NULL) {
2372 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2375 if (old_addtime == NULL) {
2376 old_addtime = &tval;
2378 if (dsdb_dn != old_dsdb_dn ||
2379 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2380 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2381 if (ret != LDB_SUCCESS) return ret;
2384 /* use our invocation id */
2385 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2386 if (ret != LDB_SUCCESS) return ret;
2388 /* changetime is the current time */
2389 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2390 if (ret != LDB_SUCCESS) return ret;
2392 /* update the USN */
2393 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2394 if (ret != LDB_SUCCESS) return ret;
2396 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2397 if (ret != LDB_SUCCESS) return ret;
2399 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2400 vers = data_blob_string_const(vstring);
2401 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2402 if (ret != LDB_SUCCESS) return ret;
2404 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2405 if (dnstring == NULL) {
2406 return LDB_ERR_OPERATIONS_ERROR;
2408 *v = data_blob_string_const(dnstring);
2414 * Updates the value for a linked attribute, including all meta data fields
2416 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2417 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2418 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2421 uint32_t old_version;
2422 uint32_t version = RMD_VERSION_INITIAL;
2426 * We're updating the linked attribute locally, so increase the version
2427 * by 1 so that other DCs will see the change when it gets replicated out
2429 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2432 if (NT_STATUS_IS_OK(status)) {
2433 version = old_version + 1;
2436 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2437 usn, local_usn, nttime, version, deleted);
2441 handle adding a linked attribute
2443 static int replmd_modify_la_add(struct ldb_module *module,
2444 struct replmd_private *replmd_private,
2445 struct replmd_replicated_request *ac,
2446 struct ldb_message *msg,
2447 struct ldb_message_element *el,
2448 struct ldb_message_element *old_el,
2449 const struct dsdb_attribute *schema_attr,
2451 struct ldb_dn *msg_dn,
2452 struct ldb_request *parent)
2455 struct parsed_dn *dns, *old_dns;
2456 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2458 struct ldb_val *new_values = NULL;
2459 unsigned old_num_values = old_el ? old_el->num_values : 0;
2460 unsigned num_values = 0;
2461 unsigned max_num_values;
2462 struct ldb_context *ldb = ldb_module_get_ctx(module);
2464 unix_to_nt_time(&now, t);
2466 /* get the DNs to be added, fully parsed.
2468 * We need full parsing because they came off the wire and we don't
2469 * trust them, besides which we need their details to know where to put
2472 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2473 schema_attr->syntax->ldap_oid, parent);
2474 if (ret != LDB_SUCCESS) {
2475 talloc_free(tmp_ctx);
2479 /* get the existing DNs, lazily parsed */
2480 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2481 tmp_ctx, old_el, &old_dns,
2482 schema_attr->syntax->ldap_oid,
2485 if (ret != LDB_SUCCESS) {
2486 talloc_free(tmp_ctx);
2490 max_num_values = old_num_values + el->num_values;
2491 if (max_num_values < old_num_values) {
2492 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2493 "old values: %u, new values: %u, sum: %u\n",
2494 old_num_values, el->num_values, max_num_values));
2495 talloc_free(tmp_ctx);
2496 return LDB_ERR_OPERATIONS_ERROR;
2499 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2501 if (new_values == NULL) {
2502 ldb_module_oom(module);
2503 talloc_free(tmp_ctx);
2504 return LDB_ERR_OPERATIONS_ERROR;
2508 * For each new value, find where it would go in the list. If there is
2509 * a matching GUID there, we update the existing value; otherwise we
2513 for (i = 0; i < el->num_values; i++) {
2514 struct parsed_dn *exact;
2515 struct parsed_dn *next;
2517 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2520 dns[i].dsdb_dn->extra_part, 0,
2522 schema_attr->syntax->ldap_oid,
2524 if (err != LDB_SUCCESS) {
2525 talloc_free(tmp_ctx);
2529 if (ac->fix_link_sid) {
2530 char *fixed_dnstring = NULL;
2531 struct dom_sid tmp_sid = { 0, };
2532 DATA_BLOB sid_blob = data_blob_null;
2533 enum ndr_err_code ndr_err;
2537 if (exact == NULL) {
2538 talloc_free(tmp_ctx);
2539 return ldb_operr(ldb);
2542 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2543 talloc_free(tmp_ctx);
2544 return ldb_operr(ldb);
2548 * Only "<GUID=...><SID=...>" is allowed.
2550 * We get the GUID to just to find the old
2551 * value and the SID in order to add it
2552 * to the found value.
2555 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2557 talloc_free(tmp_ctx);
2558 return ldb_operr(ldb);
2561 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2563 talloc_free(tmp_ctx);
2564 return ldb_operr(ldb);
2567 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2569 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2570 /* this is what we expect */
2571 } else if (NT_STATUS_IS_OK(status)) {
2572 struct GUID_txt_buf guid_str;
2573 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2574 "i[%u] SID NOT MISSING... Attribute %s already "
2575 "exists for target GUID %s, SID %s, DN: %s",
2577 GUID_buf_string(&exact->guid,
2579 dom_sid_string(tmp_ctx, &tmp_sid),
2580 dsdb_dn_get_extended_linearized(tmp_ctx,
2581 exact->dsdb_dn, 1));
2582 talloc_free(tmp_ctx);
2583 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2585 talloc_free(tmp_ctx);
2586 return ldb_operr(ldb);
2589 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2591 if (!NT_STATUS_IS_OK(status)) {
2592 struct GUID_txt_buf guid_str;
2593 ldb_asprintf_errstring(ldb,
2594 "NO SID PROVIDED... Attribute %s already "
2595 "exists for target GUID %s",
2597 GUID_buf_string(&exact->guid,
2599 talloc_free(tmp_ctx);
2600 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2603 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2604 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2605 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2606 talloc_free(tmp_ctx);
2607 return ldb_operr(ldb);
2610 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2611 data_blob_free(&sid_blob);
2612 if (ret != LDB_SUCCESS) {
2613 talloc_free(tmp_ctx);
2617 fixed_dnstring = dsdb_dn_get_extended_linearized(
2618 new_values, exact->dsdb_dn, 1);
2619 if (fixed_dnstring == NULL) {
2620 talloc_free(tmp_ctx);
2621 return ldb_operr(ldb);
2625 * We just replace the existing value...
2627 *exact->v = data_blob_string_const(fixed_dnstring);
2632 if (exact != NULL) {
2634 * We are trying to add one that exists, which is only
2635 * allowed if it was previously deleted.
2637 * When we do undelete a link we change it in place.
2638 * It will be copied across into the right spot in due
2642 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2644 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2645 struct GUID_txt_buf guid_str;
2646 ldb_asprintf_errstring(ldb,
2647 "Attribute %s already "
2648 "exists for target GUID %s",
2650 GUID_buf_string(&exact->guid,
2652 talloc_free(tmp_ctx);
2653 /* error codes for 'member' need to be
2655 if (ldb_attr_cmp(el->name, "member") == 0) {
2656 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2658 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2662 ret = replmd_update_la_val(new_values, exact->v,
2665 &ac->our_invocation_id,
2666 ac->seq_num, ac->seq_num,
2668 if (ret != LDB_SUCCESS) {
2669 talloc_free(tmp_ctx);
2673 ret = replmd_add_backlink(module, replmd_private,
2680 if (ret != LDB_SUCCESS) {
2681 talloc_free(tmp_ctx);
2687 * Here we don't have an exact match.
2689 * If next is NULL, this one goes beyond the end of the
2690 * existing list, so we need to add all of those ones first.
2692 * If next is not NULL, we need to add all the ones before
2696 offset = old_num_values;
2698 /* next should have been parsed, but let's make sure */
2699 if (next->dsdb_dn == NULL) {
2700 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2701 schema_attr->syntax->ldap_oid);
2702 if (ret != LDB_SUCCESS) {
2706 offset = MIN(next - old_dns, old_num_values);
2709 /* put all the old ones before next on the list */
2710 for (; j < offset; j++) {
2711 new_values[num_values] = *old_dns[j].v;
2715 ret = replmd_add_backlink(module, replmd_private,
2720 /* Make the new linked attribute ldb_val. */
2721 ret = replmd_build_la_val(new_values, &new_values[num_values],
2722 dns[i].dsdb_dn, &ac->our_invocation_id,
2724 if (ret != LDB_SUCCESS) {
2725 talloc_free(tmp_ctx);
2729 if (ret != LDB_SUCCESS) {
2730 talloc_free(tmp_ctx);
2734 /* copy the rest of the old ones (if any) */
2735 for (; j < old_num_values; j++) {
2736 new_values[num_values] = *old_dns[j].v;
2740 talloc_steal(msg->elements, new_values);
2741 if (old_el != NULL) {
2742 talloc_steal(msg->elements, old_el->values);
2744 el->values = new_values;
2745 el->num_values = num_values;
2747 talloc_free(tmp_ctx);
2749 /* we now tell the backend to replace all existing values
2750 with the one we have constructed */
2751 el->flags = LDB_FLAG_MOD_REPLACE;
2758 handle deleting all active linked attributes
2760 static int replmd_modify_la_delete(struct ldb_module *module,
2761 struct replmd_private *replmd_private,
2762 struct replmd_replicated_request *ac,
2763 struct ldb_message *msg,
2764 struct ldb_message_element *el,
2765 struct ldb_message_element *old_el,
2766 const struct dsdb_attribute *schema_attr,
2768 struct ldb_dn *msg_dn,
2769 struct ldb_request *parent)
2772 struct parsed_dn *dns, *old_dns;
2773 TALLOC_CTX *tmp_ctx = NULL;
2775 struct ldb_context *ldb = ldb_module_get_ctx(module);
2776 struct ldb_control *vanish_links_ctrl = NULL;
2777 bool vanish_links = false;
2778 unsigned int num_to_delete = el->num_values;
2782 unix_to_nt_time(&now, t);
2784 if (old_el == NULL || old_el->num_values == 0) {
2785 /* there is nothing to delete... */
2786 if (num_to_delete == 0) {
2787 /* and we're deleting nothing, so that's OK */
2790 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2793 tmp_ctx = talloc_new(msg);
2794 if (tmp_ctx == NULL) {
2795 return LDB_ERR_OPERATIONS_ERROR;
2798 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2799 schema_attr->syntax->ldap_oid, parent);
2800 if (ret != LDB_SUCCESS) {
2801 talloc_free(tmp_ctx);
2805 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2806 tmp_ctx, old_el, &old_dns,
2807 schema_attr->syntax->ldap_oid,
2810 if (ret != LDB_SUCCESS) {
2811 talloc_free(tmp_ctx);
2816 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2817 if (vanish_links_ctrl) {
2818 vanish_links = true;
2819 vanish_links_ctrl->critical = false;
2823 /* we empty out el->values here to avoid damage if we return early. */
2828 * If vanish links is set, we are actually removing members of
2829 * old_el->values; otherwise we are just marking them deleted.
2831 * There is a special case when no values are given: we remove them
2832 * all. When we have the vanish_links control we just have to remove
2833 * the backlinks and change our element to replace the existing values
2834 * with the empty list.
2837 if (num_to_delete == 0) {
2838 for (i = 0; i < old_el->num_values; i++) {
2839 struct parsed_dn *p = &old_dns[i];
2840 if (p->dsdb_dn == NULL) {
2841 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2842 schema_attr->syntax->ldap_oid);
2843 if (ret != LDB_SUCCESS) {
2847 ret = replmd_add_backlink(module, replmd_private,
2848 ac->schema, msg_dn, &p->guid,
2851 if (ret != LDB_SUCCESS) {
2852 talloc_free(tmp_ctx);
2859 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2860 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2864 ret = replmd_update_la_val(old_el->values, p->v,
2865 p->dsdb_dn, p->dsdb_dn,
2866 &ac->our_invocation_id,
2867 ac->seq_num, ac->seq_num,
2869 if (ret != LDB_SUCCESS) {
2870 talloc_free(tmp_ctx);
2876 el->flags = LDB_FLAG_MOD_REPLACE;
2877 talloc_free(tmp_ctx);
2883 for (i = 0; i < num_to_delete; i++) {
2884 struct parsed_dn *p = &dns[i];
2885 struct parsed_dn *exact = NULL;
2886 struct parsed_dn *next = NULL;
2887 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2890 p->dsdb_dn->extra_part, 0,
2892 schema_attr->syntax->ldap_oid,
2894 if (ret != LDB_SUCCESS) {
2895 talloc_free(tmp_ctx);
2898 if (exact == NULL) {
2899 struct GUID_txt_buf buf;
2900 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2901 "exist for target GUID %s",
2903 GUID_buf_string(&p->guid, &buf));
2904 if (ldb_attr_cmp(el->name, "member") == 0) {
2905 talloc_free(tmp_ctx);
2906 return LDB_ERR_UNWILLING_TO_PERFORM;
2908 talloc_free(tmp_ctx);
2909 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2914 if (CHECK_DEBUGLVL(5)) {
2915 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2916 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2917 struct GUID_txt_buf buf;
2918 const char *guid_str = \
2919 GUID_buf_string(&p->guid, &buf);
2920 DEBUG(5, ("Deleting deleted linked "
2921 "attribute %s to %s, because "
2922 "vanish_links control is set\n",
2923 el->name, guid_str));
2927 /* remove the backlink */
2928 ret = replmd_add_backlink(module,
2935 if (ret != LDB_SUCCESS) {
2936 talloc_free(tmp_ctx);
2940 /* We flag the deletion and tidy it up later. */
2945 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2947 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2948 struct GUID_txt_buf buf;
2949 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2950 ldb_asprintf_errstring(ldb, "Attribute %s already "
2951 "deleted for target GUID %s",
2952 el->name, guid_str);
2953 if (ldb_attr_cmp(el->name, "member") == 0) {
2954 talloc_free(tmp_ctx);
2955 return LDB_ERR_UNWILLING_TO_PERFORM;
2957 talloc_free(tmp_ctx);
2958 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2962 ret = replmd_update_la_val(old_el->values, exact->v,
2963 exact->dsdb_dn, exact->dsdb_dn,
2964 &ac->our_invocation_id,
2965 ac->seq_num, ac->seq_num,
2967 if (ret != LDB_SUCCESS) {
2968 talloc_free(tmp_ctx);
2971 ret = replmd_add_backlink(module, replmd_private,
2976 if (ret != LDB_SUCCESS) {
2977 talloc_free(tmp_ctx);
2984 struct ldb_val *tmp_vals = NULL;
2986 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2987 old_el->num_values);
2988 if (tmp_vals == NULL) {
2989 talloc_free(tmp_ctx);
2990 return ldb_module_oom(module);
2992 for (i = 0; i < old_el->num_values; i++) {
2993 if (old_dns[i].v == NULL) {
2996 tmp_vals[j] = *old_dns[i].v;
2999 for (i = 0; i < j; i++) {
3000 old_el->values[i] = tmp_vals[i];
3002 old_el->num_values = j;
3005 el->values = talloc_steal(msg->elements, old_el->values);
3006 el->num_values = old_el->num_values;
3008 talloc_free(tmp_ctx);
3010 /* we now tell the backend to replace all existing values
3011 with the one we have constructed */
3012 el->flags = LDB_FLAG_MOD_REPLACE;
3018 handle replacing a linked attribute
3020 static int replmd_modify_la_replace(struct ldb_module *module,
3021 struct replmd_private *replmd_private,
3022 struct replmd_replicated_request *ac,
3023 struct ldb_message *msg,
3024 struct ldb_message_element *el,
3025 struct ldb_message_element *old_el,
3026 const struct dsdb_attribute *schema_attr,
3028 struct ldb_dn *msg_dn,
3029 struct ldb_request *parent)
3031 unsigned int i, old_i, new_i;
3032 struct parsed_dn *dns, *old_dns;
3033 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3035 struct ldb_context *ldb = ldb_module_get_ctx(module);
3036 struct ldb_val *new_values = NULL;
3037 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3038 unsigned int old_num_values;
3039 unsigned int repl_num_values;
3040 unsigned int max_num_values;
3043 unix_to_nt_time(&now, t);
3046 * The replace operation is unlike the replace and delete cases in that
3047 * we need to look at every existing link to see whether it is being
3048 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3050 * As we are trying to combine two sorted lists, the algorithm we use
3051 * is akin to the merge phase of a merge sort. We interleave the two
3052 * lists, doing different things depending on which side the current
3055 * There are three main cases, with some sub-cases.
3057 * - a DN is in the old list but not the new one. It needs to be
3058 * marked as deleted (but left in the list).
3059 * - maybe it is already deleted, and we have less to do.
3061 * - a DN is in both lists. The old data gets replaced by the new,
3062 * and the list doesn't grow. The old link may have been marked as
3063 * deleted, in which case we undelete it.
3065 * - a DN is in the new list only. We add it in the right place.
3068 old_num_values = old_el ? old_el->num_values : 0;
3069 repl_num_values = el->num_values;
3070 max_num_values = old_num_values + repl_num_values;
3072 if (max_num_values == 0) {
3073 /* There is nothing to do! */
3077 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3078 if (ret != LDB_SUCCESS) {
3079 talloc_free(tmp_ctx);
3083 ret = check_parsed_dn_duplicates(module, el, dns);
3084 if (ret != LDB_SUCCESS) {
3085 talloc_free(tmp_ctx);
3089 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3091 if (ret != LDB_SUCCESS) {
3092 talloc_free(tmp_ctx);
3096 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3098 if (ret != LDB_SUCCESS) {
3099 talloc_free(tmp_ctx);
3103 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3104 if (new_values == NULL) {
3105 ldb_module_oom(module);
3106 talloc_free(tmp_ctx);
3107 return LDB_ERR_OPERATIONS_ERROR;
3112 for (i = 0; i < max_num_values; i++) {
3114 struct parsed_dn *old_p, *new_p;
3115 if (old_i < old_num_values && new_i < repl_num_values) {
3116 old_p = &old_dns[old_i];
3117 new_p = &dns[new_i];
3118 cmp = parsed_dn_compare(old_p, new_p);
3119 } else if (old_i < old_num_values) {
3120 /* the new list is empty, read the old list */
3121 old_p = &old_dns[old_i];
3124 } else if (new_i < repl_num_values) {
3125 /* the old list is empty, read new list */
3127 new_p = &dns[new_i];
3135 * An old ones that come before the next replacement
3136 * (if any). We mark it as deleted and add it to the
3139 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3140 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3141 ret = replmd_update_la_val(new_values, old_p->v,
3144 &ac->our_invocation_id,
3145 ac->seq_num, ac->seq_num,
3147 if (ret != LDB_SUCCESS) {
3148 talloc_free(tmp_ctx);
3152 ret = replmd_add_backlink(module, replmd_private,
3155 &old_p->guid, false,
3158 if (ret != LDB_SUCCESS) {
3159 talloc_free(tmp_ctx);
3163 new_values[i] = *old_p->v;
3165 } else if (cmp == 0) {
3167 * We are overwriting one. If it was previously
3168 * deleted, we need to add a backlink.
3170 * Note that if any RMD_FLAGs in an extended new DN
3175 ret = replmd_update_la_val(new_values, old_p->v,
3178 &ac->our_invocation_id,
3179 ac->seq_num, ac->seq_num,
3181 if (ret != LDB_SUCCESS) {
3182 talloc_free(tmp_ctx);
3186 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3187 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3188 ret = replmd_add_backlink(module, replmd_private,
3194 if (ret != LDB_SUCCESS) {
3195 talloc_free(tmp_ctx);
3200 new_values[i] = *old_p->v;
3205 * Replacements that don't match an existing one. We
3206 * just add them to the final list.
3208 ret = replmd_build_la_val(new_values,
3211 &ac->our_invocation_id,
3213 if (ret != LDB_SUCCESS) {
3214 talloc_free(tmp_ctx);
3217 ret = replmd_add_backlink(module, replmd_private,
3223 if (ret != LDB_SUCCESS) {
3224 talloc_free(tmp_ctx);
3227 new_values[i] = *new_p->v;
3231 if (old_el != NULL) {
3232 talloc_steal(msg->elements, old_el->values);
3234 el->values = talloc_steal(msg->elements, new_values);
3236 talloc_free(tmp_ctx);
3238 el->flags = LDB_FLAG_MOD_REPLACE;
3245 handle linked attributes in modify requests
3247 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3248 struct replmd_private *replmd_private,
3249 struct replmd_replicated_request *ac,
3250 struct ldb_message *msg,
3252 struct ldb_request *parent)
3254 struct ldb_result *res;
3257 struct ldb_context *ldb = ldb_module_get_ctx(module);
3258 struct ldb_message *old_msg;
3260 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3262 * Nothing special is required for modifying or vanishing links
3263 * in fl2000 since they are just strings in a multi-valued
3266 struct ldb_control *ctrl = ldb_request_get_control(parent,
3267 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3269 ctrl->critical = false;
3277 * We should restrict this to the intersection of the list of
3278 * linked attributes in the schema and the list of attributes
3281 * This will help performance a little, as otherwise we have
3282 * to allocate the entire object value-by-value.
3284 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3285 DSDB_FLAG_NEXT_MODULE |
3286 DSDB_SEARCH_SHOW_RECYCLED |
3287 DSDB_SEARCH_REVEAL_INTERNALS |
3288 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3290 if (ret != LDB_SUCCESS) {
3294 old_msg = res->msgs[0];
3296 for (i=0; i<msg->num_elements; i++) {
3297 struct ldb_message_element *el = &msg->elements[i];
3298 struct ldb_message_element *old_el, *new_el;
3299 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3300 const struct dsdb_attribute *schema_attr
3301 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3303 ldb_asprintf_errstring(ldb,
3304 "%s: attribute %s is not a valid attribute in schema",
3305 __FUNCTION__, el->name);
3306 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3308 if (schema_attr->linkID == 0) {
3311 if ((schema_attr->linkID & 1) == 1) {
3313 struct ldb_control *ctrl;
3315 ctrl = ldb_request_get_control(parent,
3316 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3318 ctrl->critical = false;
3321 ctrl = ldb_request_get_control(parent,
3322 DSDB_CONTROL_DBCHECK);
3328 /* Odd is for the target. Illegal to modify */
3329 ldb_asprintf_errstring(ldb,
3330 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3331 return LDB_ERR_UNWILLING_TO_PERFORM;
3333 old_el = ldb_msg_find_element(old_msg, el->name);
3335 case LDB_FLAG_MOD_REPLACE:
3336 ret = replmd_modify_la_replace(module, replmd_private,
3337 ac, msg, el, old_el,
3342 case LDB_FLAG_MOD_DELETE:
3343 ret = replmd_modify_la_delete(module, replmd_private,
3344 ac, msg, el, old_el,
3349 case LDB_FLAG_MOD_ADD:
3350 ret = replmd_modify_la_add(module, replmd_private,
3351 ac, msg, el, old_el,
3357 ldb_asprintf_errstring(ldb,
3358 "invalid flags 0x%x for %s linked attribute",
3359 el->flags, el->name);
3360 return LDB_ERR_UNWILLING_TO_PERFORM;
3362 if (ret != LDB_SUCCESS) {
3365 ret = dsdb_check_single_valued_link(schema_attr, el);
3366 if (ret != LDB_SUCCESS) {
3367 ldb_asprintf_errstring(ldb,
3368 "Attribute %s is single valued but more than one value has been supplied",
3370 /* Return codes as found on Windows 2012r2 */
3371 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3372 return LDB_ERR_CONSTRAINT_VIOLATION;
3374 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3377 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3381 ldb_msg_remove_attr(old_msg, el->name);
3383 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3384 new_el->num_values = el->num_values;
3385 new_el->values = talloc_steal(msg->elements, el->values);
3387 /* TODO: this relises a bit too heavily on the exact
3388 behaviour of ldb_msg_find_element and
3389 ldb_msg_remove_element */
3390 old_el = ldb_msg_find_element(msg, el->name);
3392 ldb_msg_remove_element(msg, old_el);
3402 static int send_rodc_referral(struct ldb_request *req,
3403 struct ldb_context *ldb,
3406 char *referral = NULL;
3407 struct loadparm_context *lp_ctx = NULL;
3408 struct ldb_dn *fsmo_role_dn = NULL;
3409 struct ldb_dn *role_owner_dn = NULL;
3410 const char *domain = NULL;
3413 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3414 struct loadparm_context);
3416 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3417 &fsmo_role_dn, &role_owner_dn);
3419 if (W_ERROR_IS_OK(werr)) {
3420 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3421 if (server_dn != NULL) {
3422 ldb_dn_remove_child_components(server_dn, 1);
3423 domain = samdb_dn_to_dnshostname(ldb, req,
3428 if (domain == NULL) {
3429 domain = lpcfg_dnsdomain(lp_ctx);
3432 referral = talloc_asprintf(req, "ldap://%s/%s",
3434 ldb_dn_get_linearized(dn));
3435 if (referral == NULL) {
3437 return LDB_ERR_OPERATIONS_ERROR;
3440 return ldb_module_send_referral(req, referral);
3444 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3446 struct ldb_context *ldb;
3447 struct replmd_replicated_request *ac;
3448 struct ldb_request *down_req;
3449 struct ldb_message *msg;
3450 time_t t = time(NULL);
3452 bool is_urgent = false, rodc = false;
3453 bool is_schema_nc = false;
3454 unsigned int functional_level;
3455 const struct ldb_message_element *guid_el = NULL;
3456 struct ldb_control *sd_propagation_control;
3457 struct ldb_control *fix_links_control = NULL;
3458 struct ldb_control *fix_dn_name_control = NULL;
3459 struct ldb_control *fix_dn_sid_control = NULL;
3460 struct replmd_private *replmd_private =
3461 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3463 /* do not manipulate our control entries */
3464 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3465 return ldb_next_request(module, req);
3468 sd_propagation_control = ldb_request_get_control(req,
3469 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3470 if (sd_propagation_control != NULL) {
3471 if (req->op.mod.message->num_elements != 1) {
3472 return ldb_module_operr(module);
3474 ret = strcmp(req->op.mod.message->elements[0].name,
3475 "nTSecurityDescriptor");
3477 return ldb_module_operr(module);
3480 return ldb_next_request(module, req);
3483 ldb = ldb_module_get_ctx(module);
3485 fix_links_control = ldb_request_get_control(req,
3486 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3487 if (fix_links_control != NULL) {
3488 struct dsdb_schema *schema = NULL;
3489 const struct dsdb_attribute *sa = NULL;
3491 if (req->op.mod.message->num_elements != 1) {
3492 return ldb_module_operr(module);
3495 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3496 return ldb_module_operr(module);
3499 schema = dsdb_get_schema(ldb, req);
3500 if (schema == NULL) {
3501 return ldb_module_operr(module);
3504 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3505 req->op.mod.message->elements[0].name);
3507 return ldb_module_operr(module);
3510 if (sa->linkID == 0) {
3511 return ldb_module_operr(module);
3514 fix_links_control->critical = false;
3515 return ldb_next_request(module, req);
3518 fix_dn_name_control = ldb_request_get_control(req,
3519 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3520 if (fix_dn_name_control != NULL) {
3521 struct dsdb_schema *schema = NULL;
3522 const struct dsdb_attribute *sa = NULL;
3524 if (req->op.mod.message->num_elements != 2) {
3525 return ldb_module_operr(module);
3528 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3529 return ldb_module_operr(module);
3532 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3533 return ldb_module_operr(module);
3536 if (req->op.mod.message->elements[0].num_values != 1) {
3537 return ldb_module_operr(module);
3540 if (req->op.mod.message->elements[1].num_values != 1) {
3541 return ldb_module_operr(module);
3544 schema = dsdb_get_schema(ldb, req);
3545 if (schema == NULL) {
3546 return ldb_module_operr(module);
3549 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3550 req->op.mod.message->elements[1].name) != 0) {
3551 return ldb_module_operr(module);
3554 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3555 req->op.mod.message->elements[0].name);
3557 return ldb_module_operr(module);
3560 if (sa->dn_format == DSDB_INVALID_DN) {
3561 return ldb_module_operr(module);
3564 if (sa->linkID != 0) {
3565 return ldb_module_operr(module);
3569 * If we are run from dbcheck and we are not updating
3570 * a link (as these would need to be sorted and so
3571 * can't go via such a simple update, then do not
3572 * trigger replicated updates and a new USN from this
3573 * change, it wasn't a real change, just a new
3574 * (correct) string DN
3577 fix_dn_name_control->critical = false;
3578 return ldb_next_request(module, req);
3581 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3583 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3584 if (guid_el != NULL) {
3585 ldb_set_errstring(ldb,
3586 "replmd_modify: it's not allowed to change the objectGUID!");
3587 return LDB_ERR_CONSTRAINT_VIOLATION;
3590 ac = replmd_ctx_init(module, req);
3592 return ldb_module_oom(module);
3595 functional_level = dsdb_functional_level(ldb);
3597 /* we have to copy the message as the caller might have it as a const */
3598 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3602 return LDB_ERR_OPERATIONS_ERROR;
3605 fix_dn_sid_control = ldb_request_get_control(req,
3606 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3607 if (fix_dn_sid_control != NULL) {
3608 const struct dsdb_attribute *sa = NULL;
3610 if (msg->num_elements != 1) {
3612 return ldb_module_operr(module);
3615 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3617 return ldb_module_operr(module);
3620 if (msg->elements[0].num_values != 1) {
3622 return ldb_module_operr(module);
3625 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3626 msg->elements[0].name);
3629 return ldb_module_operr(module);
3632 if (sa->dn_format != DSDB_NORMAL_DN) {
3634 return ldb_module_operr(module);
3637 fix_dn_sid_control->critical = false;
3638 ac->fix_link_sid = true;
3640 goto handle_linked_attribs;
3643 ldb_msg_remove_attr(msg, "whenChanged");
3644 ldb_msg_remove_attr(msg, "uSNChanged");
3646 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3648 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3649 msg, &ac->seq_num, t, is_schema_nc,
3651 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3652 ret = send_rodc_referral(req, ldb, msg->dn);
3658 if (ret != LDB_SUCCESS) {
3663 handle_linked_attribs:
3664 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3666 if (ret != LDB_SUCCESS) {
3672 * - replace the old object with the newly constructed one
3675 ac->is_urgent = is_urgent;
3677 ret = ldb_build_mod_req(&down_req, ldb, ac,
3680 ac, replmd_op_callback,
3682 LDB_REQ_SET_LOCATION(down_req);
3683 if (ret != LDB_SUCCESS) {
3688 /* current partition control is needed by "replmd_op_callback" */
3689 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3690 ret = ldb_request_add_control(down_req,
3691 DSDB_CONTROL_CURRENT_PARTITION_OID,
3693 if (ret != LDB_SUCCESS) {
3699 /* If we are in functional level 2000, then
3700 * replmd_modify_handle_linked_attribs will have done
3702 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3703 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3704 if (ret != LDB_SUCCESS) {
3710 talloc_steal(down_req, msg);
3712 /* we only change whenChanged and uSNChanged if the seq_num
3714 if (ac->seq_num != 0) {
3715 ret = add_time_element(msg, "whenChanged", t);
3716 if (ret != LDB_SUCCESS) {
3722 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3723 if (ret != LDB_SUCCESS) {
3730 /* go on with the call chain */
3731 return ldb_next_request(module, down_req);
3734 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3737 handle a rename request
3739 On a rename we need to do an extra ldb_modify which sets the
3740 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3742 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3744 struct ldb_context *ldb;
3745 struct ldb_control *fix_dn_name_control = NULL;
3746 struct replmd_replicated_request *ac;
3748 struct ldb_request *down_req;
3750 /* do not manipulate our control entries */
3751 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3752 return ldb_next_request(module, req);
3755 fix_dn_name_control = ldb_request_get_control(req,
3756 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3757 if (fix_dn_name_control != NULL) {
3758 return ldb_next_request(module, req);
3761 ldb = ldb_module_get_ctx(module);
3763 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3765 ac = replmd_ctx_init(module, req);
3767 return ldb_module_oom(module);
3770 ret = ldb_build_rename_req(&down_req, ldb, ac,
3771 ac->req->op.rename.olddn,
3772 ac->req->op.rename.newdn,
3774 ac, replmd_rename_callback,
3776 LDB_REQ_SET_LOCATION(down_req);
3777 if (ret != LDB_SUCCESS) {
3782 /* go on with the call chain */
3783 return ldb_next_request(module, down_req);
3786 /* After the rename is compleated, update the whenchanged etc */
3787 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3789 struct ldb_context *ldb;
3790 struct ldb_request *down_req;
3791 struct ldb_message *msg;
3792 const struct dsdb_attribute *rdn_attr;
3793 const char *rdn_name;
3794 const struct ldb_val *rdn_val;
3795 const char *attrs[5] = { NULL, };
3796 time_t t = time(NULL);
3798 bool is_urgent = false, rodc = false;
3800 struct replmd_replicated_request *ac =
3801 talloc_get_type(req->context, struct replmd_replicated_request);
3802 struct replmd_private *replmd_private =
3803 talloc_get_type(ldb_module_get_private(ac->module),
3804 struct replmd_private);
3806 ldb = ldb_module_get_ctx(ac->module);
3808 if (ares->error != LDB_SUCCESS) {
3809 return ldb_module_done(ac->req, ares->controls,
3810 ares->response, ares->error);
3813 if (ares->type != LDB_REPLY_DONE) {
3814 ldb_set_errstring(ldb,
3815 "invalid reply type in repl_meta_data rename callback");
3817 return ldb_module_done(ac->req, NULL, NULL,
3818 LDB_ERR_OPERATIONS_ERROR);
3822 * - replace the old object with the newly constructed one
3825 msg = ldb_msg_new(ac);
3828 return LDB_ERR_OPERATIONS_ERROR;
3831 msg->dn = ac->req->op.rename.newdn;
3833 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3835 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3836 if (rdn_name == NULL) {
3838 return ldb_module_done(ac->req, NULL, NULL,
3842 /* normalize the rdn attribute name */
3843 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3844 if (rdn_attr == NULL) {
3846 return ldb_module_done(ac->req, NULL, NULL,
3849 rdn_name = rdn_attr->lDAPDisplayName;
3851 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3852 if (rdn_val == NULL) {
3854 return ldb_module_done(ac->req, NULL, NULL,
3858 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3860 return ldb_module_done(ac->req, NULL, NULL,
3863 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3865 return ldb_module_done(ac->req, NULL, NULL,
3868 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3870 return ldb_module_done(ac->req, NULL, NULL,
3873 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3875 return ldb_module_done(ac->req, NULL, NULL,
3880 * here we let replmd_update_rpmd() only search for
3881 * the existing "replPropertyMetaData" and rdn_name attributes.
3883 * We do not want the existing "name" attribute as
3884 * the "name" attribute needs to get the version
3885 * updated on rename even if the rdn value hasn't changed.
3887 * This is the diff of the meta data, for a moved user
3888 * on a w2k8r2 server:
3891 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3892 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3893 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3894 * version : 0x00000001 (1)
3895 * reserved : 0x00000000 (0)
3896 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3897 * local_usn : 0x00000000000037a5 (14245)
3898 * array: struct replPropertyMetaData1
3899 * attid : DRSUAPI_ATTID_name (0x90001)
3900 * - version : 0x00000001 (1)
3901 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3902 * + version : 0x00000002 (2)
3903 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3904 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3905 * - originating_usn : 0x00000000000037a5 (14245)
3906 * - local_usn : 0x00000000000037a5 (14245)
3907 * + originating_usn : 0x0000000000003834 (14388)
3908 * + local_usn : 0x0000000000003834 (14388)
3909 * array: struct replPropertyMetaData1
3910 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3911 * version : 0x00000004 (4)
3913 attrs[0] = "replPropertyMetaData";
3914 attrs[1] = "objectClass";
3915 attrs[2] = "instanceType";
3916 attrs[3] = rdn_name;
3919 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3920 msg, &ac->seq_num, t,
3921 is_schema_nc, &is_urgent, &rodc);
3922 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3923 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3925 return ldb_module_done(req, NULL, NULL, ret);
3928 if (ret != LDB_SUCCESS) {
3930 return ldb_module_done(ac->req, NULL, NULL, ret);
3933 if (ac->seq_num == 0) {
3935 return ldb_module_done(ac->req, NULL, NULL,
3937 "internal error seq_num == 0"));
3939 ac->is_urgent = is_urgent;
3941 ret = ldb_build_mod_req(&down_req, ldb, ac,
3944 ac, replmd_op_callback,
3946 LDB_REQ_SET_LOCATION(down_req);
3947 if (ret != LDB_SUCCESS) {
3952 /* current partition control is needed by "replmd_op_callback" */
3953 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3954 ret = ldb_request_add_control(down_req,
3955 DSDB_CONTROL_CURRENT_PARTITION_OID,
3957 if (ret != LDB_SUCCESS) {
3963 talloc_steal(down_req, msg);
3965 ret = add_time_element(msg, "whenChanged", t);
3966 if (ret != LDB_SUCCESS) {
3972 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3973 if (ret != LDB_SUCCESS) {
3979 /* go on with the call chain - do the modify after the rename */
3980 return ldb_next_request(ac->module, down_req);
3984 * remove links from objects that point at this object when an object
3985 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3986 * RemoveObj which states that link removal due to the object being
3987 * deleted is NOT an originating update - they just go away!
3990 static int replmd_delete_remove_link(struct ldb_module *module,
3991 const struct dsdb_schema *schema,
3992 struct replmd_private *replmd_private,
3995 struct ldb_message_element *el,
3996 const struct dsdb_attribute *sa,
3997 struct ldb_request *parent)
4000 TALLOC_CTX *tmp_ctx = talloc_new(module);
4001 struct ldb_context *ldb = ldb_module_get_ctx(module);
4003 for (i=0; i<el->num_values; i++) {
4004 struct dsdb_dn *dsdb_dn;
4006 struct ldb_message *msg;
4007 const struct dsdb_attribute *target_attr;
4008 struct ldb_message_element *el2;
4010 struct ldb_val dn_val;
4011 uint32_t dsdb_flags = 0;
4012 const char *attrs[] = { NULL, NULL };
4013 struct ldb_result *link_res;
4014 struct ldb_message *link_msg;
4015 struct ldb_message_element *link_el;
4016 struct parsed_dn *link_dns;
4017 struct parsed_dn *p = NULL, *unused = NULL;
4019 if (dsdb_dn_is_deleted_val(&el->values[i])) {
4023 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4025 talloc_free(tmp_ctx);
4026 return LDB_ERR_OPERATIONS_ERROR;
4029 /* remove the link */
4030 msg = ldb_msg_new(tmp_ctx);
4032 ldb_module_oom(module);
4033 talloc_free(tmp_ctx);
4034 return LDB_ERR_OPERATIONS_ERROR;
4038 msg->dn = dsdb_dn->dn;
4040 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4041 if (target_attr == NULL) {
4044 attrs[0] = target_attr->lDAPDisplayName;
4046 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4047 LDB_FLAG_MOD_DELETE, &el2);
4048 if (ret != LDB_SUCCESS) {
4049 ldb_module_oom(module);
4050 talloc_free(tmp_ctx);
4051 return LDB_ERR_OPERATIONS_ERROR;
4054 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4056 DSDB_FLAG_NEXT_MODULE |
4057 DSDB_SEARCH_SHOW_EXTENDED_DN |
4058 DSDB_SEARCH_SHOW_RECYCLED,
4061 if (ret != LDB_SUCCESS) {
4062 talloc_free(tmp_ctx);
4066 link_msg = link_res->msgs[0];
4067 link_el = ldb_msg_find_element(link_msg,
4068 target_attr->lDAPDisplayName);
4069 if (link_el == NULL) {
4070 talloc_free(tmp_ctx);
4071 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4075 * This call 'upgrades' the links in link_dns, but we
4076 * do not commit the result back into the database, so
4077 * this is safe to call in FL2000 or on databases that
4078 * have been run at that level in the past.
4080 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
4083 target_attr->syntax->ldap_oid,
4085 if (ret != LDB_SUCCESS) {
4086 talloc_free(tmp_ctx);
4090 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4094 target_attr->syntax->ldap_oid, false);
4095 if (ret != LDB_SUCCESS) {
4096 talloc_free(tmp_ctx);
4101 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4102 "Failed to find forward link on %s "
4103 "as %s to remove backlink %s on %s",
4104 ldb_dn_get_linearized(msg->dn),
4105 target_attr->lDAPDisplayName,
4106 sa->lDAPDisplayName,
4107 ldb_dn_get_linearized(dn));
4108 talloc_free(tmp_ctx);
4109 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4113 /* This needs to get the Binary DN, by first searching */
4114 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4117 dn_val = data_blob_string_const(dn_str);
4118 el2->values = &dn_val;
4119 el2->num_values = 1;
4122 * Ensure that we tell the modification to vanish any linked
4123 * attributes (not simply mark them as isDeleted = TRUE)
4125 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4127 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4128 if (ret != LDB_SUCCESS) {
4129 talloc_free(tmp_ctx);
4133 talloc_free(tmp_ctx);
4139 handle update of replication meta data for deletion of objects
4141 This also handles the mapping of delete to a rename operation
4142 to allow deletes to be replicated.
4144 It also handles the incoming deleted objects, to ensure they are
4145 fully deleted here. In that case re_delete is true, and we do not
4146 use this as a signal to change the deleted state, just reinforce it.
4149 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4151 int ret = LDB_ERR_OTHER;
4152 bool retb, disallow_move_on_delete;
4153 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4154 const char *rdn_name;
4155 const struct ldb_val *rdn_value, *new_rdn_value;
4157 struct ldb_context *ldb = ldb_module_get_ctx(module);
4158 const struct dsdb_schema *schema;
4159 struct ldb_message *msg, *old_msg;
4160 struct ldb_message_element *el;
4161 TALLOC_CTX *tmp_ctx;
4162 struct ldb_result *res, *parent_res;
4163 static const char * const preserved_attrs[] = {
4165 * This list MUST be kept in case-insensitive sorted order,
4166 * as we use it in a binary search with ldb_attr_cmp().
4168 * We get this hard-coded list from
4169 * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4173 "distinguishedName",
4174 "dNReferenceUpdate",
4186 "msDS-LastKnownRDN",
4191 "nTSecurityDescriptor",
4196 "proxiedObjectName",
4197 "replPropertyMetaData",
4199 "securityIdentifier",
4207 "userAccountControl",
4213 * DO NOT JUST APPEND TO THIS LIST.
4215 * In case you missed the note at the top, this list is kept
4216 * in case-insensitive sorted order. In the unlikely event you
4217 * need to add an attrbute, please add it in the RIGHT PLACE.
4220 static const char * const all_attrs[] = {
4221 DSDB_SECRET_ATTRIBUTES,
4225 static const struct ldb_val true_val = {
4226 .data = discard_const_p(uint8_t, "TRUE"),
4231 uint32_t dsdb_flags = 0;
4232 struct replmd_private *replmd_private;
4233 enum deletion_state deletion_state, next_deletion_state;
4235 if (ldb_dn_is_special(req->op.del.dn)) {
4236 return ldb_next_request(module, req);
4240 * We have to allow dbcheck to remove an object that
4241 * is beyond repair, and to do so totally. This could
4242 * mean we we can get a partial object from the other
4243 * DC, causing havoc, so dbcheck suggests
4244 * re-replication first. dbcheck sets both DBCHECK
4245 * and RELAX in this situation.
4247 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4248 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4249 /* really, really remove it */
4250 return ldb_next_request(module, req);
4253 tmp_ctx = talloc_new(ldb);
4256 return LDB_ERR_OPERATIONS_ERROR;
4259 schema = dsdb_get_schema(ldb, tmp_ctx);
4261 talloc_free(tmp_ctx);
4262 return LDB_ERR_OPERATIONS_ERROR;
4265 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4267 /* we need the complete msg off disk, so we can work out which
4268 attributes need to be removed */
4269 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4270 DSDB_FLAG_NEXT_MODULE |
4271 DSDB_SEARCH_SHOW_RECYCLED |
4272 DSDB_SEARCH_REVEAL_INTERNALS |
4273 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4274 if (ret != LDB_SUCCESS) {
4275 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4276 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4277 re_delete ? "re-delete" : "delete",
4278 ldb_dn_get_linearized(old_dn),
4279 ldb_errstring(ldb_module_get_ctx(module)));
4280 talloc_free(tmp_ctx);
4283 old_msg = res->msgs[0];
4285 replmd_deletion_state(module, old_msg,
4287 &next_deletion_state);
4289 /* This supports us noticing an incoming isDeleted and acting on it */
4291 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4292 next_deletion_state = deletion_state;
4295 if (next_deletion_state == OBJECT_REMOVED) {
4297 * We have to prevent objects being deleted, even if
4298 * the administrator really wants them gone, as
4299 * without the tombstone, we can get a partial object
4300 * from the other DC, causing havoc.
4302 * The only other valid case is when the 180 day
4303 * timeout has expired, when relax is specified.
4305 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4306 /* it is already deleted - really remove it this time */
4307 talloc_free(tmp_ctx);
4308 return ldb_next_request(module, req);
4311 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4312 "This check is to prevent corruption of the replicated state.",
4313 ldb_dn_get_linearized(old_msg->dn));
4314 return LDB_ERR_UNWILLING_TO_PERFORM;
4317 rdn_name = ldb_dn_get_rdn_name(old_dn);
4318 rdn_value = ldb_dn_get_rdn_val(old_dn);
4319 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4320 talloc_free(tmp_ctx);
4321 return ldb_operr(ldb);
4324 msg = ldb_msg_new(tmp_ctx);
4326 ldb_module_oom(module);
4327 talloc_free(tmp_ctx);
4328 return LDB_ERR_OPERATIONS_ERROR;
4333 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4334 disallow_move_on_delete =
4335 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4336 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4338 /* work out where we will be renaming this object to */
4339 if (!disallow_move_on_delete) {
4340 struct ldb_dn *deleted_objects_dn;
4341 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4342 &deleted_objects_dn);
4345 * We should not move objects if we can't find the
4346 * deleted objects DN. Not moving (or otherwise
4347 * harming) the Deleted Objects DN itself is handled
4350 if (re_delete && (ret != LDB_SUCCESS)) {
4351 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4352 if (new_dn == NULL) {
4353 ldb_module_oom(module);
4354 talloc_free(tmp_ctx);
4355 return LDB_ERR_OPERATIONS_ERROR;
4357 } else if (ret != LDB_SUCCESS) {
4358 /* this is probably an attempted delete on a partition
4359 * that doesn't allow delete operations, such as the
4360 * schema partition */
4361 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4362 ldb_dn_get_linearized(old_dn));
4363 talloc_free(tmp_ctx);
4364 return LDB_ERR_UNWILLING_TO_PERFORM;
4366 new_dn = deleted_objects_dn;
4369 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4370 if (new_dn == NULL) {
4371 ldb_module_oom(module);
4372 talloc_free(tmp_ctx);
4373 return LDB_ERR_OPERATIONS_ERROR;
4377 /* get the objects GUID from the search we just did */
4378 guid = samdb_result_guid(old_msg, "objectGUID");
4380 if (deletion_state == OBJECT_NOT_DELETED) {
4381 struct ldb_message_element *is_deleted_el;
4383 ret = replmd_make_deleted_child_dn(tmp_ctx,
4386 rdn_name, rdn_value,
4389 if (ret != LDB_SUCCESS) {
4390 talloc_free(tmp_ctx);
4394 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4396 if (ret != LDB_SUCCESS) {
4397 ldb_asprintf_errstring(ldb, __location__
4398 ": Failed to add isDeleted string to the msg");
4399 talloc_free(tmp_ctx);
4402 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4405 * No matter what has happened with other renames etc, try again to
4406 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4409 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4410 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4412 ldb_asprintf_errstring(ldb, __location__
4413 ": Unable to add a prepare rdn of %s",
4414 ldb_dn_get_linearized(rdn));
4415 talloc_free(tmp_ctx);
4416 return LDB_ERR_OPERATIONS_ERROR;
4418 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4420 retb = ldb_dn_add_child(new_dn, rdn);
4422 ldb_asprintf_errstring(ldb, __location__
4423 ": Unable to add rdn %s to base dn: %s",
4424 ldb_dn_get_linearized(rdn),
4425 ldb_dn_get_linearized(new_dn));
4426 talloc_free(tmp_ctx);
4427 return LDB_ERR_OPERATIONS_ERROR;
4432 now we need to modify the object in the following ways:
4434 - add isDeleted=TRUE
4435 - update rDN and name, with new rDN
4436 - remove linked attributes
4437 - remove objectCategory and sAMAccountType
4438 - remove attribs not on the preserved list
4439 - preserved if in above list, or is rDN
4440 - remove all linked attribs from this object
4441 - remove all links from other objects to this object
4442 (note we use the backlinks to do this, so we won't find one-way
4443 links that still point to this object, or deactivated two-way
4444 links, i.e. 'member' after the user has been removed from the
4446 - add lastKnownParent
4447 - update replPropertyMetaData?
4449 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4452 if (deletion_state == OBJECT_NOT_DELETED) {
4453 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4454 char *parent_dn_str = NULL;
4455 struct ldb_message_element *p_el;
4457 /* we need the storage form of the parent GUID */
4458 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4460 DSDB_FLAG_NEXT_MODULE |
4461 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4462 DSDB_SEARCH_REVEAL_INTERNALS|
4463 DSDB_SEARCH_SHOW_RECYCLED, req);
4464 if (ret != LDB_SUCCESS) {
4465 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4466 "repmd_delete: Failed to %s %s, "
4467 "because we failed to find it's parent (%s): %s",
4468 re_delete ? "re-delete" : "delete",
4469 ldb_dn_get_linearized(old_dn),
4470 ldb_dn_get_linearized(parent_dn),
4471 ldb_errstring(ldb_module_get_ctx(module)));
4472 talloc_free(tmp_ctx);
4477 * Now we can use the DB version,
4478 * it will have the extended DN info in it
4480 parent_dn = parent_res->msgs[0]->dn;
4481 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4484 if (parent_dn_str == NULL) {
4485 talloc_free(tmp_ctx);
4486 return ldb_module_oom(module);
4489 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4491 if (ret != LDB_SUCCESS) {
4492 ldb_asprintf_errstring(ldb, __location__
4493 ": Failed to add lastKnownParent "
4494 "string when deleting %s",
4495 ldb_dn_get_linearized(old_dn));
4496 talloc_free(tmp_ctx);
4499 p_el = ldb_msg_find_element(msg,
4502 talloc_free(tmp_ctx);
4503 return ldb_module_operr(module);
4505 p_el->flags = LDB_FLAG_MOD_REPLACE;
4507 if (next_deletion_state == OBJECT_DELETED) {
4508 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4509 if (ret != LDB_SUCCESS) {
4510 ldb_asprintf_errstring(ldb, __location__
4511 ": Failed to add msDS-LastKnownRDN "
4512 "string when deleting %s",
4513 ldb_dn_get_linearized(old_dn));
4514 talloc_free(tmp_ctx);
4517 p_el = ldb_msg_find_element(msg,
4518 "msDS-LastKnownRDN");
4520 talloc_free(tmp_ctx);
4521 return ldb_module_operr(module);
4523 p_el->flags = LDB_FLAG_MOD_ADD;
4527 switch (next_deletion_state) {
4529 case OBJECT_RECYCLED:
4530 case OBJECT_TOMBSTONE:
4533 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4534 * describes what must be removed from a tombstone
4537 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4538 * describes what must be removed from a recycled
4544 * we also mark it as recycled, meaning this object can't be
4545 * recovered (we are stripping its attributes).
4546 * This is done only if we have this schema object of course ...
4547 * This behavior is identical to the one of Windows 2008R2 which
4548 * always set the isRecycled attribute, even if the recycle-bin is
4549 * not activated and what ever the forest level is.
4551 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4552 struct ldb_message_element *is_recycled_el;
4554 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4556 if (ret != LDB_SUCCESS) {
4557 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4558 ldb_module_oom(module);
4559 talloc_free(tmp_ctx);
4562 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4565 replmd_private = talloc_get_type(ldb_module_get_private(module),
4566 struct replmd_private);
4567 /* work out which of the old attributes we will be removing */
4568 for (i=0; i<old_msg->num_elements; i++) {
4569 const struct dsdb_attribute *sa;
4570 el = &old_msg->elements[i];
4571 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4573 talloc_free(tmp_ctx);
4574 return LDB_ERR_OPERATIONS_ERROR;
4576 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4577 /* don't remove the rDN */
4581 if (sa->linkID & 1) {
4583 * we have a backlink in this object
4584 * that needs to be removed. We're not
4585 * allowed to remove it directly
4586 * however, so we instead setup a
4587 * modify to delete the corresponding
4590 ret = replmd_delete_remove_link(module, schema,
4594 if (ret == LDB_SUCCESS) {
4596 * now we continue, which means we
4597 * won't remove this backlink
4603 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4604 const char *old_dn_str
4605 = ldb_dn_get_linearized(old_dn);
4606 ldb_asprintf_errstring(ldb,
4608 ": Failed to remove backlink of "
4609 "%s when deleting %s: %s",
4612 ldb_errstring(ldb));
4613 talloc_free(tmp_ctx);
4614 return LDB_ERR_OPERATIONS_ERROR;
4618 * Otherwise vanish the link, we are
4619 * out of sync and the controlling
4620 * object does not have the source
4624 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4626 } else if (sa->linkID == 0) {
4627 const char * const *attr = NULL;
4628 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4631 BINARY_ARRAY_SEARCH_V(preserved_attrs,
4632 ARRAY_SIZE(preserved_attrs),
4641 * Ensure that we tell the modification to vanish any linked
4642 * attributes (not simply mark them as isDeleted = TRUE)
4644 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4646 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4647 if (ret != LDB_SUCCESS) {
4648 talloc_free(tmp_ctx);
4649 ldb_module_oom(module);
4656 case OBJECT_DELETED:
4658 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4659 * describes what must be removed from a deleted
4663 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4664 if (ret != LDB_SUCCESS) {
4665 talloc_free(tmp_ctx);
4666 ldb_module_oom(module);
4670 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4671 if (ret != LDB_SUCCESS) {
4672 talloc_free(tmp_ctx);
4673 ldb_module_oom(module);
4683 if (deletion_state == OBJECT_NOT_DELETED) {
4684 const struct dsdb_attribute *sa;
4686 /* work out what the new rdn value is, for updating the
4687 rDN and name fields */
4688 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4689 if (new_rdn_value == NULL) {
4690 talloc_free(tmp_ctx);
4691 return ldb_operr(ldb);
4694 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4696 talloc_free(tmp_ctx);
4697 return LDB_ERR_OPERATIONS_ERROR;
4700 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4702 if (ret != LDB_SUCCESS) {
4703 talloc_free(tmp_ctx);
4706 el->flags = LDB_FLAG_MOD_REPLACE;
4708 el = ldb_msg_find_element(old_msg, "name");
4710 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4711 if (ret != LDB_SUCCESS) {
4712 talloc_free(tmp_ctx);
4715 el->flags = LDB_FLAG_MOD_REPLACE;
4720 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4725 * No matter what has happned with other renames, try again to
4726 * get this to be under the deleted DN.
4728 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4729 /* now rename onto the new DN */
4730 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4731 if (ret != LDB_SUCCESS){
4732 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4733 ldb_dn_get_linearized(old_dn),
4734 ldb_dn_get_linearized(new_dn),
4735 ldb_errstring(ldb)));
4736 talloc_free(tmp_ctx);
4742 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4743 if (ret != LDB_SUCCESS) {
4744 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4745 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4746 talloc_free(tmp_ctx);
4750 talloc_free(tmp_ctx);
4752 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4755 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4757 return replmd_delete_internals(module, req, false);
4761 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4766 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4768 int ret = LDB_ERR_OTHER;
4769 /* TODO: do some error mapping */
4771 /* Let the caller know the full WERROR */
4772 ar->objs->error = status;
4778 static struct replPropertyMetaData1 *
4779 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4780 enum drsuapi_DsAttributeId attid)
4783 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4785 for (i = 0; i < rpmd_ctr->count; i++) {
4786 if (rpmd_ctr->array[i].attid == attid) {
4787 return &rpmd_ctr->array[i];
4795 return true if an update is newer than an existing entry
4796 see section 5.11 of MS-ADTS
4798 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4799 const struct GUID *update_invocation_id,
4800 uint32_t current_version,
4801 uint32_t update_version,
4802 NTTIME current_change_time,
4803 NTTIME update_change_time)
4805 if (update_version != current_version) {
4806 return update_version > current_version;
4808 if (update_change_time != current_change_time) {
4809 return update_change_time > current_change_time;
4811 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4814 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4815 struct replPropertyMetaData1 *new_m)
4817 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4818 &new_m->originating_invocation_id,
4821 cur_m->originating_change_time,
4822 new_m->originating_change_time);
4825 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4826 struct replPropertyMetaData1 *cur_m,
4827 struct replPropertyMetaData1 *new_m)
4832 * If the new replPropertyMetaData entry for this attribute is
4833 * not provided (this happens in the case where we look for
4834 * ATTID_name, but the name was not changed), then the local
4835 * state is clearly still current, as the remote
4836 * server didn't send it due to being older the high watermark
4839 if (new_m == NULL) {
4843 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4845 * if we compare equal then do an
4846 * update. This is used when a client
4847 * asks for a FULL_SYNC, and can be
4848 * used to recover a corrupt
4851 * This call is a bit tricky, what we
4852 * are doing it turning the 'is_newer'
4853 * call into a 'not is older' by
4854 * swapping cur_m and new_m, and negating the
4857 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4860 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4868 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4870 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4871 struct ldb_context *ldb,
4873 const char *four_char_prefix,
4874 const char *rdn_name,
4875 const struct ldb_val *rdn_value,
4878 struct ldb_val deleted_child_rdn_val;
4879 struct GUID_txt_buf guid_str;
4883 GUID_buf_string(&guid, &guid_str);
4885 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4887 ldb_asprintf_errstring(ldb, __location__
4888 ": Unable to add a formatted child to dn: %s",
4889 ldb_dn_get_linearized(dn));
4890 return LDB_ERR_OPERATIONS_ERROR;
4894 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4895 * we should truncate this value to ensure the RDN is not more than 255 chars.
4897 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4899 * "Naming constraints are not enforced for replicated
4900 * updates." so this is safe and we don't have to work out not
4901 * splitting a UTF8 char right now.
4903 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4906 * sizeof(guid_str.buf) will always be longer than
4907 * strlen(guid_str.buf) but we allocate using this and
4908 * waste the trailing bytes to avoid scaring folks
4909 * with memcpy() using strlen() below
4912 deleted_child_rdn_val.data
4913 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4915 rdn_value->length + 5
4916 + sizeof(guid_str.buf));
4917 if (!deleted_child_rdn_val.data) {
4918 ldb_asprintf_errstring(ldb, __location__
4919 ": Unable to add a formatted child to dn: %s",
4920 ldb_dn_get_linearized(dn));
4921 return LDB_ERR_OPERATIONS_ERROR;
4924 deleted_child_rdn_val.length =
4925 rdn_value->length + 5
4926 + strlen(guid_str.buf);
4928 SMB_ASSERT(deleted_child_rdn_val.length <
4929 talloc_get_size(deleted_child_rdn_val.data));
4932 * talloc won't allocate more than 256MB so we can't
4933 * overflow but just to be sure
4935 if (deleted_child_rdn_val.length < rdn_value->length) {
4936 return LDB_ERR_OPERATIONS_ERROR;
4939 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4940 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4941 four_char_prefix, 4);
4942 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4944 sizeof(guid_str.buf));
4946 /* Now set the value into the RDN, without parsing it */
4947 ret = ldb_dn_set_component(
4951 deleted_child_rdn_val);
4960 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4961 struct ldb_context *ldb,
4965 const struct ldb_val *rdn_val;
4966 const char *rdn_name;
4967 struct ldb_dn *new_dn;
4970 rdn_val = ldb_dn_get_rdn_val(dn);
4971 rdn_name = ldb_dn_get_rdn_name(dn);
4972 if (!rdn_val || !rdn_name) {
4976 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4981 ret = replmd_make_prefix_child_dn(mem_ctx,
4987 if (ret != LDB_SUCCESS) {
4996 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4997 struct ldb_context *ldb,
4999 const char *rdn_name,
5000 const struct ldb_val *rdn_value,
5003 return replmd_make_prefix_child_dn(tmp_ctx,
5013 perform a modify operation which sets the rDN and name attributes to
5014 their current values. This has the effect of changing these
5015 attributes to have been last updated by the current DC. This is
5016 needed to ensure that renames performed as part of conflict
5017 resolution are propagated to other DCs
5019 static int replmd_name_modify(struct replmd_replicated_request *ar,
5020 struct ldb_request *req, struct ldb_dn *dn)
5022 struct ldb_message *msg;
5023 const char *rdn_name;
5024 const struct ldb_val *rdn_val;
5025 const struct dsdb_attribute *rdn_attr;
5028 msg = ldb_msg_new(req);
5034 rdn_name = ldb_dn_get_rdn_name(dn);
5035 if (rdn_name == NULL) {
5039 /* normalize the rdn attribute name */
5040 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5041 if (rdn_attr == NULL) {
5044 rdn_name = rdn_attr->lDAPDisplayName;
5046 rdn_val = ldb_dn_get_rdn_val(dn);
5047 if (rdn_val == NULL) {
5051 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5054 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
5057 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5060 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
5065 * We have to mark this as a replicated update otherwise
5066 * schema_data may reject a rename in the schema partition
5069 ret = dsdb_module_modify(ar->module, msg,
5070 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5072 if (ret != LDB_SUCCESS) {
5073 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5074 ldb_dn_get_linearized(dn),
5075 ldb_errstring(ldb_module_get_ctx(ar->module))));
5085 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5086 ldb_dn_get_linearized(dn)));
5087 return LDB_ERR_OPERATIONS_ERROR;
5092 callback for conflict DN handling where we have renamed the incoming
5093 record. After renaming it, we need to ensure the change of name and
5094 rDN for the incoming record is seen as an originating update by this DC.
5096 This also handles updating lastKnownParent for entries sent to lostAndFound
5098 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5100 struct replmd_replicated_request *ar =
5101 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5102 struct ldb_dn *conflict_dn = NULL;
5105 if (ares->error != LDB_SUCCESS) {
5106 /* call the normal callback for everything except success */
5107 return replmd_op_callback(req, ares);
5110 switch (req->operation) {
5112 conflict_dn = req->op.add.message->dn;
5115 conflict_dn = req->op.mod.message->dn;
5118 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5121 /* perform a modify of the rDN and name of the record */
5122 ret = replmd_name_modify(ar, req, conflict_dn);
5123 if (ret != LDB_SUCCESS) {
5125 return replmd_op_callback(req, ares);
5128 if (ar->objs->objects[ar->index_current].last_known_parent) {
5129 struct ldb_message *msg = ldb_msg_new(req);
5131 ldb_module_oom(ar->module);
5132 return LDB_ERR_OPERATIONS_ERROR;
5135 msg->dn = req->op.add.message->dn;
5137 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5138 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5139 if (ret != LDB_SUCCESS) {
5140 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5141 ldb_module_oom(ar->module);
5144 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5146 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5147 if (ret != LDB_SUCCESS) {
5148 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5149 ldb_dn_get_linearized(msg->dn),
5150 ldb_errstring(ldb_module_get_ctx(ar->module))));
5156 return replmd_op_callback(req, ares);
5160 callback for replmd_replicated_apply_add()
5161 This copes with the creation of conflict records in the case where
5162 the DN exists, but with a different objectGUID
5164 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))
5166 struct ldb_dn *conflict_dn;
5167 struct replmd_replicated_request *ar =
5168 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5169 struct ldb_result *res;
5170 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5172 const struct ldb_val *omd_value;
5173 struct replPropertyMetaDataBlob omd, *rmd;
5174 enum ndr_err_code ndr_err;
5175 bool rename_incoming_record, rodc;
5176 struct replPropertyMetaData1 *rmd_name, *omd_name;
5177 struct ldb_message *msg;
5178 struct ldb_request *down_req = NULL;
5180 /* call the normal callback for success */
5181 if (ares->error == LDB_SUCCESS) {
5182 return callback(req, ares);
5186 * we have a conflict, and need to decide if we will keep the
5187 * new record or the old record
5190 msg = ar->objs->objects[ar->index_current].msg;
5191 conflict_dn = msg->dn;
5193 /* For failures other than conflicts, fail the whole operation here */
5194 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5195 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5196 ldb_dn_get_linearized(conflict_dn),
5197 ldb_errstring(ldb_module_get_ctx(ar->module)));
5199 return ldb_module_done(ar->req, NULL, NULL,
5200 LDB_ERR_OPERATIONS_ERROR);
5203 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5204 if (ret != LDB_SUCCESS) {
5205 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to determine if we are an RODC when attempting to form conflict DN: %s", ldb_errstring(ldb_module_get_ctx(ar->module)));
5206 return ldb_module_done(ar->req, NULL, NULL,
5207 LDB_ERR_OPERATIONS_ERROR);
5213 * We are on an RODC, or were a GC for this
5214 * partition, so we have to fail this until
5215 * someone who owns the partition sorts it
5218 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5219 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5220 " - We must fail the operation until a master for this partition resolves the conflict",
5221 ldb_dn_get_linearized(conflict_dn));
5222 ret = LDB_ERR_OPERATIONS_ERROR;
5227 * first we need the replPropertyMetaData attribute from the
5228 * local, conflicting record
5230 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5232 DSDB_FLAG_NEXT_MODULE |
5233 DSDB_SEARCH_SHOW_DELETED |
5234 DSDB_SEARCH_SHOW_RECYCLED, req);
5235 if (ret != LDB_SUCCESS) {
5236 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5237 ldb_dn_get_linearized(conflict_dn)));
5241 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5242 if (omd_value == NULL) {
5243 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5244 ldb_dn_get_linearized(conflict_dn)));
5248 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5249 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5250 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5251 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5252 ldb_dn_get_linearized(conflict_dn)));
5256 rmd = ar->objs->objects[ar->index_current].meta_data;
5259 * we decide which is newer based on the RPMD on the name
5260 * attribute. See [MS-DRSR] ResolveNameConflict.
5262 * We expect omd_name to be present, as this is from a local
5263 * search, but while rmd_name should have been given to us by
5264 * the remote server, if it is missing we just prefer the
5266 * replmd_replPropertyMetaData1_new_should_be_taken()
5268 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5269 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5271 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5272 ldb_dn_get_linearized(conflict_dn)));
5277 * Should we preserve the current record, and so rename the
5278 * incoming record to be a conflict?
5280 rename_incoming_record
5281 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5282 omd_name, rmd_name);
5284 if (rename_incoming_record) {
5286 struct ldb_dn *new_dn;
5288 guid = samdb_result_guid(msg, "objectGUID");
5289 if (GUID_all_zero(&guid)) {
5290 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5291 ldb_dn_get_linearized(conflict_dn)));
5294 new_dn = replmd_conflict_dn(req,
5295 ldb_module_get_ctx(ar->module),
5296 conflict_dn, &guid);
5297 if (new_dn == NULL) {
5298 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5299 ldb_dn_get_linearized(conflict_dn)));
5303 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5304 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5306 /* re-submit the request, but with the new DN */
5307 callback = replmd_op_name_modify_callback;
5310 /* we are renaming the existing record */
5312 struct ldb_dn *new_dn;
5314 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5315 if (GUID_all_zero(&guid)) {
5316 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5317 ldb_dn_get_linearized(conflict_dn)));
5321 new_dn = replmd_conflict_dn(req,
5322 ldb_module_get_ctx(ar->module),
5323 conflict_dn, &guid);
5324 if (new_dn == NULL) {
5325 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5326 ldb_dn_get_linearized(conflict_dn)));
5330 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5331 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5333 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5334 DSDB_FLAG_OWN_MODULE, req);
5335 if (ret != LDB_SUCCESS) {
5336 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5337 ldb_dn_get_linearized(conflict_dn),
5338 ldb_dn_get_linearized(new_dn),
5339 ldb_errstring(ldb_module_get_ctx(ar->module))));
5344 * now we need to ensure that the rename is seen as an
5345 * originating update. We do that with a modify.
5347 ret = replmd_name_modify(ar, req, new_dn);
5348 if (ret != LDB_SUCCESS) {
5352 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5353 ldb_dn_get_linearized(req->op.add.message->dn)));
5356 ret = ldb_build_add_req(&down_req,
5357 ldb_module_get_ctx(ar->module),
5364 if (ret != LDB_SUCCESS) {
5367 LDB_REQ_SET_LOCATION(down_req);
5369 /* current partition control needed by "repmd_op_callback" */
5370 ret = ldb_request_add_control(down_req,
5371 DSDB_CONTROL_CURRENT_PARTITION_OID,
5373 if (ret != LDB_SUCCESS) {
5374 return replmd_replicated_request_error(ar, ret);
5377 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5378 /* this tells the partition module to make it a
5379 partial replica if creating an NC */
5380 ret = ldb_request_add_control(down_req,
5381 DSDB_CONTROL_PARTIAL_REPLICA,
5383 if (ret != LDB_SUCCESS) {
5384 return replmd_replicated_request_error(ar, ret);
5389 * Finally we re-run the add, otherwise the new record won't
5390 * exist, as we are here because of that exact failure!
5392 return ldb_next_request(ar->module, down_req);
5395 /* on failure make the caller get the error. This means
5396 * replication will stop with an error, but there is not much
5399 if (ret == LDB_SUCCESS) {
5400 ret = LDB_ERR_OPERATIONS_ERROR;
5402 return ldb_module_done(ar->req, NULL, NULL,
5407 callback for replmd_replicated_apply_add()
5408 This copes with the creation of conflict records in the case where
5409 the DN exists, but with a different objectGUID
5411 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5413 struct replmd_replicated_request *ar =
5414 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5416 if (ar->objs->objects[ar->index_current].last_known_parent) {
5417 /* This is like a conflict DN, where we put the object in LostAndFound
5418 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5419 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5422 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5426 this is called when a new object comes in over DRS
5428 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5430 struct ldb_context *ldb;
5431 struct ldb_request *change_req;
5432 enum ndr_err_code ndr_err;
5433 struct ldb_message *msg;
5434 struct replPropertyMetaDataBlob *md;
5435 struct ldb_val md_value;
5438 bool remote_isDeleted = false;
5441 time_t t = time(NULL);
5442 const struct ldb_val *rdn_val;
5443 struct replmd_private *replmd_private =
5444 talloc_get_type(ldb_module_get_private(ar->module),
5445 struct replmd_private);
5446 unix_to_nt_time(&now, t);
5448 ldb = ldb_module_get_ctx(ar->module);
5449 msg = ar->objs->objects[ar->index_current].msg;
5450 md = ar->objs->objects[ar->index_current].meta_data;
5451 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5453 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5454 if (ret != LDB_SUCCESS) {
5455 return replmd_replicated_request_error(ar, ret);
5458 ret = dsdb_msg_add_guid(msg,
5459 &ar->objs->objects[ar->index_current].object_guid,
5461 if (ret != LDB_SUCCESS) {
5462 return replmd_replicated_request_error(ar, ret);
5465 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5466 if (ret != LDB_SUCCESS) {
5467 return replmd_replicated_request_error(ar, ret);
5470 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5471 if (ret != LDB_SUCCESS) {
5472 return replmd_replicated_request_error(ar, ret);
5475 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5476 if (ret != LDB_SUCCESS) {
5477 return replmd_replicated_request_error(ar, ret);
5480 /* remove any message elements that have zero values */
5481 for (i=0; i<msg->num_elements; i++) {
5482 struct ldb_message_element *el = &msg->elements[i];
5484 if (el->num_values == 0) {
5485 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5486 ldb_asprintf_errstring(ldb, __location__
5487 ": empty objectClass sent on %s, aborting replication\n",
5488 ldb_dn_get_linearized(msg->dn));
5489 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5492 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5494 ldb_msg_remove_element(msg, &msg->elements[i]);
5501 struct GUID_txt_buf guid_txt;
5503 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5506 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5507 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5510 } else if (DEBUGLVL(4)) {
5511 struct GUID_txt_buf guid_txt;
5512 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5513 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5514 ldb_dn_get_linearized(msg->dn)));
5516 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5517 "isDeleted", false);
5520 * the meta data array is already sorted by the caller, except
5521 * for the RDN, which needs to be added.
5525 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5526 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5527 md, ar, now, is_schema_nc,
5529 if (ret != LDB_SUCCESS) {
5530 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5531 return replmd_replicated_request_error(ar, ret);
5534 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5535 if (ret != LDB_SUCCESS) {
5536 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5537 return replmd_replicated_request_error(ar, ret);
5540 for (i=0; i < md->ctr.ctr1.count; i++) {
5541 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5543 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5544 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5545 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5546 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5547 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5549 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5550 if (ret != LDB_SUCCESS) {
5551 return replmd_replicated_request_error(ar, ret);
5554 replmd_ldb_message_sort(msg, ar->schema);
5556 if (!remote_isDeleted) {
5557 ret = dsdb_module_schedule_sd_propagation(ar->module,
5558 ar->objs->partition_dn,
5560 if (ret != LDB_SUCCESS) {
5561 return replmd_replicated_request_error(ar, ret);
5565 ar->isDeleted = remote_isDeleted;
5567 ret = ldb_build_add_req(&change_req,
5573 replmd_op_add_callback,
5575 LDB_REQ_SET_LOCATION(change_req);
5576 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5578 /* current partition control needed by "repmd_op_callback" */
5579 ret = ldb_request_add_control(change_req,
5580 DSDB_CONTROL_CURRENT_PARTITION_OID,
5582 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5584 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5585 /* this tells the partition module to make it a
5586 partial replica if creating an NC */
5587 ret = ldb_request_add_control(change_req,
5588 DSDB_CONTROL_PARTIAL_REPLICA,
5590 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5593 return ldb_next_request(ar->module, change_req);
5596 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5597 struct ldb_reply *ares)
5599 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5600 struct replmd_replicated_request);
5604 return ldb_module_done(ar->req, NULL, NULL,
5605 LDB_ERR_OPERATIONS_ERROR);
5609 * The error NO_SUCH_OBJECT is not expected, unless the search
5610 * base is the partition DN, and that case doesn't happen here
5611 * because then we wouldn't get a parent_guid_value in any
5614 if (ares->error != LDB_SUCCESS) {
5615 return ldb_module_done(ar->req, ares->controls,
5616 ares->response, ares->error);
5619 switch (ares->type) {
5620 case LDB_REPLY_ENTRY:
5622 struct ldb_message *parent_msg = ares->message;
5623 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5624 struct ldb_dn *parent_dn = NULL;
5627 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5628 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5629 /* Per MS-DRSR 4.1.10.6.10
5630 * FindBestParentObject we need to move this
5631 * new object under a deleted object to
5633 struct ldb_dn *nc_root;
5635 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5636 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5637 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5638 "No suitable NC root found for %s. "
5639 "We need to move this object because parent object %s "
5640 "is deleted, but this object is not.",
5641 ldb_dn_get_linearized(msg->dn),
5642 ldb_dn_get_linearized(parent_msg->dn));
5643 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5644 } else if (ret != LDB_SUCCESS) {
5645 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5646 "Unable to find NC root for %s: %s. "
5647 "We need to move this object because parent object %s "
5648 "is deleted, but this object is not.",
5649 ldb_dn_get_linearized(msg->dn),
5650 ldb_errstring(ldb_module_get_ctx(ar->module)),
5651 ldb_dn_get_linearized(parent_msg->dn));
5652 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5655 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5657 DS_GUID_LOSTANDFOUND_CONTAINER,
5659 if (ret != LDB_SUCCESS) {
5660 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5661 "Unable to find LostAndFound Container for %s "
5662 "in partition %s: %s. "
5663 "We need to move this object because parent object %s "
5664 "is deleted, but this object is not.",
5665 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5666 ldb_errstring(ldb_module_get_ctx(ar->module)),
5667 ldb_dn_get_linearized(parent_msg->dn));
5668 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5670 ar->objs->objects[ar->index_current].last_known_parent
5671 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5675 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5678 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5680 comp_num = ldb_dn_get_comp_num(msg->dn);
5682 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5684 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5687 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5689 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5693 case LDB_REPLY_REFERRAL:
5694 /* we ignore referrals */
5697 case LDB_REPLY_DONE:
5699 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5700 struct GUID_txt_buf str_buf;
5701 if (ar->search_msg != NULL) {
5702 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5703 "No parent with GUID %s found for object locally known as %s",
5704 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5705 ldb_dn_get_linearized(ar->search_msg->dn));
5707 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5708 "No parent with GUID %s found for object remotely known as %s",
5709 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5710 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5714 * This error code is really important, as it
5715 * is the flag back to the callers to retry
5716 * this with DRSUAPI_DRS_GET_ANC, and so get
5717 * the parent objects before the child
5720 return ldb_module_done(ar->req, NULL, NULL,
5721 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5724 if (ar->search_msg != NULL) {
5725 ret = replmd_replicated_apply_merge(ar);
5727 ret = replmd_replicated_apply_add(ar);
5729 if (ret != LDB_SUCCESS) {
5730 return ldb_module_done(ar->req, NULL, NULL, ret);
5739 * Look for the parent object, so we put the new object in the right
5740 * place This is akin to NameObject in MS-DRSR - this routine and the
5741 * callbacks find the right parent name, and correct name for this
5745 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5747 struct ldb_context *ldb;
5751 struct ldb_request *search_req;
5752 static const char *attrs[] = {"isDeleted", NULL};
5753 struct GUID_txt_buf guid_str_buf;
5755 ldb = ldb_module_get_ctx(ar->module);
5757 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5758 if (ar->search_msg != NULL) {
5759 return replmd_replicated_apply_merge(ar);
5761 return replmd_replicated_apply_add(ar);
5765 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5768 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5769 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5771 ret = ldb_build_search_req(&search_req,
5774 ar->objs->partition_dn,
5780 replmd_replicated_apply_search_for_parent_callback,
5782 LDB_REQ_SET_LOCATION(search_req);
5784 ret = dsdb_request_add_controls(search_req,
5785 DSDB_SEARCH_SHOW_RECYCLED|
5786 DSDB_SEARCH_SHOW_DELETED|
5787 DSDB_SEARCH_SHOW_EXTENDED_DN);
5788 if (ret != LDB_SUCCESS) {
5792 return ldb_next_request(ar->module, search_req);
5796 handle renames that come in over DRS replication
5798 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5799 struct ldb_message *msg,
5800 struct ldb_request *parent,
5804 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5805 struct ldb_result *res;
5806 struct ldb_dn *conflict_dn;
5807 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5808 const struct ldb_val *omd_value;
5809 struct replPropertyMetaDataBlob omd, *rmd;
5810 enum ndr_err_code ndr_err;
5811 bool rename_incoming_record, rodc;
5812 struct replPropertyMetaData1 *rmd_name, *omd_name;
5813 struct ldb_dn *new_dn;
5816 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5817 ldb_dn_get_linearized(ar->search_msg->dn),
5818 ldb_dn_get_linearized(msg->dn)));
5821 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5822 DSDB_FLAG_NEXT_MODULE, ar->req);
5823 if (ret == LDB_SUCCESS) {
5824 talloc_free(tmp_ctx);
5829 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5830 talloc_free(tmp_ctx);
5831 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5832 ldb_dn_get_linearized(ar->search_msg->dn),
5833 ldb_dn_get_linearized(msg->dn),
5834 ldb_errstring(ldb_module_get_ctx(ar->module)));
5838 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5839 if (ret != LDB_SUCCESS) {
5840 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5841 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5842 ldb_errstring(ldb_module_get_ctx(ar->module)));
5843 return LDB_ERR_OPERATIONS_ERROR;
5846 * we have a conflict, and need to decide if we will keep the
5847 * new record or the old record
5850 conflict_dn = msg->dn;
5854 * We are on an RODC, or were a GC for this
5855 * partition, so we have to fail this until
5856 * someone who owns the partition sorts it
5859 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5860 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5861 " - We must fail the operation until a master for this partition resolves the conflict",
5862 ldb_dn_get_linearized(conflict_dn));
5863 ret = LDB_ERR_OPERATIONS_ERROR;
5868 * first we need the replPropertyMetaData attribute from the
5871 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5873 DSDB_FLAG_NEXT_MODULE |
5874 DSDB_SEARCH_SHOW_DELETED |
5875 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5876 if (ret != LDB_SUCCESS) {
5877 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5878 ldb_dn_get_linearized(conflict_dn)));
5882 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5883 if (omd_value == NULL) {
5884 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5885 ldb_dn_get_linearized(conflict_dn)));
5889 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5890 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5891 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5892 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5893 ldb_dn_get_linearized(conflict_dn)));
5897 rmd = ar->objs->objects[ar->index_current].meta_data;
5900 * we decide which is newer based on the RPMD on the name
5901 * attribute. See [MS-DRSR] ResolveNameConflict.
5903 * We expect omd_name to be present, as this is from a local
5904 * search, but while rmd_name should have been given to us by
5905 * the remote server, if it is missing we just prefer the
5907 * replmd_replPropertyMetaData1_new_should_be_taken()
5909 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5910 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5912 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5913 ldb_dn_get_linearized(conflict_dn)));
5918 * Should we preserve the current record, and so rename the
5919 * incoming record to be a conflict?
5921 rename_incoming_record =
5922 !replmd_replPropertyMetaData1_new_should_be_taken(
5923 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5924 omd_name, rmd_name);
5926 if (rename_incoming_record) {
5928 new_dn = replmd_conflict_dn(msg,
5929 ldb_module_get_ctx(ar->module),
5931 &ar->objs->objects[ar->index_current].object_guid);
5932 if (new_dn == NULL) {
5933 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5934 "Failed to form conflict DN for %s\n",
5935 ldb_dn_get_linearized(msg->dn));
5937 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5940 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5941 DSDB_FLAG_NEXT_MODULE, ar->req);
5942 if (ret != LDB_SUCCESS) {
5943 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5944 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5945 ldb_dn_get_linearized(conflict_dn),
5946 ldb_dn_get_linearized(ar->search_msg->dn),
5947 ldb_dn_get_linearized(new_dn),
5948 ldb_errstring(ldb_module_get_ctx(ar->module)));
5949 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5957 /* we are renaming the existing record */
5959 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5960 if (GUID_all_zero(&guid)) {
5961 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5962 ldb_dn_get_linearized(conflict_dn)));
5966 new_dn = replmd_conflict_dn(tmp_ctx,
5967 ldb_module_get_ctx(ar->module),
5968 conflict_dn, &guid);
5969 if (new_dn == NULL) {
5970 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5971 ldb_dn_get_linearized(conflict_dn)));
5975 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5976 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5978 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5979 DSDB_FLAG_OWN_MODULE, ar->req);
5980 if (ret != LDB_SUCCESS) {
5981 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5982 ldb_dn_get_linearized(conflict_dn),
5983 ldb_dn_get_linearized(new_dn),
5984 ldb_errstring(ldb_module_get_ctx(ar->module))));
5989 * now we need to ensure that the rename is seen as an
5990 * originating update. We do that with a modify.
5992 ret = replmd_name_modify(ar, ar->req, new_dn);
5993 if (ret != LDB_SUCCESS) {
5997 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5998 ldb_dn_get_linearized(ar->search_msg->dn),
5999 ldb_dn_get_linearized(msg->dn)));
6002 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6003 DSDB_FLAG_NEXT_MODULE, ar->req);
6004 if (ret != LDB_SUCCESS) {
6005 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6006 ldb_dn_get_linearized(ar->search_msg->dn),
6007 ldb_dn_get_linearized(msg->dn),
6008 ldb_errstring(ldb_module_get_ctx(ar->module))));
6012 talloc_free(tmp_ctx);
6016 * On failure make the caller get the error
6017 * This means replication will stop with an error,
6018 * but there is not much else we can do. In the
6019 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6022 if (ret == LDB_SUCCESS) {
6023 ret = LDB_ERR_OPERATIONS_ERROR;
6026 talloc_free(tmp_ctx);
6031 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6033 struct ldb_context *ldb;
6034 struct ldb_request *change_req;
6035 enum ndr_err_code ndr_err;
6036 struct ldb_message *msg;
6037 struct replPropertyMetaDataBlob *rmd;
6038 struct replPropertyMetaDataBlob omd;
6039 const struct ldb_val *omd_value;
6040 struct replPropertyMetaDataBlob nmd;
6041 struct ldb_val nmd_value;
6042 struct GUID remote_parent_guid;
6045 unsigned int removed_attrs = 0;
6047 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6048 bool isDeleted = false;
6049 bool local_isDeleted = false;
6050 bool remote_isDeleted = false;
6051 bool take_remote_isDeleted = false;
6052 bool sd_updated = false;
6053 bool renamed = false;
6054 bool is_schema_nc = false;
6056 const struct ldb_val *old_rdn, *new_rdn;
6057 struct replmd_private *replmd_private =
6058 talloc_get_type(ldb_module_get_private(ar->module),
6059 struct replmd_private);
6061 time_t t = time(NULL);
6062 unix_to_nt_time(&now, t);
6064 ldb = ldb_module_get_ctx(ar->module);
6065 msg = ar->objs->objects[ar->index_current].msg;
6067 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6069 rmd = ar->objs->objects[ar->index_current].meta_data;
6073 /* find existing meta data */
6074 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6076 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6077 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6078 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6079 nt_status = ndr_map_error2ntstatus(ndr_err);
6080 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6083 if (omd.version != 1) {
6084 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6089 struct GUID_txt_buf guid_txt;
6091 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6092 LDB_CHANGETYPE_MODIFY, msg);
6093 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6096 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6098 ndr_print_struct_string(s,
6099 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6100 "existing replPropertyMetaData",
6102 ndr_print_struct_string(s,
6103 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6104 "incoming replPropertyMetaData",
6107 } else if (DEBUGLVL(4)) {
6108 struct GUID_txt_buf guid_txt;
6110 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6111 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6113 ldb_dn_get_linearized(msg->dn)));
6116 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6117 "isDeleted", false);
6118 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6119 "isDeleted", false);
6122 * Fill in the remote_parent_guid with the GUID or an all-zero
6125 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6126 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6128 remote_parent_guid = GUID_zero();
6132 * To ensure we follow a complex rename chain around, we have
6133 * to confirm that the DN is the same (mostly to confirm the
6134 * RDN) and the parentGUID is the same.
6136 * This ensures we keep things under the correct parent, which
6137 * replmd_replicated_handle_rename() will do.
6140 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6141 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6145 * handle renames, even just by case that come in over
6146 * DRS. Changes in the parent DN don't hit us here,
6147 * because the search for a parent will clean up those
6150 * We also have already filtered out the case where
6151 * the peer has an older name to what we have (see
6152 * replmd_replicated_apply_search_callback())
6154 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6157 if (ret != LDB_SUCCESS) {
6158 ldb_debug(ldb, LDB_DEBUG_FATAL,
6159 "replmd_replicated_request rename %s => %s failed - %s\n",
6160 ldb_dn_get_linearized(ar->search_msg->dn),
6161 ldb_dn_get_linearized(msg->dn),
6162 ldb_errstring(ldb));
6163 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6166 if (renamed == true) {
6168 * Set the callback to one that will fix up the name
6169 * metadata on the new conflict DN
6171 callback = replmd_op_name_modify_callback;
6176 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6177 nmd.ctr.ctr1.array = talloc_array(ar,
6178 struct replPropertyMetaData1,
6179 nmd.ctr.ctr1.count);
6180 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6182 /* first copy the old meta data */
6183 for (i=0; i < omd.ctr.ctr1.count; i++) {
6184 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6189 /* now merge in the new meta data */
6190 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6193 for (j=0; j < ni; j++) {
6196 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6200 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6201 ar->objs->dsdb_repl_flags,
6202 &nmd.ctr.ctr1.array[j],
6203 &rmd->ctr.ctr1.array[i]);
6205 /* replace the entry */
6206 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6207 if (ar->seq_num == 0) {
6208 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6209 if (ret != LDB_SUCCESS) {
6210 return replmd_replicated_request_error(ar, ret);
6213 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6214 switch (nmd.ctr.ctr1.array[j].attid) {
6215 case DRSUAPI_ATTID_ntSecurityDescriptor:
6218 case DRSUAPI_ATTID_isDeleted:
6219 take_remote_isDeleted = true;
6228 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6229 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6230 msg->elements[i-removed_attrs].name,
6231 ldb_dn_get_linearized(msg->dn),
6232 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6235 /* we don't want to apply this change so remove the attribute */
6236 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6243 if (found) continue;
6245 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6246 if (ar->seq_num == 0) {
6247 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6248 if (ret != LDB_SUCCESS) {
6249 return replmd_replicated_request_error(ar, ret);
6252 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6253 switch (nmd.ctr.ctr1.array[ni].attid) {
6254 case DRSUAPI_ATTID_ntSecurityDescriptor:
6257 case DRSUAPI_ATTID_isDeleted:
6258 take_remote_isDeleted = true;
6267 * finally correct the size of the meta_data array
6269 nmd.ctr.ctr1.count = ni;
6271 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6272 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6275 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6276 &nmd, ar, now, is_schema_nc,
6278 if (ret != LDB_SUCCESS) {
6279 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6280 return replmd_replicated_request_error(ar, ret);
6284 * sort the new meta data array
6286 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6287 if (ret != LDB_SUCCESS) {
6288 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6293 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6296 * This also controls SD propagation below
6298 if (take_remote_isDeleted) {
6299 isDeleted = remote_isDeleted;
6301 isDeleted = local_isDeleted;
6304 ar->isDeleted = isDeleted;
6307 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6309 if (msg->num_elements == 0) {
6310 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6313 return replmd_replicated_apply_isDeleted(ar);
6316 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6317 ar->index_current, msg->num_elements);
6323 if (sd_updated && !isDeleted) {
6324 ret = dsdb_module_schedule_sd_propagation(ar->module,
6325 ar->objs->partition_dn,
6327 if (ret != LDB_SUCCESS) {
6328 return ldb_operr(ldb);
6332 /* create the meta data value */
6333 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6334 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6335 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6336 nt_status = ndr_map_error2ntstatus(ndr_err);
6337 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6341 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6342 * and replPopertyMetaData attributes
6344 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6345 if (ret != LDB_SUCCESS) {
6346 return replmd_replicated_request_error(ar, ret);
6348 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6349 if (ret != LDB_SUCCESS) {
6350 return replmd_replicated_request_error(ar, ret);
6352 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6353 if (ret != LDB_SUCCESS) {
6354 return replmd_replicated_request_error(ar, ret);
6357 replmd_ldb_message_sort(msg, ar->schema);
6359 /* we want to replace the old values */
6360 for (i=0; i < msg->num_elements; i++) {
6361 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6362 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6363 if (msg->elements[i].num_values == 0) {
6364 ldb_asprintf_errstring(ldb, __location__
6365 ": objectClass removed on %s, aborting replication\n",
6366 ldb_dn_get_linearized(msg->dn));
6367 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6373 struct GUID_txt_buf guid_txt;
6375 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6376 LDB_CHANGETYPE_MODIFY,
6378 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6379 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6383 } else if (DEBUGLVL(4)) {
6384 struct GUID_txt_buf guid_txt;
6386 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6387 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6389 ldb_dn_get_linearized(msg->dn)));
6392 ret = ldb_build_mod_req(&change_req,
6400 LDB_REQ_SET_LOCATION(change_req);
6401 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6403 /* current partition control needed by "repmd_op_callback" */
6404 ret = ldb_request_add_control(change_req,
6405 DSDB_CONTROL_CURRENT_PARTITION_OID,
6407 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6409 return ldb_next_request(ar->module, change_req);
6412 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6413 struct ldb_reply *ares)
6415 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6416 struct replmd_replicated_request);
6420 return ldb_module_done(ar->req, NULL, NULL,
6421 LDB_ERR_OPERATIONS_ERROR);
6423 if (ares->error != LDB_SUCCESS &&
6424 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6425 return ldb_module_done(ar->req, ares->controls,
6426 ares->response, ares->error);
6429 switch (ares->type) {
6430 case LDB_REPLY_ENTRY:
6431 ar->search_msg = talloc_steal(ar, ares->message);
6434 case LDB_REPLY_REFERRAL:
6435 /* we ignore referrals */
6438 case LDB_REPLY_DONE:
6440 struct replPropertyMetaData1 *md_remote;
6441 struct replPropertyMetaData1 *md_local;
6443 struct replPropertyMetaDataBlob omd;
6444 const struct ldb_val *omd_value;
6445 struct replPropertyMetaDataBlob *rmd;
6446 struct ldb_message *msg;
6448 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6449 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6452 * This is the ADD case, find the appropriate parent,
6453 * as this object doesn't exist locally:
6455 if (ar->search_msg == NULL) {
6456 ret = replmd_replicated_apply_search_for_parent(ar);
6457 if (ret != LDB_SUCCESS) {
6458 return ldb_module_done(ar->req, NULL, NULL, ret);
6465 * Otherwise, in the MERGE case, work out if we are
6466 * attempting a rename, and if so find the parent the
6467 * newly renamed object wants to belong under (which
6468 * may not be the parent in it's attached string DN
6470 rmd = ar->objs->objects[ar->index_current].meta_data;
6474 /* find existing meta data */
6475 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6477 enum ndr_err_code ndr_err;
6478 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6479 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6480 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6481 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6482 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6485 if (omd.version != 1) {
6486 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6490 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6492 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6493 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6494 && GUID_all_zero(&ar->local_parent_guid)) {
6495 DEBUG(0, ("Refusing to replicate new version of %s "
6496 "as local object has an all-zero parentGUID attribute, "
6497 "despite not being an NC root\n",
6498 ldb_dn_get_linearized(ar->search_msg->dn)));
6499 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6503 * now we need to check for double renames. We could have a
6504 * local rename pending which our replication partner hasn't
6505 * received yet. We choose which one wins by looking at the
6506 * attribute stamps on the two objects, the newer one wins.
6508 * This also simply applies the correct algorithms for
6509 * determining if a change was made to name at all, or
6510 * if the object has just been renamed under the same
6513 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6514 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6516 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6517 ldb_dn_get_linearized(ar->search_msg->dn)));
6518 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6522 * if there is no name attribute given then we have to assume the
6523 * object we've received has the older name
6525 if (replmd_replPropertyMetaData1_new_should_be_taken(
6526 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6527 md_local, md_remote)) {
6528 struct GUID_txt_buf p_guid_local;
6529 struct GUID_txt_buf p_guid_remote;
6530 msg = ar->objs->objects[ar->index_current].msg;
6532 /* Merge on the existing object, with rename */
6534 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6535 "as incoming object changing to %s under %s\n",
6536 ldb_dn_get_linearized(ar->search_msg->dn),
6537 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6538 ldb_dn_get_linearized(msg->dn),
6539 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6541 ret = replmd_replicated_apply_search_for_parent(ar);
6543 struct GUID_txt_buf p_guid_local;
6544 struct GUID_txt_buf p_guid_remote;
6545 msg = ar->objs->objects[ar->index_current].msg;
6548 * Merge on the existing object, force no
6549 * rename (code below just to explain why in
6553 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6554 ldb_dn_get_linearized(msg->dn)) == 0) {
6555 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6556 GUID_equal(&ar->local_parent_guid,
6557 ar->objs->objects[ar->index_current].parent_guid)
6559 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6560 "despite incoming object changing parent to %s\n",
6561 ldb_dn_get_linearized(ar->search_msg->dn),
6562 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6563 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6567 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6568 " and rejecting older rename to %s under %s\n",
6569 ldb_dn_get_linearized(ar->search_msg->dn),
6570 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6571 ldb_dn_get_linearized(msg->dn),
6572 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6576 * This assignment ensures that the strcmp()
6577 * and GUID_equal() calls in
6578 * replmd_replicated_apply_merge() avoids the
6581 ar->objs->objects[ar->index_current].parent_guid =
6582 &ar->local_parent_guid;
6584 msg->dn = ar->search_msg->dn;
6585 ret = replmd_replicated_apply_merge(ar);
6587 if (ret != LDB_SUCCESS) {
6588 return ldb_module_done(ar->req, NULL, NULL, ret);
6598 * Returns true if we can group together processing this link attribute,
6599 * i.e. it has the same source-object and attribute ID as other links
6600 * already in the group
6602 static bool la_entry_matches_group(struct la_entry *la_entry,
6603 struct la_group *la_group)
6605 struct la_entry *prev = la_group->la_entries;
6607 return (la_entry->la->attid == prev->la->attid &&
6608 GUID_equal(&la_entry->la->identifier->guid,
6609 &prev->la->identifier->guid));
6613 * Creates a new la_entry to store replication info for a single
6616 static struct la_entry *
6617 create_la_entry(struct replmd_private *replmd_private,
6618 struct drsuapi_DsReplicaLinkedAttribute *la,
6619 uint32_t dsdb_repl_flags)
6621 struct la_entry *la_entry;
6623 if (replmd_private->la_ctx == NULL) {
6624 replmd_private->la_ctx = talloc_new(replmd_private);
6626 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6627 if (la_entry == NULL) {
6630 la_entry->la = talloc(la_entry,
6631 struct drsuapi_DsReplicaLinkedAttribute);
6632 if (la_entry->la == NULL) {
6633 talloc_free(la_entry);
6636 *la_entry->la = *la;
6637 la_entry->dsdb_repl_flags = dsdb_repl_flags;
6640 * we need to steal the non-scalars so they stay
6641 * around until the end of the transaction
6643 talloc_steal(la_entry->la, la_entry->la->identifier);
6644 talloc_steal(la_entry->la, la_entry->la->value.blob);
6650 * Stores the linked attributes received in the replication chunk - these get
6651 * applied at the end of the transaction. We also check that each linked
6652 * attribute is valid, i.e. source and target objects are known.
6654 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6656 int ret = LDB_SUCCESS;
6658 struct ldb_module *module = ar->module;
6659 struct replmd_private *replmd_private =
6660 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6661 struct la_group *la_group = NULL;
6662 struct ldb_context *ldb;
6663 TALLOC_CTX *tmp_ctx = NULL;
6664 struct ldb_message *src_msg = NULL;
6665 const struct dsdb_attribute *attr = NULL;
6667 ldb = ldb_module_get_ctx(module);
6669 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6671 /* save away the linked attributes for the end of the transaction */
6672 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6673 struct la_entry *la_entry;
6676 /* create an entry to store the received link attribute info */
6677 la_entry = create_la_entry(replmd_private,
6678 &ar->objs->linked_attributes[i],
6679 ar->objs->dsdb_repl_flags);
6680 if (la_entry == NULL) {
6682 return LDB_ERR_OPERATIONS_ERROR;
6686 * check if we're still dealing with the same source object
6689 new_srcobj = (la_group == NULL ||
6690 !la_entry_matches_group(la_entry, la_group));
6694 /* get a new mem_ctx to lookup the source object */
6695 TALLOC_FREE(tmp_ctx);
6696 tmp_ctx = talloc_new(ar);
6697 if (tmp_ctx == NULL) {
6699 return LDB_ERR_OPERATIONS_ERROR;
6702 /* verify the link source exists */
6703 ret = replmd_get_la_entry_source(module, la_entry,
6708 * When we fail to find the source object, the error
6709 * code we pass back here is really important. It flags
6710 * back to the callers to retry this request with
6711 * DRSUAPI_DRS_GET_ANC. This case should never happen
6712 * if we're replicating from a Samba DC, but it is
6713 * needed to talk to a Windows DC
6715 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6716 WERROR err = WERR_DS_DRA_MISSING_PARENT;
6717 ret = replmd_replicated_request_werror(ar,
6723 ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6725 if (ret != LDB_SUCCESS) {
6729 /* group the links together by source-object for efficiency */
6731 la_group = talloc_zero(replmd_private->la_ctx,
6733 if (la_group == NULL) {
6735 return LDB_ERR_OPERATIONS_ERROR;
6737 DLIST_ADD(replmd_private->la_list, la_group);
6739 DLIST_ADD(la_group->la_entries, la_entry);
6740 replmd_private->total_links++;
6743 TALLOC_FREE(tmp_ctx);
6747 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6749 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6751 struct ldb_context *ldb;
6755 struct ldb_request *search_req;
6756 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6757 "parentGUID", "instanceType",
6758 "replPropertyMetaData", "nTSecurityDescriptor",
6759 "isDeleted", NULL };
6760 struct GUID_txt_buf guid_str_buf;
6762 if (ar->index_current >= ar->objs->num_objects) {
6765 * Now that we've applied all the objects, check the new linked
6766 * attributes and store them (we apply them in .prepare_commit)
6768 ret = replmd_store_linked_attributes(ar);
6770 if (ret != LDB_SUCCESS) {
6774 /* done applying objects, move on to the next stage */
6775 return replmd_replicated_uptodate_vector(ar);
6778 ldb = ldb_module_get_ctx(ar->module);
6779 ar->search_msg = NULL;
6780 ar->isDeleted = false;
6782 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6785 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6786 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6788 ret = ldb_build_search_req(&search_req,
6791 ar->objs->partition_dn,
6797 replmd_replicated_apply_search_callback,
6799 LDB_REQ_SET_LOCATION(search_req);
6801 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6803 if (ret != LDB_SUCCESS) {
6807 return ldb_next_request(ar->module, search_req);
6811 * Returns true if we need to do extra processing to handle deleted object
6812 * changes received via replication
6814 static bool replmd_should_apply_isDeleted(struct replmd_replicated_request *ar,
6815 struct ldb_message *msg)
6817 struct ldb_dn *deleted_objects_dn;
6820 if (!ar->isDeleted) {
6822 /* not a deleted object, so don't set isDeleted */
6826 ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module),
6828 &deleted_objects_dn);
6831 * if the Deleted Object container lookup failed, then just apply
6832 * isDeleted (note that it doesn't exist for the Schema partition)
6834 if (ret != LDB_SUCCESS) {
6839 * the Deleted Objects container has isDeleted set but is not entirely
6840 * a deleted object, so DON'T re-apply isDeleted to it
6842 if (ldb_dn_compare(msg->dn, deleted_objects_dn) == 0) {
6850 * This is essentially a wrapper for replmd_replicated_apply_next()
6852 * This is needed to ensure that both codepaths call this handler.
6854 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6856 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6858 bool apply_isDeleted;
6859 struct ldb_request *del_req = NULL;
6860 struct ldb_result *res = NULL;
6861 TALLOC_CTX *tmp_ctx = NULL;
6863 apply_isDeleted = replmd_should_apply_isDeleted(ar, msg);
6865 if (!apply_isDeleted) {
6868 ar->index_current++;
6869 return replmd_replicated_apply_next(ar);
6873 * Do a delete here again, so that if there is
6874 * anything local that conflicts with this
6875 * object being deleted, it is removed. This
6876 * includes links. See MS-DRSR 4.1.10.6.9
6879 * If the object is already deleted, and there
6880 * is no more work required, it doesn't do
6884 /* This has been updated to point to the DN we eventually did the modify on */
6886 tmp_ctx = talloc_new(ar);
6888 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6892 res = talloc_zero(tmp_ctx, struct ldb_result);
6894 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6895 talloc_free(tmp_ctx);
6899 /* Build a delete request, which hopefully will artually turn into nothing */
6900 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6904 ldb_modify_default_callback,
6906 LDB_REQ_SET_LOCATION(del_req);
6907 if (ret != LDB_SUCCESS) {
6908 talloc_free(tmp_ctx);
6913 * This is the guts of the call, call back
6914 * into our delete code, but setting the
6915 * re_delete flag so we delete anything that
6916 * shouldn't be there on a deleted or recycled
6919 ret = replmd_delete_internals(ar->module, del_req, true);
6920 if (ret == LDB_SUCCESS) {
6921 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6924 talloc_free(tmp_ctx);
6925 if (ret != LDB_SUCCESS) {
6929 ar->index_current++;
6930 return replmd_replicated_apply_next(ar);
6933 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6934 struct ldb_reply *ares)
6936 struct ldb_context *ldb;
6937 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6938 struct replmd_replicated_request);
6939 ldb = ldb_module_get_ctx(ar->module);
6942 return ldb_module_done(ar->req, NULL, NULL,
6943 LDB_ERR_OPERATIONS_ERROR);
6945 if (ares->error != LDB_SUCCESS) {
6946 return ldb_module_done(ar->req, ares->controls,
6947 ares->response, ares->error);
6950 if (ares->type != LDB_REPLY_DONE) {
6951 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6952 return ldb_module_done(ar->req, NULL, NULL,
6953 LDB_ERR_OPERATIONS_ERROR);
6958 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6961 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6963 struct ldb_context *ldb;
6964 struct ldb_request *change_req;
6965 enum ndr_err_code ndr_err;
6966 struct ldb_message *msg;
6967 struct replUpToDateVectorBlob ouv;
6968 const struct ldb_val *ouv_value;
6969 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6970 struct replUpToDateVectorBlob nuv;
6971 struct ldb_val nuv_value;
6972 struct ldb_message_element *nuv_el = NULL;
6973 struct ldb_message_element *orf_el = NULL;
6974 struct repsFromToBlob nrf;
6975 struct ldb_val *nrf_value = NULL;
6976 struct ldb_message_element *nrf_el = NULL;
6980 time_t t = time(NULL);
6983 uint32_t instanceType;
6985 ldb = ldb_module_get_ctx(ar->module);
6986 ruv = ar->objs->uptodateness_vector;
6992 unix_to_nt_time(&now, t);
6994 if (ar->search_msg == NULL) {
6995 /* this happens for a REPL_OBJ call where we are
6996 creating the target object by replicating it. The
6997 subdomain join code does this for the partition DN
6999 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
7000 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7003 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
7004 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
7005 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
7006 ldb_dn_get_linearized(ar->search_msg->dn)));
7007 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7011 * first create the new replUpToDateVector
7013 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
7015 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
7016 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
7017 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7018 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7019 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7022 if (ouv.version != 2) {
7023 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7028 * the new uptodateness vector will at least
7029 * contain 1 entry, one for the source_dsa
7031 * plus optional values from our old vector and the one from the source_dsa
7033 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
7034 if (ruv) nuv.ctr.ctr2.count += ruv->count;
7035 nuv.ctr.ctr2.cursors = talloc_array(ar,
7036 struct drsuapi_DsReplicaCursor2,
7037 nuv.ctr.ctr2.count);
7038 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7040 /* first copy the old vector */
7041 for (i=0; i < ouv.ctr.ctr2.count; i++) {
7042 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
7046 /* merge in the source_dsa vector is available */
7047 for (i=0; (ruv && i < ruv->count); i++) {
7050 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7051 &ar->our_invocation_id)) {
7055 for (j=0; j < ni; j++) {
7056 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7057 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7063 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7064 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7069 if (found) continue;
7071 /* if it's not there yet, add it */
7072 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7077 * finally correct the size of the cursors array
7079 nuv.ctr.ctr2.count = ni;
7084 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7087 * create the change ldb_message
7089 msg = ldb_msg_new(ar);
7090 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7091 msg->dn = ar->search_msg->dn;
7093 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7094 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7095 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7096 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7097 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7099 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7100 if (ret != LDB_SUCCESS) {
7101 return replmd_replicated_request_error(ar, ret);
7103 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7106 * now create the new repsFrom value from the given repsFromTo1 structure
7110 nrf.ctr.ctr1 = *ar->objs->source_dsa;
7111 nrf.ctr.ctr1.last_attempt = now;
7112 nrf.ctr.ctr1.last_success = now;
7113 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7116 * first see if we already have a repsFrom value for the current source dsa
7117 * if so we'll later replace this value
7119 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7121 for (i=0; i < orf_el->num_values; i++) {
7122 struct repsFromToBlob *trf;
7124 trf = talloc(ar, struct repsFromToBlob);
7125 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7127 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7128 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7129 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7130 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7131 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7134 if (trf->version != 1) {
7135 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7139 * we compare the source dsa objectGUID not the invocation_id
7140 * because we want only one repsFrom value per source dsa
7141 * and when the invocation_id of the source dsa has changed we don't need
7142 * the old repsFrom with the old invocation_id
7144 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7145 &ar->objs->source_dsa->source_dsa_obj_guid)) {
7151 nrf_value = &orf_el->values[i];
7156 * copy over all old values to the new ldb_message
7158 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7159 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7164 * if we haven't found an old repsFrom value for the current source dsa
7165 * we'll add a new value
7168 struct ldb_val zero_value;
7169 ZERO_STRUCT(zero_value);
7170 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7171 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7173 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7176 /* we now fill the value which is already attached to ldb_message */
7177 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7179 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7180 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7181 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7182 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7186 * the ldb_message_element for the attribute, has all the old values and the new one
7187 * so we'll replace the whole attribute with all values
7189 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7191 if (CHECK_DEBUGLVL(4)) {
7192 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7193 LDB_CHANGETYPE_MODIFY,
7195 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7199 /* prepare the ldb_modify() request */
7200 ret = ldb_build_mod_req(&change_req,
7206 replmd_replicated_uptodate_modify_callback,
7208 LDB_REQ_SET_LOCATION(change_req);
7209 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7211 return ldb_next_request(ar->module, change_req);
7214 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7215 struct ldb_reply *ares)
7217 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7218 struct replmd_replicated_request);
7222 return ldb_module_done(ar->req, NULL, NULL,
7223 LDB_ERR_OPERATIONS_ERROR);
7225 if (ares->error != LDB_SUCCESS &&
7226 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7227 return ldb_module_done(ar->req, ares->controls,
7228 ares->response, ares->error);
7231 switch (ares->type) {
7232 case LDB_REPLY_ENTRY:
7233 ar->search_msg = talloc_steal(ar, ares->message);
7236 case LDB_REPLY_REFERRAL:
7237 /* we ignore referrals */
7240 case LDB_REPLY_DONE:
7241 ret = replmd_replicated_uptodate_modify(ar);
7242 if (ret != LDB_SUCCESS) {
7243 return ldb_module_done(ar->req, NULL, NULL, ret);
7252 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7254 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7255 struct replmd_private *replmd_private =
7256 talloc_get_type_abort(ldb_module_get_private(ar->module),
7257 struct replmd_private);
7259 static const char *attrs[] = {
7260 "replUpToDateVector",
7265 struct ldb_request *search_req;
7267 ar->search_msg = NULL;
7270 * Let the caller know that we did an originating updates
7272 ar->objs->originating_updates = replmd_private->originating_updates;
7274 ret = ldb_build_search_req(&search_req,
7277 ar->objs->partition_dn,
7283 replmd_replicated_uptodate_search_callback,
7285 LDB_REQ_SET_LOCATION(search_req);
7286 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7288 return ldb_next_request(ar->module, search_req);
7293 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7295 struct ldb_context *ldb;
7296 struct dsdb_extended_replicated_objects *objs;
7297 struct replmd_replicated_request *ar;
7298 struct ldb_control **ctrls;
7301 ldb = ldb_module_get_ctx(module);
7303 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7305 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7307 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7308 return LDB_ERR_PROTOCOL_ERROR;
7311 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7312 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7313 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7314 return LDB_ERR_PROTOCOL_ERROR;
7317 ar = replmd_ctx_init(module, req);
7319 return LDB_ERR_OPERATIONS_ERROR;
7321 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7322 ar->apply_mode = true;
7324 ar->schema = dsdb_get_schema(ldb, ar);
7326 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7328 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7329 return LDB_ERR_CONSTRAINT_VIOLATION;
7332 ctrls = req->controls;
7334 if (req->controls) {
7335 req->controls = talloc_memdup(ar, req->controls,
7336 talloc_get_size(req->controls));
7337 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7340 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7341 if (ret != LDB_SUCCESS) {
7345 /* If this change contained linked attributes in the body
7346 * (rather than in the links section) we need to update
7347 * backlinks in linked_attributes */
7348 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7349 if (ret != LDB_SUCCESS) {
7353 ar->controls = req->controls;
7354 req->controls = ctrls;
7356 return replmd_replicated_apply_next(ar);
7360 * Checks how to handle an missing target - either we need to fail the
7361 * replication and retry with GET_TGT, ignore the link and continue, or try to
7362 * add a partial link to an unknown target.
7364 static int replmd_allow_missing_target(struct ldb_module *module,
7365 TALLOC_CTX *mem_ctx,
7366 struct ldb_dn *target_dn,
7367 struct ldb_dn *source_dn,
7370 uint32_t dsdb_repl_flags,
7372 const char * missing_str)
7374 struct ldb_context *ldb = ldb_module_get_ctx(module);
7378 * we may not be able to resolve link targets properly when
7379 * dealing with subsets of objects, e.g. the source is a
7380 * critical object and the target isn't
7383 * When we implement Trusted Domains we need to consider
7384 * whether they get treated as an incomplete replica here or not
7386 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7389 * Ignore the link. We don't increase the highwater-mark in
7390 * the object subset cases, so subsequent replications should
7391 * resolve any missing links
7393 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7394 ldb_dn_get_linearized(target_dn),
7395 ldb_dn_get_linearized(source_dn)));
7396 *ignore_link = true;
7400 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7403 * target should already be up-to-date so there's no point in
7404 * retrying. This could be due to bad timing, or if a target
7405 * on a one-way link was deleted. We ignore the link rather
7406 * than failing the replication cycle completely
7408 *ignore_link = true;
7409 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7410 ldb_dn_get_linearized(target_dn), missing_str,
7411 ldb_dn_get_linearized(source_dn));
7415 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7419 if (is_in_same_nc) {
7420 /* fail the replication and retry with GET_TGT */
7421 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7423 ldb_dn_get_linearized(target_dn),
7424 GUID_string(mem_ctx, guid),
7425 ldb_dn_get_linearized(source_dn));
7426 return LDB_ERR_NO_SUCH_OBJECT;
7430 * The target of the cross-partition link is missing. Continue
7431 * and try to at least add the forward-link. This isn't great,
7432 * but a partial link can be fixed by dbcheck, so it's better
7433 * than dropping the link completely.
7435 *ignore_link = false;
7437 if (is_obj_commit) {
7440 * Only log this when we're actually committing the objects.
7441 * This avoids spurious logs, i.e. if we're just verifying the
7442 * received link during a join.
7444 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7445 missing_str, ldb_dn_get_linearized(target_dn),
7446 ldb_dn_get_linearized(source_dn));
7453 * Checks that the target object for a linked attribute exists.
7454 * @param guid returns the target object's GUID (is returned)if it exists)
7455 * @param ignore_link set to true if the linked attribute should be ignored
7456 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7458 static int replmd_check_target_exists(struct ldb_module *module,
7459 struct dsdb_dn *dsdb_dn,
7460 struct la_entry *la_entry,
7461 struct ldb_dn *source_dn,
7466 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7467 struct ldb_context *ldb = ldb_module_get_ctx(module);
7468 struct ldb_result *target_res;
7469 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7470 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7473 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7474 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7476 *ignore_link = false;
7477 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7479 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7482 * This strange behaviour (allowing a NULL/missing
7483 * GUID) originally comes from:
7485 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7486 * Author: Andrew Tridgell <tridge@samba.org>
7487 * Date: Mon Dec 21 21:21:55 2009 +1100
7489 * s4-drs: cope better with NULL GUIDS from DRS
7491 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7492 * need to match by DN if possible when seeing if we should update an
7495 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7497 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7499 DSDB_FLAG_NEXT_MODULE |
7500 DSDB_SEARCH_SHOW_RECYCLED |
7501 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7502 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7504 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7505 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7507 ldb_dn_get_linearized(dsdb_dn->dn),
7508 ldb_dn_get_linearized(source_dn));
7509 talloc_free(tmp_ctx);
7510 return LDB_ERR_OPERATIONS_ERROR;
7512 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7513 NULL, LDB_SCOPE_SUBTREE,
7515 DSDB_FLAG_NEXT_MODULE |
7516 DSDB_SEARCH_SHOW_RECYCLED |
7517 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7518 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7521 GUID_string(tmp_ctx, guid));
7524 if (ret != LDB_SUCCESS) {
7525 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7526 GUID_string(tmp_ctx, guid),
7527 ldb_errstring(ldb));
7528 talloc_free(tmp_ctx);
7532 if (target_res->count == 0) {
7535 * target object is unknown. Check whether to ignore the link,
7536 * fail the replication, or add a partial link
7538 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7539 source_dn, is_obj_commit, guid,
7540 la_entry->dsdb_repl_flags,
7541 ignore_link, "Unknown");
7543 } else if (target_res->count != 1) {
7544 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7545 GUID_string(tmp_ctx, guid));
7546 ret = LDB_ERR_OPERATIONS_ERROR;
7548 struct ldb_message *target_msg = target_res->msgs[0];
7550 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7552 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7553 replmd_deletion_state(module, target_msg,
7554 &target_deletion_state, NULL);
7557 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7558 * ProcessLinkValue(). Link updates should not be sent for
7559 * recycled and tombstone objects (deleting the links should
7560 * happen when we delete the object). This probably means our
7561 * copy of the target object isn't up to date.
7563 if (target_deletion_state >= OBJECT_RECYCLED) {
7566 * target object is deleted. Check whether to ignore the
7567 * link, fail the replication, or add a partial link
7569 ret = replmd_allow_missing_target(module, tmp_ctx,
7570 dsdb_dn->dn, source_dn,
7571 is_obj_commit, guid,
7572 la_entry->dsdb_repl_flags,
7573 ignore_link, "Deleted");
7577 talloc_free(tmp_ctx);
7582 * Extracts the key details about the source object for a
7583 * linked-attribute entry.
7584 * This returns the following details:
7585 * @param ret_attr the schema details for the linked attribute
7586 * @param source_msg the search result for the source object
7588 static int replmd_get_la_entry_source(struct ldb_module *module,
7589 struct la_entry *la_entry,
7590 TALLOC_CTX *mem_ctx,
7591 const struct dsdb_attribute **ret_attr,
7592 struct ldb_message **source_msg)
7594 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7595 struct ldb_context *ldb = ldb_module_get_ctx(module);
7596 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7598 const struct dsdb_attribute *attr;
7599 struct ldb_result *res;
7600 const char *attrs[4];
7603 linked_attributes[0]:
7604 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7606 identifier: struct drsuapi_DsReplicaObjectIdentifier
7607 __ndr_size : 0x0000003a (58)
7608 __ndr_size_sid : 0x00000000 (0)
7609 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7611 __ndr_size_dn : 0x00000000 (0)
7613 attid : DRSUAPI_ATTID_member (0x1F)
7614 value: struct drsuapi_DsAttributeValue
7615 __ndr_size : 0x0000007e (126)
7617 blob : DATA_BLOB length=126
7618 flags : 0x00000001 (1)
7619 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7620 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7621 meta_data: struct drsuapi_DsReplicaMetaData
7622 version : 0x00000015 (21)
7623 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7624 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7625 originating_usn : 0x000000000001e19c (123292)
7627 (for cases where the link is to a normal DN)
7628 &target: struct drsuapi_DsReplicaObjectIdentifier3
7629 __ndr_size : 0x0000007e (126)
7630 __ndr_size_sid : 0x0000001c (28)
7631 guid : 7639e594-db75-4086-b0d4-67890ae46031
7632 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7633 __ndr_size_dn : 0x00000022 (34)
7634 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7637 /* find the attribute being modified */
7638 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7640 struct GUID_txt_buf guid_str;
7641 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7643 GUID_buf_string(&la->identifier->guid,
7645 return LDB_ERR_OPERATIONS_ERROR;
7649 * All attributes listed here must be dealt with in some way
7650 * by replmd_process_linked_attribute() otherwise in the case
7651 * of isDeleted: FALSE the modify will fail with:
7653 * Failed to apply linked attribute change 'attribute 'isDeleted':
7654 * invalid modify flags on
7655 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7658 * This is becaue isDeleted is a Boolean, so FALSE is a
7659 * legitimate value (set by Samba's deletetest.py)
7661 attrs[0] = attr->lDAPDisplayName;
7662 attrs[1] = "isDeleted";
7663 attrs[2] = "isRecycled";
7667 * get the existing message from the db for the object with
7668 * this GUID, returning attribute being modified. We will then
7669 * use this msg as the basis for a modify call
7671 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7672 DSDB_FLAG_NEXT_MODULE |
7673 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7674 DSDB_SEARCH_SHOW_RECYCLED |
7675 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7676 DSDB_SEARCH_REVEAL_INTERNALS,
7678 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7679 if (ret != LDB_SUCCESS) {
7682 if (res->count != 1) {
7683 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7684 GUID_string(mem_ctx, &la->identifier->guid));
7685 return LDB_ERR_NO_SUCH_OBJECT;
7688 *source_msg = res->msgs[0];
7695 * Verifies the target object is known for a linked attribute
7697 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7698 TALLOC_CTX *mem_ctx,
7699 struct la_entry *la_entry,
7700 struct ldb_dn *src_dn,
7701 const struct dsdb_attribute *attr)
7703 int ret = LDB_SUCCESS;
7704 struct ldb_module *module = ar->module;
7705 struct dsdb_dn *tgt_dsdb_dn = NULL;
7706 struct GUID guid = GUID_zero();
7709 struct ldb_context *ldb = ldb_module_get_ctx(module);
7710 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7711 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7713 /* the value blob for the attribute holds the target object DN */
7714 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7715 la->value.blob, &tgt_dsdb_dn);
7716 if (!W_ERROR_IS_OK(status)) {
7717 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7718 attr->lDAPDisplayName,
7719 ldb_dn_get_linearized(src_dn),
7720 win_errstr(status));
7721 return LDB_ERR_OPERATIONS_ERROR;
7725 * We can skip the target object checks if we're only syncing critical
7726 * objects, or we know the target is up-to-date. If either case, we
7727 * still continue even if the target doesn't exist
7729 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7730 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7732 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7733 src_dn, false, &guid, &dummy);
7737 * When we fail to find the target object, the error code we pass
7738 * back here is really important. It flags back to the callers to
7739 * retry this request with DRSUAPI_DRS_GET_TGT
7741 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7742 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7749 * Finds the current active Parsed-DN value for a single-valued linked
7750 * attribute, if one exists.
7751 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7752 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7755 static int replmd_get_active_singleval_link(struct ldb_module *module,
7756 TALLOC_CTX *mem_ctx,
7757 struct parsed_dn pdn_list[],
7759 const struct dsdb_attribute *attr,
7760 struct parsed_dn **ret_pdn)
7766 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7768 /* nothing to do for multi-valued linked attributes */
7772 for (i = 0; i < count; i++) {
7773 int ret = LDB_SUCCESS;
7774 struct parsed_dn *pdn = &pdn_list[i];
7776 /* skip any inactive links */
7777 if (dsdb_dn_is_deleted_val(pdn->v)) {
7781 /* we've found an active value for this attribute */
7784 if (pdn->dsdb_dn == NULL) {
7785 struct ldb_context *ldb = ldb_module_get_ctx(module);
7787 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7788 attr->syntax->ldap_oid);
7794 /* no active link found */
7799 * @returns true if the replication linked attribute info is newer than we
7800 * already have in our DB
7801 * @param pdn the existing linked attribute info in our DB
7802 * @param la the new linked attribute info received during replication
7804 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7805 struct drsuapi_DsReplicaLinkedAttribute *la)
7807 /* see if this update is newer than what we have already */
7808 struct GUID invocation_id = GUID_zero();
7809 uint32_t version = 0;
7810 NTTIME change_time = 0;
7814 /* no existing info so update is newer */
7818 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7819 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7820 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7822 return replmd_update_is_newer(&invocation_id,
7823 &la->meta_data.originating_invocation_id,
7825 la->meta_data.version,
7827 la->meta_data.originating_change_time);
7831 * Marks an existing linked attribute value as deleted in the DB
7832 * @param pdn the parsed-DN of the target-value to delete
7834 static int replmd_delete_link_value(struct ldb_module *module,
7835 struct replmd_private *replmd_private,
7836 TALLOC_CTX *mem_ctx,
7837 struct ldb_dn *src_obj_dn,
7838 const struct dsdb_schema *schema,
7839 const struct dsdb_attribute *attr,
7842 struct GUID *target_guid,
7843 struct dsdb_dn *target_dsdb_dn,
7844 struct ldb_val *output_val)
7846 struct ldb_context *ldb = ldb_module_get_ctx(module);
7849 const struct GUID *invocation_id = NULL;
7853 unix_to_nt_time(&now, t);
7855 invocation_id = samdb_ntds_invocation_id(ldb);
7856 if (invocation_id == NULL) {
7857 return LDB_ERR_OPERATIONS_ERROR;
7860 /* if the existing link is active, remove its backlink */
7864 * NOTE WELL: After this we will never (at runtime) be
7865 * able to find this forward link (for instant
7866 * removal) if/when the link target is deleted.
7868 * We have dbcheck rules to cover this and cope otherwise
7869 * by filtering at runtime (i.e. in the extended_dn module).
7871 ret = replmd_add_backlink(module, replmd_private, schema,
7872 src_obj_dn, target_guid, false,
7874 if (ret != LDB_SUCCESS) {
7879 /* mark the existing value as deleted */
7880 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7881 target_dsdb_dn, invocation_id, seq_num,
7882 seq_num, now, true);
7887 * Checks for a conflict in single-valued link attributes, and tries to
7888 * resolve the problem if possible.
7890 * Single-valued links should only ever have one active value. If we already
7891 * have an active link value, and during replication we receive an active link
7892 * value for a different target DN, then we need to resolve this inconsistency
7893 * and determine which value should be active. If the received info is better/
7894 * newer than the existing link attribute, then we need to set our existing
7895 * link as deleted. If the received info is worse/older, then we should continue
7896 * to add it, but set it as an inactive link.
7898 * Note that this is a corner-case that is unlikely to happen (but if it does
7899 * happen, we don't want it to break replication completely).
7901 * @param pdn_being_modified the parsed DN corresponding to the received link
7902 * target (note this is NULL if the link does not already exist in our DB)
7903 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7904 * any existing active or inactive values for the attribute in our DB.
7905 * @param dsdb_dn the target DN for the received link attribute
7906 * @param add_as_inactive gets set to true if the received link is worse than
7907 * the existing link - it should still be added, but as an inactive link.
7909 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7910 struct replmd_private *replmd_private,
7911 TALLOC_CTX *mem_ctx,
7912 struct ldb_dn *src_obj_dn,
7913 struct drsuapi_DsReplicaLinkedAttribute *la,
7914 struct dsdb_dn *dsdb_dn,
7915 struct parsed_dn *pdn_being_modified,
7916 struct parsed_dn *pdn_list,
7917 struct ldb_message_element *old_el,
7918 const struct dsdb_schema *schema,
7919 const struct dsdb_attribute *attr,
7921 bool *add_as_inactive)
7923 struct parsed_dn *active_pdn = NULL;
7924 bool update_is_newer = false;
7928 * check if there's a conflict for single-valued links, i.e. an active
7929 * linked attribute already exists, but it has a different target value
7931 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7932 old_el->num_values, attr,
7935 if (ret != LDB_SUCCESS) {
7940 * If no active value exists (or the received info is for the currently
7941 * active value), then no conflict exists
7943 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7947 DBG_WARNING("Link conflict for %s attribute on %s\n",
7948 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7950 /* Work out how to resolve the conflict based on which info is better */
7951 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7953 if (update_is_newer) {
7954 DBG_WARNING("Using received value %s, over existing target %s\n",
7955 ldb_dn_get_linearized(dsdb_dn->dn),
7956 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7959 * Delete our existing active link. The received info will then
7960 * be added (through normal link processing) as the active value
7962 ret = replmd_delete_link_value(module, replmd_private, old_el,
7963 src_obj_dn, schema, attr,
7964 seq_num, true, &active_pdn->guid,
7965 active_pdn->dsdb_dn,
7968 if (ret != LDB_SUCCESS) {
7972 DBG_WARNING("Using existing target %s, over received value %s\n",
7973 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7974 ldb_dn_get_linearized(dsdb_dn->dn));
7977 * we want to keep our existing active link and add the
7978 * received link as inactive
7980 *add_as_inactive = true;
7987 * Processes one linked attribute received via replication.
7988 * @param src_dn the DN of the source object for the link
7989 * @param attr schema info for the linked attribute
7990 * @param la_entry the linked attribute info received via DRS
7991 * @param element_ctx mem context for msg->element[] (when adding a new value
7992 * we need to realloc old_el->values)
7993 * @param old_el the corresponding msg->element[] for the linked attribute
7994 * @param pdn_list a (binary-searchable) parsed DN array for the existing link
7995 * values in the msg. E.g. for a group, this is the existing members.
7996 * @param change what got modified: either nothing, an existing link value was
7997 * modified, or a new link value was added.
7998 * @returns LDB_SUCCESS if OK, an error otherwise
8000 static int replmd_process_linked_attribute(struct ldb_module *module,
8001 TALLOC_CTX *mem_ctx,
8002 struct replmd_private *replmd_private,
8003 struct ldb_dn *src_dn,
8004 const struct dsdb_attribute *attr,
8005 struct la_entry *la_entry,
8006 struct ldb_request *parent,
8007 struct ldb_message_element *old_el,
8008 TALLOC_CTX *element_ctx,
8009 struct parsed_dn *pdn_list,
8010 replmd_link_changed *change)
8012 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
8013 struct ldb_context *ldb = ldb_module_get_ctx(module);
8014 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
8016 struct dsdb_dn *dsdb_dn = NULL;
8017 uint64_t seq_num = 0;
8018 struct parsed_dn *pdn, *next;
8019 struct GUID guid = GUID_zero();
8020 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
8022 struct dsdb_dn *old_dsdb_dn = NULL;
8023 struct ldb_val *val_to_update = NULL;
8024 bool add_as_inactive = false;
8027 *change = LINK_CHANGE_NONE;
8029 /* the value blob for the attribute holds the target object DN */
8030 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
8031 la->value.blob, &dsdb_dn);
8032 if (!W_ERROR_IS_OK(status)) {
8033 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8034 attr->lDAPDisplayName,
8035 ldb_dn_get_linearized(src_dn),
8036 win_errstr(status));
8037 return LDB_ERR_OPERATIONS_ERROR;
8040 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
8041 true, &guid, &ignore_link);
8043 if (ret != LDB_SUCCESS) {
8048 * there are some cases where the target object doesn't exist, but it's
8049 * OK to ignore the linked attribute
8055 /* see if this link already exists */
8056 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8059 dsdb_dn->extra_part, 0,
8061 attr->syntax->ldap_oid,
8063 if (ret != LDB_SUCCESS) {
8067 if (!replmd_link_update_is_newer(pdn, la)) {
8068 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8069 old_el->name, ldb_dn_get_linearized(src_dn),
8070 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8074 /* get a seq_num for this change */
8075 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8076 if (ret != LDB_SUCCESS) {
8081 * check for single-valued link conflicts, i.e. an active linked
8082 * attribute already exists, but it has a different target value
8085 ret = replmd_check_singleval_la_conflict(module, replmd_private,
8086 mem_ctx, src_dn, la,
8087 dsdb_dn, pdn, pdn_list,
8088 old_el, schema, attr,
8091 if (ret != LDB_SUCCESS) {
8097 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8099 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8100 /* remove the existing backlink */
8101 ret = replmd_add_backlink(module, replmd_private,
8104 &pdn->guid, false, attr,
8106 if (ret != LDB_SUCCESS) {
8111 val_to_update = pdn->v;
8112 old_dsdb_dn = pdn->dsdb_dn;
8113 *change = LINK_CHANGE_MODIFIED;
8119 * We know where the new one needs to be, from the *next
8120 * pointer into pdn_list.
8123 offset = old_el->num_values;
8125 if (next->dsdb_dn == NULL) {
8126 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8127 attr->syntax->ldap_oid);
8128 if (ret != LDB_SUCCESS) {
8132 offset = next - pdn_list;
8133 if (offset > old_el->num_values) {
8134 return LDB_ERR_OPERATIONS_ERROR;
8138 old_el->values = talloc_realloc(element_ctx, old_el->values,
8139 struct ldb_val, old_el->num_values+1);
8140 if (!old_el->values) {
8141 ldb_module_oom(module);
8142 return LDB_ERR_OPERATIONS_ERROR;
8145 if (offset != old_el->num_values) {
8146 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8147 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8150 old_el->num_values++;
8152 val_to_update = &old_el->values[offset];
8154 *change = LINK_CHANGE_ADDED;
8157 /* set the link attribute's value to the info that was received */
8158 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8159 &la->meta_data.originating_invocation_id,
8160 la->meta_data.originating_usn, seq_num,
8161 la->meta_data.originating_change_time,
8162 la->meta_data.version,
8164 if (ret != LDB_SUCCESS) {
8168 if (add_as_inactive) {
8170 /* Set the new link as inactive/deleted to avoid conflicts */
8171 ret = replmd_delete_link_value(module, replmd_private, old_el,
8172 src_dn, schema, attr, seq_num,
8173 false, &guid, dsdb_dn,
8176 if (ret != LDB_SUCCESS) {
8180 } else if (active) {
8182 /* if the new link is active, then add the new backlink */
8183 ret = replmd_add_backlink(module, replmd_private,
8188 if (ret != LDB_SUCCESS) {
8193 ret = dsdb_check_single_valued_link(attr, old_el);
8194 if (ret != LDB_SUCCESS) {
8198 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8203 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8205 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8206 return replmd_extended_replicated_objects(module, req);
8209 return ldb_next_request(module, req);
8214 we hook into the transaction operations to allow us to
8215 perform the linked attribute updates at the end of the whole
8216 transaction. This allows a forward linked attribute to be created
8217 before the object is created. During a vampire, w2k8 sends us linked
8218 attributes before the objects they are part of.
8220 static int replmd_start_transaction(struct ldb_module *module)
8222 /* create our private structure for this transaction */
8223 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8224 struct replmd_private);
8225 replmd_txn_cleanup(replmd_private);
8227 /* free any leftover mod_usn records from cancelled
8229 while (replmd_private->ncs) {
8230 struct nc_entry *e = replmd_private->ncs;
8231 DLIST_REMOVE(replmd_private->ncs, e);
8235 replmd_private->originating_updates = false;
8237 return ldb_next_start_trans(module);
8241 * Processes a group of linked attributes that apply to the same source-object
8242 * and attribute-ID (and were received in the same replication chunk).
8244 static int replmd_process_la_group(struct ldb_module *module,
8245 struct replmd_private *replmd_private,
8246 struct la_group *la_group)
8248 struct la_entry *la = NULL;
8249 struct la_entry *prev = NULL;
8251 TALLOC_CTX *tmp_ctx = NULL;
8252 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8253 struct ldb_message *msg = NULL;
8254 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8255 struct ldb_context *ldb = ldb_module_get_ctx(module);
8256 const struct dsdb_attribute *attr = NULL;
8257 struct ldb_message_element *old_el = NULL;
8258 struct parsed_dn *pdn_list = NULL;
8259 replmd_link_changed change_type;
8260 uint32_t num_changes = 0;
8262 uint64_t seq_num = 0;
8264 tmp_ctx = talloc_new(la_group);
8265 if (tmp_ctx == NULL) {
8266 return ldb_oom(ldb);
8270 * get the attribute being modified and the search result for the
8273 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8276 if (ret != LDB_SUCCESS) {
8281 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8282 * ProcessLinkValue, because link updates are not applied to
8283 * recycled and tombstone objects. We don't have to delete
8284 * any existing link, that should have happened when the
8285 * object deletion was replicated or initiated.
8287 * This needs isDeleted and isRecycled to be included as
8288 * attributes in the search and so in msg if set.
8290 replmd_deletion_state(module, msg, &deletion_state, NULL);
8292 if (deletion_state >= OBJECT_RECYCLED) {
8293 TALLOC_FREE(tmp_ctx);
8298 * Now that we know the deletion_state, remove the extra
8299 * attributes added for that purpose. We need to do this
8300 * otherwise in the case of isDeleted: FALSE the modify will
8303 * Failed to apply linked attribute change 'attribute 'isDeleted':
8304 * invalid modify flags on
8305 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8308 * This is becaue isDeleted is a Boolean, so FALSE is a
8309 * legitimate value (set by Samba's deletetest.py)
8311 ldb_msg_remove_attr(msg, "isDeleted");
8312 ldb_msg_remove_attr(msg, "isRecycled");
8314 /* get the msg->element[] for the link attribute being processed */
8315 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8316 if (old_el == NULL) {
8317 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
8318 LDB_FLAG_MOD_REPLACE, &old_el);
8319 if (ret != LDB_SUCCESS) {
8320 ldb_module_oom(module);
8321 return LDB_ERR_OPERATIONS_ERROR;
8324 old_el->flags = LDB_FLAG_MOD_REPLACE;
8328 * go through and process the link target value(s) for this particular
8329 * source object and attribute. For optimization, the same msg is used
8330 * across multiple calls to replmd_process_linked_attribute().
8331 * Note that we should not add or remove any msg attributes inside the
8332 * loop (we should only add/modify *values* for the attribute being
8333 * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8334 * pointers will be invalidated
8336 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8337 prev = DLIST_PREV(la);
8338 DLIST_REMOVE(la_group->la_entries, la);
8341 * parse the existing links (this can be costly for a large
8342 * group, so we try to minimize the times we do it)
8344 if (pdn_list == NULL) {
8345 ret = get_parsed_dns_trusted_fallback(module,
8349 attr->syntax->ldap_oid,
8352 if (ret != LDB_SUCCESS) {
8356 ret = replmd_process_linked_attribute(module, tmp_ctx,
8358 msg->dn, attr, la, NULL,
8359 msg->elements, old_el,
8360 pdn_list, &change_type);
8361 if (ret != LDB_SUCCESS) {
8362 replmd_txn_cleanup(replmd_private);
8367 * Adding a link reallocs memory, and so invalidates all the
8368 * pointers in pdn_list. Reparse the PDNs on the next loop
8370 if (change_type == LINK_CHANGE_ADDED) {
8371 TALLOC_FREE(pdn_list);
8374 if (change_type != LINK_CHANGE_NONE) {
8378 if ((++replmd_private->num_processed % 8192) == 0) {
8379 DBG_NOTICE("Processed %u/%u linked attributes\n",
8380 replmd_private->num_processed,
8381 replmd_private->total_links);
8386 * it's possible we're already up-to-date and so don't need to modify
8387 * the object at all (e.g. doing a 'drs replicate --full-sync')
8389 if (num_changes == 0) {
8390 TALLOC_FREE(tmp_ctx);
8395 * Note that adding the whenChanged/etc attributes below will realloc
8396 * msg->elements, invalidating the existing element/parsed-DN pointers
8399 TALLOC_FREE(pdn_list);
8401 /* update whenChanged/uSNChanged as the object has changed */
8403 ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
8405 if (ret != LDB_SUCCESS) {
8409 ret = add_time_element(msg, "whenChanged", t);
8410 if (ret != LDB_SUCCESS) {
8415 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8416 if (ret != LDB_SUCCESS) {
8421 /* apply the link changes to the source object */
8422 ret = linked_attr_modify(module, msg, NULL);
8423 if (ret != LDB_SUCCESS) {
8424 ldb_debug(ldb, LDB_DEBUG_WARNING,
8425 "Failed to apply linked attribute change '%s'\n%s\n",
8427 ldb_ldif_message_redacted_string(ldb,
8429 LDB_CHANGETYPE_MODIFY,
8431 TALLOC_FREE(tmp_ctx);
8435 TALLOC_FREE(tmp_ctx);
8440 on prepare commit we loop over our queued la_context structures and
8443 static int replmd_prepare_commit(struct ldb_module *module)
8445 struct replmd_private *replmd_private =
8446 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8447 struct la_group *la_group, *prev;
8450 if (replmd_private->la_list != NULL) {
8451 DBG_NOTICE("Processing linked attributes\n");
8455 * Walk the list of linked attributes from DRS replication.
8457 * We walk backwards, to do the first entry first, as we
8458 * added the entries with DLIST_ADD() which puts them at the
8461 * Links are grouped together so we process links for the same
8462 * source object in one go.
8464 for (la_group = DLIST_TAIL(replmd_private->la_list);
8468 prev = DLIST_PREV(la_group);
8469 DLIST_REMOVE(replmd_private->la_list, la_group);
8470 ret = replmd_process_la_group(module, replmd_private,
8472 if (ret != LDB_SUCCESS) {
8473 replmd_txn_cleanup(replmd_private);
8478 replmd_txn_cleanup(replmd_private);
8480 /* possibly change @REPLCHANGED */
8481 ret = replmd_notify_store(module, NULL);
8482 if (ret != LDB_SUCCESS) {
8486 return ldb_next_prepare_commit(module);
8489 static int replmd_del_transaction(struct ldb_module *module)
8491 struct replmd_private *replmd_private =
8492 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8493 replmd_txn_cleanup(replmd_private);
8495 return ldb_next_del_trans(module);
8499 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8500 .name = "repl_meta_data",
8501 .init_context = replmd_init,
8503 .modify = replmd_modify,
8504 .rename = replmd_rename,
8505 .del = replmd_delete,
8506 .extended = replmd_extended,
8507 .start_transaction = replmd_start_transaction,
8508 .prepare_commit = replmd_prepare_commit,
8509 .del_transaction = replmd_del_transaction,
8512 int ldb_repl_meta_data_module_init(const char *version)
8514 LDB_MODULE_CHECK_VERSION(version);
8515 return ldb_register_module(&ldb_repl_meta_data_module_ops);