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;
1034 * At the successful end of these functions el->values is
1035 * overwritten with new_values. However get_parsed_dns()
1036 * points p->v at the supplied el and it effectively gets used
1037 * as a working area by replmd_build_la_val(). So we must
1038 * duplicate it because our caller only called
1039 * ldb_msg_copy_shallow().
1042 el->values = talloc_memdup(tmp_ctx,
1044 sizeof(el->values[0]) * el->num_values);
1045 if (el->values == NULL) {
1046 ldb_module_oom(module);
1047 talloc_free(tmp_ctx);
1048 return LDB_ERR_OPERATIONS_ERROR;
1051 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1052 sa->syntax->ldap_oid, parent);
1053 if (ret != LDB_SUCCESS) {
1054 talloc_free(tmp_ctx);
1058 ret = check_parsed_dn_duplicates(module, el, pdn);
1059 if (ret != LDB_SUCCESS) {
1060 talloc_free(tmp_ctx);
1064 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1065 if (new_values == NULL) {
1066 ldb_module_oom(module);
1067 talloc_free(tmp_ctx);
1068 return LDB_ERR_OPERATIONS_ERROR;
1071 for (i = 0; i < el->num_values; i++) {
1072 struct parsed_dn *p = &pdn[i];
1073 ret = replmd_build_la_val(new_values, p->v, p->dsdb_dn,
1074 &ac->our_invocation_id,
1076 if (ret != LDB_SUCCESS) {
1077 talloc_free(tmp_ctx);
1081 ret = replmd_defer_add_backlink(module, replmd_private,
1083 forward_dn, &p->guid, true, sa,
1085 if (ret != LDB_SUCCESS) {
1086 talloc_free(tmp_ctx);
1090 new_values[i] = *p->v;
1092 el->values = talloc_steal(mem_ctx, new_values);
1094 talloc_free(tmp_ctx);
1098 static int replmd_add_make_extended_dn(struct ldb_request *req,
1099 const DATA_BLOB *guid_blob,
1100 struct ldb_dn **_extended_dn)
1103 const DATA_BLOB *sid_blob;
1104 /* Calculate an extended DN for any linked attributes */
1105 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1107 return LDB_ERR_OPERATIONS_ERROR;
1109 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1110 if (ret != LDB_SUCCESS) {
1114 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1115 if (sid_blob != NULL) {
1116 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1117 if (ret != LDB_SUCCESS) {
1121 *_extended_dn = extended_dn;
1126 intercept add requests
1128 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1130 struct ldb_context *ldb;
1131 struct ldb_control *control;
1132 struct replmd_replicated_request *ac;
1133 enum ndr_err_code ndr_err;
1134 struct ldb_request *down_req;
1135 struct ldb_message *msg;
1136 const DATA_BLOB *guid_blob;
1137 DATA_BLOB guid_blob_stack;
1139 uint8_t guid_data[16];
1140 struct replPropertyMetaDataBlob nmd;
1141 struct ldb_val nmd_value;
1142 struct ldb_dn *extended_dn = NULL;
1145 * The use of a time_t here seems odd, but as the NTTIME
1146 * elements are actually declared as NTTIME_1sec in the IDL,
1147 * getting a higher resolution timestamp is not required.
1149 time_t t = time(NULL);
1154 unsigned int functional_level;
1156 bool allow_add_guid = false;
1157 bool remove_current_guid = false;
1158 bool is_urgent = false;
1159 bool is_schema_nc = false;
1160 struct ldb_message_element *objectclass_el;
1161 struct replmd_private *replmd_private =
1162 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1164 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1165 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1167 allow_add_guid = true;
1170 /* do not manipulate our control entries */
1171 if (ldb_dn_is_special(req->op.add.message->dn)) {
1172 return ldb_next_request(module, req);
1175 ldb = ldb_module_get_ctx(module);
1177 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1179 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1180 if (guid_blob != NULL) {
1181 if (!allow_add_guid) {
1182 ldb_set_errstring(ldb,
1183 "replmd_add: it's not allowed to add an object with objectGUID!");
1184 return LDB_ERR_UNWILLING_TO_PERFORM;
1186 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1187 if (!NT_STATUS_IS_OK(status)) {
1188 ldb_set_errstring(ldb,
1189 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1190 return LDB_ERR_UNWILLING_TO_PERFORM;
1192 /* we remove this attribute as it can be a string and
1193 * will not be treated correctly and then we will re-add
1194 * it later on in the good format */
1195 remove_current_guid = true;
1199 guid = GUID_random();
1201 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1203 /* This can't fail */
1204 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1205 (ndr_push_flags_fn_t)ndr_push_GUID);
1206 guid_blob = &guid_blob_stack;
1209 ac = replmd_ctx_init(module, req);
1211 return ldb_module_oom(module);
1214 functional_level = dsdb_functional_level(ldb);
1216 /* Get a sequence number from the backend */
1217 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1218 if (ret != LDB_SUCCESS) {
1223 /* we have to copy the message as the caller might have it as a const */
1224 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1228 return LDB_ERR_OPERATIONS_ERROR;
1231 /* generated times */
1232 unix_to_nt_time(&now, t);
1233 time_str = ldb_timestring(msg, t);
1237 return LDB_ERR_OPERATIONS_ERROR;
1239 if (remove_current_guid) {
1240 ldb_msg_remove_attr(msg,"objectGUID");
1244 * remove autogenerated attributes
1246 ldb_msg_remove_attr(msg, "whenCreated");
1247 ldb_msg_remove_attr(msg, "whenChanged");
1248 ldb_msg_remove_attr(msg, "uSNCreated");
1249 ldb_msg_remove_attr(msg, "uSNChanged");
1250 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1253 * readd replicated attributes
1255 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1256 if (ret != LDB_SUCCESS) {
1262 /* build the replication meta_data */
1265 nmd.ctr.ctr1.count = msg->num_elements;
1266 nmd.ctr.ctr1.array = talloc_array(msg,
1267 struct replPropertyMetaData1,
1268 nmd.ctr.ctr1.count);
1269 if (!nmd.ctr.ctr1.array) {
1272 return LDB_ERR_OPERATIONS_ERROR;
1275 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1277 for (i=0; i < msg->num_elements;) {
1278 struct ldb_message_element *e = &msg->elements[i];
1279 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1280 const struct dsdb_attribute *sa;
1282 if (e->name[0] == '@') {
1287 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1289 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1290 "replmd_add: attribute '%s' not defined in schema\n",
1293 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1296 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1297 /* if the attribute is not replicated (0x00000001)
1298 * or constructed (0x00000004) it has no metadata
1304 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1305 if (extended_dn == NULL) {
1306 ret = replmd_add_make_extended_dn(req,
1309 if (ret != LDB_SUCCESS) {
1316 * Prepare the context for the backlinks and
1317 * create metadata for the forward links. The
1318 * backlinks are created in
1319 * replmd_op_callback() after the successful
1320 * ADD of the object.
1322 ret = replmd_add_fix_la(module, msg->elements,
1327 if (ret != LDB_SUCCESS) {
1331 /* linked attributes are not stored in
1332 replPropertyMetaData in FL above w2k */
1337 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1339 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1340 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1343 if (rdn_val == NULL) {
1346 return LDB_ERR_OPERATIONS_ERROR;
1349 rdn = (const char*)rdn_val->data;
1350 if (strcmp(rdn, "Deleted Objects") == 0) {
1352 * Set the originating_change_time to 29/12/9999 at 23:59:59
1353 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1355 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1357 m->originating_change_time = now;
1360 m->originating_change_time = now;
1362 m->originating_invocation_id = ac->our_invocation_id;
1363 m->originating_usn = ac->seq_num;
1364 m->local_usn = ac->seq_num;
1367 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1372 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1374 if (e->num_values != 0) {
1379 ldb_msg_remove_element(msg, e);
1382 /* fix meta data count */
1383 nmd.ctr.ctr1.count = ni;
1386 * sort meta data array
1388 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1389 if (ret != LDB_SUCCESS) {
1390 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1395 /* generated NDR encoded values */
1396 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1398 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1399 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1402 return LDB_ERR_OPERATIONS_ERROR;
1406 * add the autogenerated values
1408 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1409 if (ret != LDB_SUCCESS) {
1414 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1415 if (ret != LDB_SUCCESS) {
1420 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1421 if (ret != LDB_SUCCESS) {
1426 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1427 if (ret != LDB_SUCCESS) {
1432 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1433 if (ret != LDB_SUCCESS) {
1440 * sort the attributes by attid before storing the object
1442 replmd_ldb_message_sort(msg, ac->schema);
1445 * Assert that we do have an objectClass
1447 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1448 if (objectclass_el == NULL) {
1449 ldb_asprintf_errstring(ldb, __location__
1450 ": objectClass missing on %s\n",
1451 ldb_dn_get_linearized(msg->dn));
1453 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1455 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1456 REPL_URGENT_ON_CREATE);
1458 ac->is_urgent = is_urgent;
1459 ret = ldb_build_add_req(&down_req, ldb, ac,
1462 ac, replmd_op_callback,
1465 LDB_REQ_SET_LOCATION(down_req);
1466 if (ret != LDB_SUCCESS) {
1471 /* current partition control is needed by "replmd_op_callback" */
1472 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1473 ret = ldb_request_add_control(down_req,
1474 DSDB_CONTROL_CURRENT_PARTITION_OID,
1476 if (ret != LDB_SUCCESS) {
1482 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1483 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1484 if (ret != LDB_SUCCESS) {
1490 /* mark the control done */
1492 control->critical = 0;
1494 /* go on with the call chain */
1495 return ldb_next_request(module, down_req);
1500 * update the replPropertyMetaData for one element
1502 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1503 struct ldb_message *msg,
1504 struct ldb_message_element *el,
1505 struct ldb_message_element *old_el,
1506 struct replPropertyMetaDataBlob *omd,
1507 const struct dsdb_schema *schema,
1509 const struct GUID *our_invocation_id,
1512 bool is_forced_rodc,
1513 struct ldb_request *req)
1516 const struct dsdb_attribute *a;
1517 struct replPropertyMetaData1 *md1;
1518 bool may_skip = false;
1521 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1523 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1524 /* allow this to make it possible for dbcheck
1525 to remove bad attributes */
1529 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1531 return LDB_ERR_OPERATIONS_ERROR;
1534 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1536 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1541 * if the attribute's value haven't changed, and this isn't
1542 * just a delete of everything then return LDB_SUCCESS Unless
1543 * we have the provision control or if the attribute is
1544 * interSiteTopologyGenerator as this page explain:
1545 * http://support.microsoft.com/kb/224815 this attribute is
1546 * periodicaly written by the DC responsible for the intersite
1547 * generation in a given site
1549 * Unchanged could be deleting or replacing an already-gone
1550 * thing with an unconstrained delete/empty replace or a
1551 * replace with the same value, but not an add with the same
1552 * value because that could be about adding a duplicate (which
1553 * is for someone else to error out on).
1555 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1556 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1559 } else if (old_el == NULL && el->num_values == 0) {
1560 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1562 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1565 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1566 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1568 * We intentionally skip the version bump when attempting to
1571 * The control is set by dbcheck and expunge-tombstones which
1572 * both attempt to be non-replicating. Otherwise, making an
1573 * alteration to the replication state would trigger a
1574 * broadcast of all expunged objects.
1579 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1581 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1585 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1586 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1588 * allow this to make it possible for dbcheck
1589 * to rebuild broken metadata
1595 for (i=0; i<omd->ctr.ctr1.count; i++) {
1597 * First check if we find it under the msDS-IntID,
1598 * then check if we find it under the OID and
1601 * This allows the administrator to simply re-write
1602 * the attributes and so restore replication, which is
1603 * likely what they will try to do.
1605 if (attid == omd->ctr.ctr1.array[i].attid) {
1609 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1614 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1615 /* linked attributes are not stored in
1616 replPropertyMetaData in FL above w2k, but we do
1617 raise the seqnum for the object */
1618 if (*seq_num == 0 &&
1619 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1620 return LDB_ERR_OPERATIONS_ERROR;
1625 if (i == omd->ctr.ctr1.count) {
1626 /* we need to add a new one */
1627 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1628 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1629 if (omd->ctr.ctr1.array == NULL) {
1631 return LDB_ERR_OPERATIONS_ERROR;
1633 omd->ctr.ctr1.count++;
1634 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1637 /* Get a new sequence number from the backend. We only do this
1638 * if we have a change that requires a new
1639 * replPropertyMetaData element
1641 if (*seq_num == 0) {
1642 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1643 if (ret != LDB_SUCCESS) {
1644 return LDB_ERR_OPERATIONS_ERROR;
1648 md1 = &omd->ctr.ctr1.array[i];
1652 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1653 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1656 if (rdn_val == NULL) {
1658 return LDB_ERR_OPERATIONS_ERROR;
1661 rdn = (const char*)rdn_val->data;
1662 if (strcmp(rdn, "Deleted Objects") == 0) {
1664 * Set the originating_change_time to 29/12/9999 at 23:59:59
1665 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1667 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1669 md1->originating_change_time = now;
1672 md1->originating_change_time = now;
1674 md1->originating_invocation_id = *our_invocation_id;
1675 md1->originating_usn = *seq_num;
1676 md1->local_usn = *seq_num;
1678 if (is_forced_rodc) {
1679 /* Force version to 0 to be overriden later via replication */
1687 * Bump the replPropertyMetaData version on an attribute, and if it
1688 * has changed (or forced by leaving rdn_old NULL), update the value
1691 * This is important, as calling a modify operation may not change the
1692 * version number if the values appear unchanged, but a rename between
1693 * parents bumps this value.
1696 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1697 struct ldb_message *msg,
1698 const struct ldb_val *rdn_new,
1699 const struct ldb_val *rdn_old,
1700 struct replPropertyMetaDataBlob *omd,
1701 struct replmd_replicated_request *ar,
1704 bool is_forced_rodc)
1706 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1707 const struct dsdb_attribute *rdn_attr =
1708 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1709 const char *attr_name = rdn_attr != NULL ?
1710 rdn_attr->lDAPDisplayName :
1712 struct ldb_message_element new_el = {
1713 .flags = LDB_FLAG_MOD_REPLACE,
1716 .values = discard_const_p(struct ldb_val, rdn_new)
1718 struct ldb_message_element old_el = {
1719 .flags = LDB_FLAG_MOD_REPLACE,
1721 .num_values = rdn_old ? 1 : 0,
1722 .values = discard_const_p(struct ldb_val, rdn_old)
1725 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1726 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1727 if (ret != LDB_SUCCESS) {
1728 return ldb_oom(ldb);
1732 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1733 omd, ar->schema, &ar->seq_num,
1734 &ar->our_invocation_id,
1735 now, is_schema_nc, is_forced_rodc,
1740 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1742 uint32_t count = omd.ctr.ctr1.count;
1745 for (i=0; i < count; i++) {
1746 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1747 if (max < m.local_usn) {
1755 * update the replPropertyMetaData object each time we modify an
1756 * object. This is needed for DRS replication, as the merge on the
1757 * client is based on this object
1759 static int replmd_update_rpmd(struct ldb_module *module,
1760 const struct dsdb_schema *schema,
1761 struct ldb_request *req,
1762 const char * const *rename_attrs,
1763 struct ldb_message *msg, uint64_t *seq_num,
1764 time_t t, bool is_schema_nc,
1765 bool *is_urgent, bool *rodc)
1767 const struct ldb_val *omd_value;
1768 enum ndr_err_code ndr_err;
1769 struct replPropertyMetaDataBlob omd;
1772 const struct GUID *our_invocation_id;
1774 const char * const *attrs = NULL;
1775 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1776 struct ldb_result *res;
1777 struct ldb_context *ldb;
1778 struct ldb_message_element *objectclass_el;
1779 enum urgent_situation situation;
1780 bool rmd_is_provided;
1781 bool rmd_is_just_resorted = false;
1782 const char *not_rename_attrs[4 + msg->num_elements];
1783 bool is_forced_rodc = false;
1786 attrs = rename_attrs;
1788 for (i = 0; i < msg->num_elements; i++) {
1789 not_rename_attrs[i] = msg->elements[i].name;
1791 not_rename_attrs[i] = "replPropertyMetaData";
1792 not_rename_attrs[i+1] = "objectClass";
1793 not_rename_attrs[i+2] = "instanceType";
1794 not_rename_attrs[i+3] = NULL;
1795 attrs = not_rename_attrs;
1798 ldb = ldb_module_get_ctx(module);
1800 ret = samdb_rodc(ldb, rodc);
1801 if (ret != LDB_SUCCESS) {
1802 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1807 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1808 is_forced_rodc = true;
1811 our_invocation_id = samdb_ntds_invocation_id(ldb);
1812 if (!our_invocation_id) {
1813 /* this happens during an initial vampire while
1814 updating the schema */
1815 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1819 unix_to_nt_time(&now, t);
1821 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1822 rmd_is_provided = true;
1823 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1824 rmd_is_just_resorted = true;
1827 rmd_is_provided = false;
1830 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1831 * otherwise we consider we are updating */
1832 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1833 situation = REPL_URGENT_ON_DELETE;
1834 } else if (rename_attrs) {
1835 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1837 situation = REPL_URGENT_ON_UPDATE;
1840 if (rmd_is_provided) {
1841 /* In this case the change_replmetadata control was supplied */
1842 /* We check that it's the only attribute that is provided
1843 * (it's a rare case so it's better to keep the code simplier)
1844 * We also check that the highest local_usn is bigger or the same as
1847 if( msg->num_elements != 1 ||
1848 strncmp(msg->elements[0].name,
1849 "replPropertyMetaData", 20) ) {
1850 DEBUG(0,(__location__ ": changereplmetada control called without "\
1851 "a specified replPropertyMetaData attribute or with others\n"));
1852 return LDB_ERR_OPERATIONS_ERROR;
1854 if (situation != REPL_URGENT_ON_UPDATE) {
1855 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1856 return LDB_ERR_OPERATIONS_ERROR;
1858 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1860 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1861 ldb_dn_get_linearized(msg->dn)));
1862 return LDB_ERR_OPERATIONS_ERROR;
1864 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1865 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1866 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1867 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1868 ldb_dn_get_linearized(msg->dn)));
1869 return LDB_ERR_OPERATIONS_ERROR;
1872 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1873 DSDB_FLAG_NEXT_MODULE |
1874 DSDB_SEARCH_SHOW_RECYCLED |
1875 DSDB_SEARCH_SHOW_EXTENDED_DN |
1876 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1877 DSDB_SEARCH_REVEAL_INTERNALS, req);
1879 if (ret != LDB_SUCCESS) {
1883 if (rmd_is_just_resorted == false) {
1884 *seq_num = find_max_local_usn(omd);
1886 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1889 * The test here now allows for a new
1890 * replPropertyMetaData with no change, if was
1891 * just dbcheck re-sorting the values.
1893 if (*seq_num <= db_seq) {
1894 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1895 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1896 (long long)*seq_num, (long long)db_seq));
1897 return LDB_ERR_OPERATIONS_ERROR;
1902 /* search for the existing replPropertyMetaDataBlob. We need
1903 * to use REVEAL and ask for DNs in storage format to support
1904 * the check for values being the same in
1905 * replmd_update_rpmd_element()
1907 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1908 DSDB_FLAG_NEXT_MODULE |
1909 DSDB_SEARCH_SHOW_RECYCLED |
1910 DSDB_SEARCH_SHOW_EXTENDED_DN |
1911 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1912 DSDB_SEARCH_REVEAL_INTERNALS, req);
1913 if (ret != LDB_SUCCESS) {
1917 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1919 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1920 ldb_dn_get_linearized(msg->dn)));
1921 return LDB_ERR_OPERATIONS_ERROR;
1924 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1925 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1926 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1927 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1928 ldb_dn_get_linearized(msg->dn)));
1929 return LDB_ERR_OPERATIONS_ERROR;
1932 if (omd.version != 1) {
1933 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1934 omd.version, ldb_dn_get_linearized(msg->dn)));
1935 return LDB_ERR_OPERATIONS_ERROR;
1938 for (i=0; i<msg->num_elements;) {
1939 struct ldb_message_element *el = &msg->elements[i];
1940 struct ldb_message_element *old_el;
1942 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1943 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1944 &omd, schema, seq_num,
1949 if (ret != LDB_SUCCESS) {
1953 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1954 *is_urgent = replmd_check_urgent_attribute(el);
1957 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1962 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1964 if (el->num_values != 0) {
1969 ldb_msg_remove_element(msg, el);
1974 * Assert that we have an objectClass attribute - this is major
1975 * corruption if we don't have this!
1977 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1978 if (objectclass_el != NULL) {
1980 * Now check if this objectClass means we need to do urgent replication
1982 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1986 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1987 ldb_asprintf_errstring(ldb, __location__
1988 ": objectClass missing on %s\n",
1989 ldb_dn_get_linearized(msg->dn));
1990 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1994 * replmd_update_rpmd_element has done an update if the
1997 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1998 struct ldb_val *md_value;
1999 struct ldb_message_element *el;
2001 /*if we are RODC and this is a DRSR update then its ok*/
2002 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
2003 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
2004 && !is_forced_rodc) {
2005 unsigned instanceType;
2008 ldb_set_errstring(ldb, "RODC modify is forbidden!");
2009 return LDB_ERR_REFERRAL;
2012 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
2013 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
2014 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
2015 "cannot change replicated attribute on partial replica");
2019 md_value = talloc(msg, struct ldb_val);
2020 if (md_value == NULL) {
2022 return LDB_ERR_OPERATIONS_ERROR;
2025 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2026 if (ret != LDB_SUCCESS) {
2027 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2031 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2032 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2033 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2034 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2035 ldb_dn_get_linearized(msg->dn)));
2036 return LDB_ERR_OPERATIONS_ERROR;
2039 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2040 if (ret != LDB_SUCCESS) {
2041 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2042 ldb_dn_get_linearized(msg->dn)));
2047 el->values = md_value;
2053 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2055 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2057 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2058 &pdn2->dsdb_dn->extra_part);
2064 get a series of message element values as an array of DNs and GUIDs
2065 the result is sorted by GUID
2067 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2068 struct ldb_message_element *el, struct parsed_dn **pdn,
2069 const char *ldap_oid, struct ldb_request *parent)
2072 bool values_are_sorted = true;
2073 struct ldb_context *ldb = ldb_module_get_ctx(module);
2080 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2082 ldb_module_oom(module);
2083 return LDB_ERR_OPERATIONS_ERROR;
2086 for (i=0; i<el->num_values; i++) {
2087 struct ldb_val *v = &el->values[i];
2090 struct parsed_dn *p;
2094 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2095 if (p->dsdb_dn == NULL) {
2096 return LDB_ERR_INVALID_DN_SYNTAX;
2099 dn = p->dsdb_dn->dn;
2101 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2102 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2103 unlikely(GUID_all_zero(&p->guid))) {
2104 /* we got a DN without a GUID - go find the GUID */
2105 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2106 if (ret != LDB_SUCCESS) {
2107 char *dn_str = NULL;
2108 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2110 ldb_asprintf_errstring(ldb,
2111 "Unable to find GUID for DN %s\n",
2113 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2114 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2115 ldb_attr_cmp(el->name, "member") == 0) {
2116 return LDB_ERR_UNWILLING_TO_PERFORM;
2120 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2121 if (ret != LDB_SUCCESS) {
2124 } else if (!NT_STATUS_IS_OK(status)) {
2125 return LDB_ERR_OPERATIONS_ERROR;
2127 if (i > 0 && values_are_sorted) {
2128 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2130 values_are_sorted = false;
2133 /* keep a pointer to the original ldb_val */
2136 if (! values_are_sorted) {
2137 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2143 * Get a series of trusted message element values. The result is sorted by
2144 * GUID, even though the GUIDs might not be known. That works because we trust
2145 * the database to give us the elements like that if the
2146 * replmd_private->sorted_links flag is set.
2148 * We also ensure that the links are in the Functional Level 2003
2149 * linked attributes format.
2151 static int get_parsed_dns_trusted_fallback(struct ldb_module *module,
2152 struct replmd_private *replmd_private,
2153 TALLOC_CTX *mem_ctx,
2154 struct ldb_message_element *el,
2155 struct parsed_dn **pdn,
2156 const char *ldap_oid,
2157 struct ldb_request *parent)
2165 if (!replmd_private->sorted_links) {
2166 /* We need to sort the list. This is the slow old path we want
2169 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2171 if (ret != LDB_SUCCESS) {
2175 ret = get_parsed_dns_trusted(mem_ctx, el, pdn);
2176 if (ret != LDB_SUCCESS) {
2177 ldb_module_oom(module);
2178 return LDB_ERR_OPERATIONS_ERROR;
2183 * This upgrades links to FL2003 style, and sorts the result
2184 * if that was needed.
2186 * TODO: Add a database feature that asserts we have no FL2000
2187 * style links to avoid this check or add a feature that
2188 * uses a similar check to find sorted/unsorted links
2189 * for an on-the-fly upgrade.
2192 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2193 *pdn, el->num_values,
2196 if (ret != LDB_SUCCESS) {
2204 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2205 otherwise an error code. For compatibility the error code differs depending
2206 on whether or not the attribute is "member".
2208 As always, the parsed_dn list is assumed to be sorted.
2210 static int check_parsed_dn_duplicates(struct ldb_module *module,
2211 struct ldb_message_element *el,
2212 struct parsed_dn *pdn)
2215 struct ldb_context *ldb = ldb_module_get_ctx(module);
2217 for (i = 1; i < el->num_values; i++) {
2218 struct parsed_dn *p = &pdn[i];
2219 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2220 ldb_asprintf_errstring(ldb,
2221 "Linked attribute %s has "
2222 "multiple identical values",
2224 if (ldb_attr_cmp(el->name, "member") == 0) {
2225 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2227 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2235 build a new extended DN, including all meta data fields
2237 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2238 RMD_ADDTIME = originating_add_time
2239 RMD_INVOCID = originating_invocation_id
2240 RMD_CHANGETIME = originating_change_time
2241 RMD_ORIGINATING_USN = originating_usn
2242 RMD_LOCAL_USN = local_usn
2243 RMD_VERSION = version
2245 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2246 struct dsdb_dn *dsdb_dn,
2247 const struct GUID *invocation_id,
2248 uint64_t local_usn, NTTIME nttime)
2250 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2251 local_usn, local_usn, nttime,
2252 RMD_VERSION_INITIAL, false);
2255 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2256 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2257 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2261 check if any links need upgrading from w2k format
2263 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2264 struct parsed_dn *dns, uint32_t count,
2265 struct ldb_message_element *el,
2266 const char *ldap_oid)
2269 const struct GUID *invocation_id = NULL;
2270 for (i=0; i<count; i++) {
2274 if (dns[i].dsdb_dn == NULL) {
2275 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2277 if (ret != LDB_SUCCESS) {
2278 return LDB_ERR_INVALID_DN_SYNTAX;
2282 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2283 &version, "RMD_VERSION");
2284 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2286 * We optimistically assume they are all the same; if
2287 * the first one is fixed, they are all fixed.
2289 * If the first one was *not* fixed and we find a
2290 * later one that is, that is an occasion to shout
2296 DEBUG(0, ("Mixed w2k and fixed format "
2297 "linked attributes\n"));
2301 if (invocation_id == NULL) {
2302 invocation_id = samdb_ntds_invocation_id(ldb);
2303 if (invocation_id == NULL) {
2304 return LDB_ERR_OPERATIONS_ERROR;
2309 /* it's an old one that needs upgrading */
2310 ret = replmd_update_la_val(el->values, dns[i].v,
2311 dns[i].dsdb_dn, dns[i].dsdb_dn,
2312 invocation_id, 1, 1, 0, false);
2313 if (ret != LDB_SUCCESS) {
2319 * This sort() is critical for the operation of
2320 * get_parsed_dns_trusted_fallback() because callers of this function
2321 * expect a sorted list, and FL2000 style links are not
2322 * sorted. In particular, as well as the upgrade case,
2323 * get_parsed_dns_trusted_fallback() is called from
2324 * replmd_delete_remove_link() even in FL2000 mode
2326 * We do not normally pay the cost of the qsort() due to the
2327 * early return in the RMD_VERSION found case.
2329 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2334 Sets the value for a linked attribute, including all meta data fields
2336 see replmd_build_la_val for value names
2338 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2339 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2340 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2341 uint32_t version, bool deleted)
2343 struct ldb_dn *dn = dsdb_dn->dn;
2344 const char *tstring, *usn_string, *flags_string;
2345 struct ldb_val tval;
2347 struct ldb_val usnv, local_usnv;
2348 struct ldb_val vers, flagsv;
2349 const struct ldb_val *old_addtime = NULL;
2352 const char *dnstring;
2354 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2356 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2358 return LDB_ERR_OPERATIONS_ERROR;
2360 tval = data_blob_string_const(tstring);
2362 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2364 return LDB_ERR_OPERATIONS_ERROR;
2366 usnv = data_blob_string_const(usn_string);
2368 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2370 return LDB_ERR_OPERATIONS_ERROR;
2372 local_usnv = data_blob_string_const(usn_string);
2374 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2375 if (!NT_STATUS_IS_OK(status)) {
2376 return LDB_ERR_OPERATIONS_ERROR;
2379 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2380 if (!flags_string) {
2381 return LDB_ERR_OPERATIONS_ERROR;
2383 flagsv = data_blob_string_const(flags_string);
2385 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2386 if (ret != LDB_SUCCESS) return ret;
2388 /* get the ADDTIME from the original */
2389 if (old_dsdb_dn != NULL) {
2390 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2393 if (old_addtime == NULL) {
2394 old_addtime = &tval;
2396 if (dsdb_dn != old_dsdb_dn ||
2397 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2398 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2399 if (ret != LDB_SUCCESS) return ret;
2402 /* use our invocation id */
2403 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2404 if (ret != LDB_SUCCESS) return ret;
2406 /* changetime is the current time */
2407 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2408 if (ret != LDB_SUCCESS) return ret;
2410 /* update the USN */
2411 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2412 if (ret != LDB_SUCCESS) return ret;
2414 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2415 if (ret != LDB_SUCCESS) return ret;
2417 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2418 vers = data_blob_string_const(vstring);
2419 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2420 if (ret != LDB_SUCCESS) return ret;
2422 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2423 if (dnstring == NULL) {
2424 return LDB_ERR_OPERATIONS_ERROR;
2426 *v = data_blob_string_const(dnstring);
2432 * Updates the value for a linked attribute, including all meta data fields
2434 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2435 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2436 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2439 uint32_t old_version;
2440 uint32_t version = RMD_VERSION_INITIAL;
2444 * We're updating the linked attribute locally, so increase the version
2445 * by 1 so that other DCs will see the change when it gets replicated out
2447 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2450 if (NT_STATUS_IS_OK(status)) {
2451 version = old_version + 1;
2454 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2455 usn, local_usn, nttime, version, deleted);
2459 handle adding a linked attribute
2461 static int replmd_modify_la_add(struct ldb_module *module,
2462 struct replmd_private *replmd_private,
2463 struct replmd_replicated_request *ac,
2464 struct ldb_message *msg,
2465 struct ldb_message_element *el,
2466 struct ldb_message_element *old_el,
2467 const struct dsdb_attribute *schema_attr,
2469 struct ldb_dn *msg_dn,
2470 struct ldb_request *parent)
2473 struct parsed_dn *dns, *old_dns;
2474 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2476 struct ldb_val *new_values = NULL;
2477 unsigned old_num_values = old_el ? old_el->num_values : 0;
2478 unsigned num_values = 0;
2479 unsigned max_num_values;
2480 struct ldb_context *ldb = ldb_module_get_ctx(module);
2482 unix_to_nt_time(&now, t);
2484 /* get the DNs to be added, fully parsed.
2486 * We need full parsing because they came off the wire and we don't
2487 * trust them, besides which we need their details to know where to put
2490 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2491 schema_attr->syntax->ldap_oid, parent);
2492 if (ret != LDB_SUCCESS) {
2493 talloc_free(tmp_ctx);
2497 /* get the existing DNs, lazily parsed */
2498 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2499 tmp_ctx, old_el, &old_dns,
2500 schema_attr->syntax->ldap_oid,
2503 if (ret != LDB_SUCCESS) {
2504 talloc_free(tmp_ctx);
2508 max_num_values = old_num_values + el->num_values;
2509 if (max_num_values < old_num_values) {
2510 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2511 "old values: %u, new values: %u, sum: %u\n",
2512 old_num_values, el->num_values, max_num_values));
2513 talloc_free(tmp_ctx);
2514 return LDB_ERR_OPERATIONS_ERROR;
2517 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2519 if (new_values == NULL) {
2520 ldb_module_oom(module);
2521 talloc_free(tmp_ctx);
2522 return LDB_ERR_OPERATIONS_ERROR;
2526 * For each new value, find where it would go in the list. If there is
2527 * a matching GUID there, we update the existing value; otherwise we
2531 for (i = 0; i < el->num_values; i++) {
2532 struct parsed_dn *exact;
2533 struct parsed_dn *next;
2535 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2538 dns[i].dsdb_dn->extra_part, 0,
2540 schema_attr->syntax->ldap_oid,
2542 if (err != LDB_SUCCESS) {
2543 talloc_free(tmp_ctx);
2547 if (ac->fix_link_sid) {
2548 char *fixed_dnstring = NULL;
2549 struct dom_sid tmp_sid = { 0, };
2550 DATA_BLOB sid_blob = data_blob_null;
2551 enum ndr_err_code ndr_err;
2555 if (exact == NULL) {
2556 talloc_free(tmp_ctx);
2557 return ldb_operr(ldb);
2560 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2561 talloc_free(tmp_ctx);
2562 return ldb_operr(ldb);
2566 * Only "<GUID=...><SID=...>" is allowed.
2568 * We get the GUID to just to find the old
2569 * value and the SID in order to add it
2570 * to the found value.
2573 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2575 talloc_free(tmp_ctx);
2576 return ldb_operr(ldb);
2579 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2581 talloc_free(tmp_ctx);
2582 return ldb_operr(ldb);
2585 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2587 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2588 /* this is what we expect */
2589 } else if (NT_STATUS_IS_OK(status)) {
2590 struct GUID_txt_buf guid_str;
2591 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2592 "i[%u] SID NOT MISSING... Attribute %s already "
2593 "exists for target GUID %s, SID %s, DN: %s",
2595 GUID_buf_string(&exact->guid,
2597 dom_sid_string(tmp_ctx, &tmp_sid),
2598 dsdb_dn_get_extended_linearized(tmp_ctx,
2599 exact->dsdb_dn, 1));
2600 talloc_free(tmp_ctx);
2601 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2603 talloc_free(tmp_ctx);
2604 return ldb_operr(ldb);
2607 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2609 if (!NT_STATUS_IS_OK(status)) {
2610 struct GUID_txt_buf guid_str;
2611 ldb_asprintf_errstring(ldb,
2612 "NO SID PROVIDED... Attribute %s already "
2613 "exists for target GUID %s",
2615 GUID_buf_string(&exact->guid,
2617 talloc_free(tmp_ctx);
2618 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2621 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2622 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2623 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2624 talloc_free(tmp_ctx);
2625 return ldb_operr(ldb);
2628 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2629 data_blob_free(&sid_blob);
2630 if (ret != LDB_SUCCESS) {
2631 talloc_free(tmp_ctx);
2635 fixed_dnstring = dsdb_dn_get_extended_linearized(
2636 new_values, exact->dsdb_dn, 1);
2637 if (fixed_dnstring == NULL) {
2638 talloc_free(tmp_ctx);
2639 return ldb_operr(ldb);
2643 * We just replace the existing value...
2645 *exact->v = data_blob_string_const(fixed_dnstring);
2650 if (exact != NULL) {
2652 * We are trying to add one that exists, which is only
2653 * allowed if it was previously deleted.
2655 * When we do undelete a link we change it in place.
2656 * It will be copied across into the right spot in due
2660 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2662 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2663 struct GUID_txt_buf guid_str;
2664 ldb_asprintf_errstring(ldb,
2665 "Attribute %s already "
2666 "exists for target GUID %s",
2668 GUID_buf_string(&exact->guid,
2670 talloc_free(tmp_ctx);
2671 /* error codes for 'member' need to be
2673 if (ldb_attr_cmp(el->name, "member") == 0) {
2674 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2676 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2680 ret = replmd_update_la_val(new_values, exact->v,
2683 &ac->our_invocation_id,
2684 ac->seq_num, ac->seq_num,
2686 if (ret != LDB_SUCCESS) {
2687 talloc_free(tmp_ctx);
2691 ret = replmd_add_backlink(module, replmd_private,
2698 if (ret != LDB_SUCCESS) {
2699 talloc_free(tmp_ctx);
2705 * Here we don't have an exact match.
2707 * If next is NULL, this one goes beyond the end of the
2708 * existing list, so we need to add all of those ones first.
2710 * If next is not NULL, we need to add all the ones before
2714 offset = old_num_values;
2716 /* next should have been parsed, but let's make sure */
2717 if (next->dsdb_dn == NULL) {
2718 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2719 schema_attr->syntax->ldap_oid);
2720 if (ret != LDB_SUCCESS) {
2724 offset = MIN(next - old_dns, old_num_values);
2727 /* put all the old ones before next on the list */
2728 for (; j < offset; j++) {
2729 new_values[num_values] = *old_dns[j].v;
2733 ret = replmd_add_backlink(module, replmd_private,
2738 /* Make the new linked attribute ldb_val. */
2739 ret = replmd_build_la_val(new_values, &new_values[num_values],
2740 dns[i].dsdb_dn, &ac->our_invocation_id,
2742 if (ret != LDB_SUCCESS) {
2743 talloc_free(tmp_ctx);
2747 if (ret != LDB_SUCCESS) {
2748 talloc_free(tmp_ctx);
2752 /* copy the rest of the old ones (if any) */
2753 for (; j < old_num_values; j++) {
2754 new_values[num_values] = *old_dns[j].v;
2758 talloc_steal(msg->elements, new_values);
2759 if (old_el != NULL) {
2760 talloc_steal(msg->elements, old_el->values);
2762 el->values = new_values;
2763 el->num_values = num_values;
2765 talloc_free(tmp_ctx);
2767 /* we now tell the backend to replace all existing values
2768 with the one we have constructed */
2769 el->flags = LDB_FLAG_MOD_REPLACE;
2776 handle deleting all active linked attributes
2778 static int replmd_modify_la_delete(struct ldb_module *module,
2779 struct replmd_private *replmd_private,
2780 struct replmd_replicated_request *ac,
2781 struct ldb_message *msg,
2782 struct ldb_message_element *el,
2783 struct ldb_message_element *old_el,
2784 const struct dsdb_attribute *schema_attr,
2786 struct ldb_dn *msg_dn,
2787 struct ldb_request *parent)
2790 struct parsed_dn *dns, *old_dns;
2791 TALLOC_CTX *tmp_ctx = NULL;
2793 struct ldb_context *ldb = ldb_module_get_ctx(module);
2794 struct ldb_control *vanish_links_ctrl = NULL;
2795 bool vanish_links = false;
2796 unsigned int num_to_delete = el->num_values;
2800 unix_to_nt_time(&now, t);
2802 if (old_el == NULL || old_el->num_values == 0) {
2803 /* there is nothing to delete... */
2804 if (num_to_delete == 0) {
2805 /* and we're deleting nothing, so that's OK */
2808 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2811 tmp_ctx = talloc_new(msg);
2812 if (tmp_ctx == NULL) {
2813 return LDB_ERR_OPERATIONS_ERROR;
2816 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2817 schema_attr->syntax->ldap_oid, parent);
2818 if (ret != LDB_SUCCESS) {
2819 talloc_free(tmp_ctx);
2823 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2824 tmp_ctx, old_el, &old_dns,
2825 schema_attr->syntax->ldap_oid,
2828 if (ret != LDB_SUCCESS) {
2829 talloc_free(tmp_ctx);
2834 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2835 if (vanish_links_ctrl) {
2836 vanish_links = true;
2837 vanish_links_ctrl->critical = false;
2841 /* we empty out el->values here to avoid damage if we return early. */
2846 * If vanish links is set, we are actually removing members of
2847 * old_el->values; otherwise we are just marking them deleted.
2849 * There is a special case when no values are given: we remove them
2850 * all. When we have the vanish_links control we just have to remove
2851 * the backlinks and change our element to replace the existing values
2852 * with the empty list.
2855 if (num_to_delete == 0) {
2856 for (i = 0; i < old_el->num_values; i++) {
2857 struct parsed_dn *p = &old_dns[i];
2858 if (p->dsdb_dn == NULL) {
2859 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2860 schema_attr->syntax->ldap_oid);
2861 if (ret != LDB_SUCCESS) {
2865 ret = replmd_add_backlink(module, replmd_private,
2866 ac->schema, msg_dn, &p->guid,
2869 if (ret != LDB_SUCCESS) {
2870 talloc_free(tmp_ctx);
2877 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2878 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2882 ret = replmd_update_la_val(old_el->values, p->v,
2883 p->dsdb_dn, p->dsdb_dn,
2884 &ac->our_invocation_id,
2885 ac->seq_num, ac->seq_num,
2887 if (ret != LDB_SUCCESS) {
2888 talloc_free(tmp_ctx);
2894 el->flags = LDB_FLAG_MOD_REPLACE;
2895 talloc_free(tmp_ctx);
2901 for (i = 0; i < num_to_delete; i++) {
2902 struct parsed_dn *p = &dns[i];
2903 struct parsed_dn *exact = NULL;
2904 struct parsed_dn *next = NULL;
2905 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2908 p->dsdb_dn->extra_part, 0,
2910 schema_attr->syntax->ldap_oid,
2912 if (ret != LDB_SUCCESS) {
2913 talloc_free(tmp_ctx);
2916 if (exact == NULL) {
2917 struct GUID_txt_buf buf;
2918 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2919 "exist for target GUID %s",
2921 GUID_buf_string(&p->guid, &buf));
2922 if (ldb_attr_cmp(el->name, "member") == 0) {
2923 talloc_free(tmp_ctx);
2924 return LDB_ERR_UNWILLING_TO_PERFORM;
2926 talloc_free(tmp_ctx);
2927 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2932 if (CHECK_DEBUGLVL(5)) {
2933 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2934 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2935 struct GUID_txt_buf buf;
2936 const char *guid_str = \
2937 GUID_buf_string(&p->guid, &buf);
2938 DEBUG(5, ("Deleting deleted linked "
2939 "attribute %s to %s, because "
2940 "vanish_links control is set\n",
2941 el->name, guid_str));
2945 /* remove the backlink */
2946 ret = replmd_add_backlink(module,
2953 if (ret != LDB_SUCCESS) {
2954 talloc_free(tmp_ctx);
2958 /* We flag the deletion and tidy it up later. */
2963 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2965 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2966 struct GUID_txt_buf buf;
2967 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2968 ldb_asprintf_errstring(ldb, "Attribute %s already "
2969 "deleted for target GUID %s",
2970 el->name, guid_str);
2971 if (ldb_attr_cmp(el->name, "member") == 0) {
2972 talloc_free(tmp_ctx);
2973 return LDB_ERR_UNWILLING_TO_PERFORM;
2975 talloc_free(tmp_ctx);
2976 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2980 ret = replmd_update_la_val(old_el->values, exact->v,
2981 exact->dsdb_dn, exact->dsdb_dn,
2982 &ac->our_invocation_id,
2983 ac->seq_num, ac->seq_num,
2985 if (ret != LDB_SUCCESS) {
2986 talloc_free(tmp_ctx);
2989 ret = replmd_add_backlink(module, replmd_private,
2994 if (ret != LDB_SUCCESS) {
2995 talloc_free(tmp_ctx);
3002 struct ldb_val *tmp_vals = NULL;
3004 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
3005 old_el->num_values);
3006 if (tmp_vals == NULL) {
3007 talloc_free(tmp_ctx);
3008 return ldb_module_oom(module);
3010 for (i = 0; i < old_el->num_values; i++) {
3011 if (old_dns[i].v == NULL) {
3014 tmp_vals[j] = *old_dns[i].v;
3017 for (i = 0; i < j; i++) {
3018 old_el->values[i] = tmp_vals[i];
3020 old_el->num_values = j;
3023 el->values = talloc_steal(msg->elements, old_el->values);
3024 el->num_values = old_el->num_values;
3026 talloc_free(tmp_ctx);
3028 /* we now tell the backend to replace all existing values
3029 with the one we have constructed */
3030 el->flags = LDB_FLAG_MOD_REPLACE;
3036 handle replacing a linked attribute
3038 static int replmd_modify_la_replace(struct ldb_module *module,
3039 struct replmd_private *replmd_private,
3040 struct replmd_replicated_request *ac,
3041 struct ldb_message *msg,
3042 struct ldb_message_element *el,
3043 struct ldb_message_element *old_el,
3044 const struct dsdb_attribute *schema_attr,
3046 struct ldb_dn *msg_dn,
3047 struct ldb_request *parent)
3049 unsigned int i, old_i, new_i;
3050 struct parsed_dn *dns, *old_dns;
3051 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3053 struct ldb_context *ldb = ldb_module_get_ctx(module);
3054 struct ldb_val *new_values = NULL;
3055 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3056 unsigned int old_num_values;
3057 unsigned int repl_num_values;
3058 unsigned int max_num_values;
3061 unix_to_nt_time(&now, t);
3064 * The replace operation is unlike the replace and delete cases in that
3065 * we need to look at every existing link to see whether it is being
3066 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3068 * As we are trying to combine two sorted lists, the algorithm we use
3069 * is akin to the merge phase of a merge sort. We interleave the two
3070 * lists, doing different things depending on which side the current
3073 * There are three main cases, with some sub-cases.
3075 * - a DN is in the old list but not the new one. It needs to be
3076 * marked as deleted (but left in the list).
3077 * - maybe it is already deleted, and we have less to do.
3079 * - a DN is in both lists. The old data gets replaced by the new,
3080 * and the list doesn't grow. The old link may have been marked as
3081 * deleted, in which case we undelete it.
3083 * - a DN is in the new list only. We add it in the right place.
3086 old_num_values = old_el ? old_el->num_values : 0;
3087 repl_num_values = el->num_values;
3088 max_num_values = old_num_values + repl_num_values;
3090 if (max_num_values == 0) {
3091 /* There is nothing to do! */
3096 * At the successful end of these functions el->values is
3097 * overwritten with new_values. However get_parsed_dns()
3098 * points p->v at the supplied el and it effectively gets used
3099 * as a working area by replmd_build_la_val(). So we must
3100 * duplicate it because our caller only called
3101 * ldb_msg_copy_shallow().
3104 el->values = talloc_memdup(tmp_ctx,
3106 sizeof(el->values[0]) * el->num_values);
3107 if (el->values == NULL) {
3108 ldb_module_oom(module);
3109 talloc_free(tmp_ctx);
3110 return LDB_ERR_OPERATIONS_ERROR;
3113 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3114 if (ret != LDB_SUCCESS) {
3115 talloc_free(tmp_ctx);
3119 ret = check_parsed_dn_duplicates(module, el, dns);
3120 if (ret != LDB_SUCCESS) {
3121 talloc_free(tmp_ctx);
3125 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3127 if (ret != LDB_SUCCESS) {
3128 talloc_free(tmp_ctx);
3132 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3134 if (ret != LDB_SUCCESS) {
3135 talloc_free(tmp_ctx);
3139 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3140 if (new_values == NULL) {
3141 ldb_module_oom(module);
3142 talloc_free(tmp_ctx);
3143 return LDB_ERR_OPERATIONS_ERROR;
3148 for (i = 0; i < max_num_values; i++) {
3150 struct parsed_dn *old_p, *new_p;
3151 if (old_i < old_num_values && new_i < repl_num_values) {
3152 old_p = &old_dns[old_i];
3153 new_p = &dns[new_i];
3154 cmp = parsed_dn_compare(old_p, new_p);
3155 } else if (old_i < old_num_values) {
3156 /* the new list is empty, read the old list */
3157 old_p = &old_dns[old_i];
3160 } else if (new_i < repl_num_values) {
3161 /* the old list is empty, read new list */
3163 new_p = &dns[new_i];
3171 * An old ones that come before the next replacement
3172 * (if any). We mark it as deleted and add it to the
3175 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3176 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3177 ret = replmd_update_la_val(new_values, old_p->v,
3180 &ac->our_invocation_id,
3181 ac->seq_num, ac->seq_num,
3183 if (ret != LDB_SUCCESS) {
3184 talloc_free(tmp_ctx);
3188 ret = replmd_add_backlink(module, replmd_private,
3191 &old_p->guid, false,
3194 if (ret != LDB_SUCCESS) {
3195 talloc_free(tmp_ctx);
3199 new_values[i] = *old_p->v;
3201 } else if (cmp == 0) {
3203 * We are overwriting one. If it was previously
3204 * deleted, we need to add a backlink.
3206 * Note that if any RMD_FLAGs in an extended new DN
3211 ret = replmd_update_la_val(new_values, old_p->v,
3214 &ac->our_invocation_id,
3215 ac->seq_num, ac->seq_num,
3217 if (ret != LDB_SUCCESS) {
3218 talloc_free(tmp_ctx);
3222 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3223 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3224 ret = replmd_add_backlink(module, replmd_private,
3230 if (ret != LDB_SUCCESS) {
3231 talloc_free(tmp_ctx);
3236 new_values[i] = *old_p->v;
3241 * Replacements that don't match an existing one. We
3242 * just add them to the final list.
3244 ret = replmd_build_la_val(new_values,
3247 &ac->our_invocation_id,
3249 if (ret != LDB_SUCCESS) {
3250 talloc_free(tmp_ctx);
3253 ret = replmd_add_backlink(module, replmd_private,
3259 if (ret != LDB_SUCCESS) {
3260 talloc_free(tmp_ctx);
3263 new_values[i] = *new_p->v;
3267 if (old_el != NULL) {
3268 talloc_steal(msg->elements, old_el->values);
3270 el->values = talloc_steal(msg->elements, new_values);
3272 talloc_free(tmp_ctx);
3274 el->flags = LDB_FLAG_MOD_REPLACE;
3281 handle linked attributes in modify requests
3283 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3284 struct replmd_private *replmd_private,
3285 struct replmd_replicated_request *ac,
3286 struct ldb_message *msg,
3288 struct ldb_request *parent)
3290 struct ldb_result *res;
3293 struct ldb_context *ldb = ldb_module_get_ctx(module);
3294 struct ldb_message *old_msg;
3296 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3298 * Nothing special is required for modifying or vanishing links
3299 * in fl2000 since they are just strings in a multi-valued
3302 struct ldb_control *ctrl = ldb_request_get_control(parent,
3303 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3305 ctrl->critical = false;
3313 * We should restrict this to the intersection of the list of
3314 * linked attributes in the schema and the list of attributes
3317 * This will help performance a little, as otherwise we have
3318 * to allocate the entire object value-by-value.
3320 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3321 DSDB_FLAG_NEXT_MODULE |
3322 DSDB_SEARCH_SHOW_RECYCLED |
3323 DSDB_SEARCH_REVEAL_INTERNALS |
3324 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3326 if (ret != LDB_SUCCESS) {
3330 old_msg = res->msgs[0];
3332 for (i=0; i<msg->num_elements; i++) {
3333 struct ldb_message_element *el = &msg->elements[i];
3334 struct ldb_message_element *old_el, *new_el;
3335 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3336 const struct dsdb_attribute *schema_attr
3337 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3339 ldb_asprintf_errstring(ldb,
3340 "%s: attribute %s is not a valid attribute in schema",
3341 __FUNCTION__, el->name);
3342 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3344 if (schema_attr->linkID == 0) {
3347 if ((schema_attr->linkID & 1) == 1) {
3349 struct ldb_control *ctrl;
3351 ctrl = ldb_request_get_control(parent,
3352 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3354 ctrl->critical = false;
3357 ctrl = ldb_request_get_control(parent,
3358 DSDB_CONTROL_DBCHECK);
3364 /* Odd is for the target. Illegal to modify */
3365 ldb_asprintf_errstring(ldb,
3366 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3367 return LDB_ERR_UNWILLING_TO_PERFORM;
3369 old_el = ldb_msg_find_element(old_msg, el->name);
3371 case LDB_FLAG_MOD_REPLACE:
3372 ret = replmd_modify_la_replace(module, replmd_private,
3373 ac, msg, el, old_el,
3378 case LDB_FLAG_MOD_DELETE:
3379 ret = replmd_modify_la_delete(module, replmd_private,
3380 ac, msg, el, old_el,
3385 case LDB_FLAG_MOD_ADD:
3386 ret = replmd_modify_la_add(module, replmd_private,
3387 ac, msg, el, old_el,
3393 ldb_asprintf_errstring(ldb,
3394 "invalid flags 0x%x for %s linked attribute",
3395 el->flags, el->name);
3396 return LDB_ERR_UNWILLING_TO_PERFORM;
3398 if (ret != LDB_SUCCESS) {
3401 ret = dsdb_check_single_valued_link(schema_attr, el);
3402 if (ret != LDB_SUCCESS) {
3403 ldb_asprintf_errstring(ldb,
3404 "Attribute %s is single valued but more than one value has been supplied",
3406 /* Return codes as found on Windows 2012r2 */
3407 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3408 return LDB_ERR_CONSTRAINT_VIOLATION;
3410 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3413 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3417 ldb_msg_remove_attr(old_msg, el->name);
3419 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3420 new_el->num_values = el->num_values;
3421 new_el->values = talloc_steal(msg->elements, el->values);
3423 /* TODO: this relises a bit too heavily on the exact
3424 behaviour of ldb_msg_find_element and
3425 ldb_msg_remove_element */
3426 old_el = ldb_msg_find_element(msg, el->name);
3428 ldb_msg_remove_element(msg, old_el);
3438 static int send_rodc_referral(struct ldb_request *req,
3439 struct ldb_context *ldb,
3442 char *referral = NULL;
3443 struct loadparm_context *lp_ctx = NULL;
3444 struct ldb_dn *fsmo_role_dn = NULL;
3445 struct ldb_dn *role_owner_dn = NULL;
3446 const char *domain = NULL;
3449 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3450 struct loadparm_context);
3452 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3453 &fsmo_role_dn, &role_owner_dn);
3455 if (W_ERROR_IS_OK(werr)) {
3456 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3457 if (server_dn != NULL) {
3458 ldb_dn_remove_child_components(server_dn, 1);
3459 domain = samdb_dn_to_dnshostname(ldb, req,
3464 if (domain == NULL) {
3465 domain = lpcfg_dnsdomain(lp_ctx);
3468 referral = talloc_asprintf(req, "ldap://%s/%s",
3470 ldb_dn_get_linearized(dn));
3471 if (referral == NULL) {
3473 return LDB_ERR_OPERATIONS_ERROR;
3476 return ldb_module_send_referral(req, referral);
3480 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3482 struct ldb_context *ldb;
3483 struct replmd_replicated_request *ac;
3484 struct ldb_request *down_req;
3485 struct ldb_message *msg;
3486 time_t t = time(NULL);
3488 bool is_urgent = false, rodc = false;
3489 bool is_schema_nc = false;
3490 unsigned int functional_level;
3491 const struct ldb_message_element *guid_el = NULL;
3492 struct ldb_control *sd_propagation_control;
3493 struct ldb_control *fix_links_control = NULL;
3494 struct ldb_control *fix_dn_name_control = NULL;
3495 struct ldb_control *fix_dn_sid_control = NULL;
3496 struct replmd_private *replmd_private =
3497 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3499 /* do not manipulate our control entries */
3500 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3501 return ldb_next_request(module, req);
3504 sd_propagation_control = ldb_request_get_control(req,
3505 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3506 if (sd_propagation_control != NULL) {
3507 if (req->op.mod.message->num_elements != 1) {
3508 return ldb_module_operr(module);
3510 ret = strcmp(req->op.mod.message->elements[0].name,
3511 "nTSecurityDescriptor");
3513 return ldb_module_operr(module);
3516 return ldb_next_request(module, req);
3519 ldb = ldb_module_get_ctx(module);
3521 fix_links_control = ldb_request_get_control(req,
3522 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3523 if (fix_links_control != NULL) {
3524 struct dsdb_schema *schema = NULL;
3525 const struct dsdb_attribute *sa = NULL;
3527 if (req->op.mod.message->num_elements != 1) {
3528 return ldb_module_operr(module);
3531 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3532 return ldb_module_operr(module);
3535 schema = dsdb_get_schema(ldb, req);
3536 if (schema == NULL) {
3537 return ldb_module_operr(module);
3540 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3541 req->op.mod.message->elements[0].name);
3543 return ldb_module_operr(module);
3546 if (sa->linkID == 0) {
3547 return ldb_module_operr(module);
3550 fix_links_control->critical = false;
3551 return ldb_next_request(module, req);
3554 fix_dn_name_control = ldb_request_get_control(req,
3555 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3556 if (fix_dn_name_control != NULL) {
3557 struct dsdb_schema *schema = NULL;
3558 const struct dsdb_attribute *sa = NULL;
3560 if (req->op.mod.message->num_elements != 2) {
3561 return ldb_module_operr(module);
3564 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3565 return ldb_module_operr(module);
3568 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3569 return ldb_module_operr(module);
3572 if (req->op.mod.message->elements[0].num_values != 1) {
3573 return ldb_module_operr(module);
3576 if (req->op.mod.message->elements[1].num_values != 1) {
3577 return ldb_module_operr(module);
3580 schema = dsdb_get_schema(ldb, req);
3581 if (schema == NULL) {
3582 return ldb_module_operr(module);
3585 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3586 req->op.mod.message->elements[1].name) != 0) {
3587 return ldb_module_operr(module);
3590 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3591 req->op.mod.message->elements[0].name);
3593 return ldb_module_operr(module);
3596 if (sa->dn_format == DSDB_INVALID_DN) {
3597 return ldb_module_operr(module);
3600 if (sa->linkID != 0) {
3601 return ldb_module_operr(module);
3605 * If we are run from dbcheck and we are not updating
3606 * a link (as these would need to be sorted and so
3607 * can't go via such a simple update, then do not
3608 * trigger replicated updates and a new USN from this
3609 * change, it wasn't a real change, just a new
3610 * (correct) string DN
3613 fix_dn_name_control->critical = false;
3614 return ldb_next_request(module, req);
3617 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3619 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3620 if (guid_el != NULL) {
3621 ldb_set_errstring(ldb,
3622 "replmd_modify: it's not allowed to change the objectGUID!");
3623 return LDB_ERR_CONSTRAINT_VIOLATION;
3626 ac = replmd_ctx_init(module, req);
3628 return ldb_module_oom(module);
3631 functional_level = dsdb_functional_level(ldb);
3633 /* we have to copy the message as the caller might have it as a const */
3634 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3638 return LDB_ERR_OPERATIONS_ERROR;
3641 fix_dn_sid_control = ldb_request_get_control(req,
3642 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3643 if (fix_dn_sid_control != NULL) {
3644 const struct dsdb_attribute *sa = NULL;
3646 if (msg->num_elements != 1) {
3648 return ldb_module_operr(module);
3651 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3653 return ldb_module_operr(module);
3656 if (msg->elements[0].num_values != 1) {
3658 return ldb_module_operr(module);
3661 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3662 msg->elements[0].name);
3665 return ldb_module_operr(module);
3668 if (sa->dn_format != DSDB_NORMAL_DN) {
3670 return ldb_module_operr(module);
3673 fix_dn_sid_control->critical = false;
3674 ac->fix_link_sid = true;
3676 goto handle_linked_attribs;
3679 ldb_msg_remove_attr(msg, "whenChanged");
3680 ldb_msg_remove_attr(msg, "uSNChanged");
3682 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3684 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3685 msg, &ac->seq_num, t, is_schema_nc,
3687 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3688 ret = send_rodc_referral(req, ldb, msg->dn);
3694 if (ret != LDB_SUCCESS) {
3699 handle_linked_attribs:
3700 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3702 if (ret != LDB_SUCCESS) {
3708 * - replace the old object with the newly constructed one
3711 ac->is_urgent = is_urgent;
3713 ret = ldb_build_mod_req(&down_req, ldb, ac,
3716 ac, replmd_op_callback,
3718 LDB_REQ_SET_LOCATION(down_req);
3719 if (ret != LDB_SUCCESS) {
3724 /* current partition control is needed by "replmd_op_callback" */
3725 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3726 ret = ldb_request_add_control(down_req,
3727 DSDB_CONTROL_CURRENT_PARTITION_OID,
3729 if (ret != LDB_SUCCESS) {
3735 /* If we are in functional level 2000, then
3736 * replmd_modify_handle_linked_attribs will have done
3738 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3739 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3740 if (ret != LDB_SUCCESS) {
3746 talloc_steal(down_req, msg);
3748 /* we only change whenChanged and uSNChanged if the seq_num
3750 if (ac->seq_num != 0) {
3751 ret = add_time_element(msg, "whenChanged", t);
3752 if (ret != LDB_SUCCESS) {
3758 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3759 if (ret != LDB_SUCCESS) {
3766 /* go on with the call chain */
3767 return ldb_next_request(module, down_req);
3770 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3773 handle a rename request
3775 On a rename we need to do an extra ldb_modify which sets the
3776 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3778 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3780 struct ldb_context *ldb;
3781 struct ldb_control *fix_dn_name_control = NULL;
3782 struct replmd_replicated_request *ac;
3784 struct ldb_request *down_req;
3786 /* do not manipulate our control entries */
3787 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3788 return ldb_next_request(module, req);
3791 fix_dn_name_control = ldb_request_get_control(req,
3792 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3793 if (fix_dn_name_control != NULL) {
3794 return ldb_next_request(module, req);
3797 ldb = ldb_module_get_ctx(module);
3799 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3801 ac = replmd_ctx_init(module, req);
3803 return ldb_module_oom(module);
3806 ret = ldb_build_rename_req(&down_req, ldb, ac,
3807 ac->req->op.rename.olddn,
3808 ac->req->op.rename.newdn,
3810 ac, replmd_rename_callback,
3812 LDB_REQ_SET_LOCATION(down_req);
3813 if (ret != LDB_SUCCESS) {
3818 /* go on with the call chain */
3819 return ldb_next_request(module, down_req);
3822 /* After the rename is compleated, update the whenchanged etc */
3823 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3825 struct ldb_context *ldb;
3826 struct ldb_request *down_req;
3827 struct ldb_message *msg;
3828 const struct dsdb_attribute *rdn_attr;
3829 const char *rdn_name;
3830 const struct ldb_val *rdn_val;
3831 const char *attrs[5] = { NULL, };
3832 time_t t = time(NULL);
3834 bool is_urgent = false, rodc = false;
3836 struct replmd_replicated_request *ac =
3837 talloc_get_type(req->context, struct replmd_replicated_request);
3838 struct replmd_private *replmd_private =
3839 talloc_get_type(ldb_module_get_private(ac->module),
3840 struct replmd_private);
3842 ldb = ldb_module_get_ctx(ac->module);
3844 if (ares->error != LDB_SUCCESS) {
3845 return ldb_module_done(ac->req, ares->controls,
3846 ares->response, ares->error);
3849 if (ares->type != LDB_REPLY_DONE) {
3850 ldb_set_errstring(ldb,
3851 "invalid reply type in repl_meta_data rename callback");
3853 return ldb_module_done(ac->req, NULL, NULL,
3854 LDB_ERR_OPERATIONS_ERROR);
3858 * - replace the old object with the newly constructed one
3861 msg = ldb_msg_new(ac);
3864 return LDB_ERR_OPERATIONS_ERROR;
3867 msg->dn = ac->req->op.rename.newdn;
3869 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3871 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3872 if (rdn_name == NULL) {
3874 return ldb_module_done(ac->req, NULL, NULL,
3878 /* normalize the rdn attribute name */
3879 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3880 if (rdn_attr == NULL) {
3882 return ldb_module_done(ac->req, NULL, NULL,
3885 rdn_name = rdn_attr->lDAPDisplayName;
3887 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3888 if (rdn_val == NULL) {
3890 return ldb_module_done(ac->req, NULL, NULL,
3894 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3896 return ldb_module_done(ac->req, NULL, NULL,
3899 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3901 return ldb_module_done(ac->req, NULL, NULL,
3904 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3906 return ldb_module_done(ac->req, NULL, NULL,
3909 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3911 return ldb_module_done(ac->req, NULL, NULL,
3916 * here we let replmd_update_rpmd() only search for
3917 * the existing "replPropertyMetaData" and rdn_name attributes.
3919 * We do not want the existing "name" attribute as
3920 * the "name" attribute needs to get the version
3921 * updated on rename even if the rdn value hasn't changed.
3923 * This is the diff of the meta data, for a moved user
3924 * on a w2k8r2 server:
3927 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3928 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3929 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3930 * version : 0x00000001 (1)
3931 * reserved : 0x00000000 (0)
3932 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3933 * local_usn : 0x00000000000037a5 (14245)
3934 * array: struct replPropertyMetaData1
3935 * attid : DRSUAPI_ATTID_name (0x90001)
3936 * - version : 0x00000001 (1)
3937 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3938 * + version : 0x00000002 (2)
3939 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3940 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3941 * - originating_usn : 0x00000000000037a5 (14245)
3942 * - local_usn : 0x00000000000037a5 (14245)
3943 * + originating_usn : 0x0000000000003834 (14388)
3944 * + local_usn : 0x0000000000003834 (14388)
3945 * array: struct replPropertyMetaData1
3946 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3947 * version : 0x00000004 (4)
3949 attrs[0] = "replPropertyMetaData";
3950 attrs[1] = "objectClass";
3951 attrs[2] = "instanceType";
3952 attrs[3] = rdn_name;
3955 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3956 msg, &ac->seq_num, t,
3957 is_schema_nc, &is_urgent, &rodc);
3958 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3959 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3961 return ldb_module_done(req, NULL, NULL, ret);
3964 if (ret != LDB_SUCCESS) {
3966 return ldb_module_done(ac->req, NULL, NULL, ret);
3969 if (ac->seq_num == 0) {
3971 return ldb_module_done(ac->req, NULL, NULL,
3973 "internal error seq_num == 0"));
3975 ac->is_urgent = is_urgent;
3977 ret = ldb_build_mod_req(&down_req, ldb, ac,
3980 ac, replmd_op_callback,
3982 LDB_REQ_SET_LOCATION(down_req);
3983 if (ret != LDB_SUCCESS) {
3988 /* current partition control is needed by "replmd_op_callback" */
3989 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3990 ret = ldb_request_add_control(down_req,
3991 DSDB_CONTROL_CURRENT_PARTITION_OID,
3993 if (ret != LDB_SUCCESS) {
3999 talloc_steal(down_req, msg);
4001 ret = add_time_element(msg, "whenChanged", t);
4002 if (ret != LDB_SUCCESS) {
4008 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
4009 if (ret != LDB_SUCCESS) {
4015 /* go on with the call chain - do the modify after the rename */
4016 return ldb_next_request(ac->module, down_req);
4020 * remove links from objects that point at this object when an object
4021 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
4022 * RemoveObj which states that link removal due to the object being
4023 * deleted is NOT an originating update - they just go away!
4026 static int replmd_delete_remove_link(struct ldb_module *module,
4027 const struct dsdb_schema *schema,
4028 struct replmd_private *replmd_private,
4031 struct ldb_message_element *el,
4032 const struct dsdb_attribute *sa,
4033 struct ldb_request *parent)
4036 TALLOC_CTX *tmp_ctx = talloc_new(module);
4037 struct ldb_context *ldb = ldb_module_get_ctx(module);
4039 for (i=0; i<el->num_values; i++) {
4040 struct dsdb_dn *dsdb_dn;
4042 struct ldb_message *msg;
4043 const struct dsdb_attribute *target_attr;
4044 struct ldb_message_element *el2;
4046 struct ldb_val dn_val;
4047 uint32_t dsdb_flags = 0;
4048 const char *attrs[] = { NULL, NULL };
4049 struct ldb_result *link_res;
4050 struct ldb_message *link_msg;
4051 struct ldb_message_element *link_el;
4052 struct parsed_dn *link_dns;
4053 struct parsed_dn *p = NULL, *unused = NULL;
4055 if (dsdb_dn_is_deleted_val(&el->values[i])) {
4059 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4061 talloc_free(tmp_ctx);
4062 return LDB_ERR_OPERATIONS_ERROR;
4065 /* remove the link */
4066 msg = ldb_msg_new(tmp_ctx);
4068 ldb_module_oom(module);
4069 talloc_free(tmp_ctx);
4070 return LDB_ERR_OPERATIONS_ERROR;
4074 msg->dn = dsdb_dn->dn;
4076 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4077 if (target_attr == NULL) {
4080 attrs[0] = target_attr->lDAPDisplayName;
4082 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4083 LDB_FLAG_MOD_DELETE, &el2);
4084 if (ret != LDB_SUCCESS) {
4085 ldb_module_oom(module);
4086 talloc_free(tmp_ctx);
4087 return LDB_ERR_OPERATIONS_ERROR;
4090 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4092 DSDB_FLAG_NEXT_MODULE |
4093 DSDB_SEARCH_SHOW_EXTENDED_DN |
4094 DSDB_SEARCH_SHOW_RECYCLED,
4097 if (ret != LDB_SUCCESS) {
4098 talloc_free(tmp_ctx);
4102 link_msg = link_res->msgs[0];
4103 link_el = ldb_msg_find_element(link_msg,
4104 target_attr->lDAPDisplayName);
4105 if (link_el == NULL) {
4106 talloc_free(tmp_ctx);
4107 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4111 * This call 'upgrades' the links in link_dns, but we
4112 * do not commit the result back into the database, so
4113 * this is safe to call in FL2000 or on databases that
4114 * have been run at that level in the past.
4116 ret = get_parsed_dns_trusted_fallback(module, replmd_private,
4119 target_attr->syntax->ldap_oid,
4121 if (ret != LDB_SUCCESS) {
4122 talloc_free(tmp_ctx);
4126 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4130 target_attr->syntax->ldap_oid, false);
4131 if (ret != LDB_SUCCESS) {
4132 talloc_free(tmp_ctx);
4137 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4138 "Failed to find forward link on %s "
4139 "as %s to remove backlink %s on %s",
4140 ldb_dn_get_linearized(msg->dn),
4141 target_attr->lDAPDisplayName,
4142 sa->lDAPDisplayName,
4143 ldb_dn_get_linearized(dn));
4144 talloc_free(tmp_ctx);
4145 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4149 /* This needs to get the Binary DN, by first searching */
4150 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4153 dn_val = data_blob_string_const(dn_str);
4154 el2->values = &dn_val;
4155 el2->num_values = 1;
4158 * Ensure that we tell the modification to vanish any linked
4159 * attributes (not simply mark them as isDeleted = TRUE)
4161 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4163 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4164 if (ret != LDB_SUCCESS) {
4165 talloc_free(tmp_ctx);
4169 talloc_free(tmp_ctx);
4175 handle update of replication meta data for deletion of objects
4177 This also handles the mapping of delete to a rename operation
4178 to allow deletes to be replicated.
4180 It also handles the incoming deleted objects, to ensure they are
4181 fully deleted here. In that case re_delete is true, and we do not
4182 use this as a signal to change the deleted state, just reinforce it.
4185 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4187 int ret = LDB_ERR_OTHER;
4188 bool retb, disallow_move_on_delete;
4189 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4190 const char *rdn_name;
4191 const struct ldb_val *rdn_value, *new_rdn_value;
4193 struct ldb_context *ldb = ldb_module_get_ctx(module);
4194 const struct dsdb_schema *schema;
4195 struct ldb_message *msg, *old_msg;
4196 struct ldb_message_element *el;
4197 TALLOC_CTX *tmp_ctx;
4198 struct ldb_result *res, *parent_res;
4199 static const char * const preserved_attrs[] = {
4201 * This list MUST be kept in case-insensitive sorted order,
4202 * as we use it in a binary search with ldb_attr_cmp().
4204 * We get this hard-coded list from
4205 * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4209 "distinguishedName",
4210 "dNReferenceUpdate",
4222 "msDS-LastKnownRDN",
4227 "nTSecurityDescriptor",
4232 "proxiedObjectName",
4233 "replPropertyMetaData",
4235 "securityIdentifier",
4243 "userAccountControl",
4249 * DO NOT JUST APPEND TO THIS LIST.
4251 * In case you missed the note at the top, this list is kept
4252 * in case-insensitive sorted order. In the unlikely event you
4253 * need to add an attrbute, please add it in the RIGHT PLACE.
4256 static const char * const all_attrs[] = {
4257 DSDB_SECRET_ATTRIBUTES,
4261 static const struct ldb_val true_val = {
4262 .data = discard_const_p(uint8_t, "TRUE"),
4267 uint32_t dsdb_flags = 0;
4268 struct replmd_private *replmd_private;
4269 enum deletion_state deletion_state, next_deletion_state;
4271 if (ldb_dn_is_special(req->op.del.dn)) {
4272 return ldb_next_request(module, req);
4276 * We have to allow dbcheck to remove an object that
4277 * is beyond repair, and to do so totally. This could
4278 * mean we we can get a partial object from the other
4279 * DC, causing havoc, so dbcheck suggests
4280 * re-replication first. dbcheck sets both DBCHECK
4281 * and RELAX in this situation.
4283 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4284 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4285 /* really, really remove it */
4286 return ldb_next_request(module, req);
4289 tmp_ctx = talloc_new(ldb);
4292 return LDB_ERR_OPERATIONS_ERROR;
4295 schema = dsdb_get_schema(ldb, tmp_ctx);
4297 talloc_free(tmp_ctx);
4298 return LDB_ERR_OPERATIONS_ERROR;
4301 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4303 /* we need the complete msg off disk, so we can work out which
4304 attributes need to be removed */
4305 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4306 DSDB_FLAG_NEXT_MODULE |
4307 DSDB_SEARCH_SHOW_RECYCLED |
4308 DSDB_SEARCH_REVEAL_INTERNALS |
4309 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4310 if (ret != LDB_SUCCESS) {
4311 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4312 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4313 re_delete ? "re-delete" : "delete",
4314 ldb_dn_get_linearized(old_dn),
4315 ldb_errstring(ldb_module_get_ctx(module)));
4316 talloc_free(tmp_ctx);
4319 old_msg = res->msgs[0];
4321 replmd_deletion_state(module, old_msg,
4323 &next_deletion_state);
4325 /* This supports us noticing an incoming isDeleted and acting on it */
4327 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4328 next_deletion_state = deletion_state;
4331 if (next_deletion_state == OBJECT_REMOVED) {
4333 * We have to prevent objects being deleted, even if
4334 * the administrator really wants them gone, as
4335 * without the tombstone, we can get a partial object
4336 * from the other DC, causing havoc.
4338 * The only other valid case is when the 180 day
4339 * timeout has expired, when relax is specified.
4341 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4342 /* it is already deleted - really remove it this time */
4343 talloc_free(tmp_ctx);
4344 return ldb_next_request(module, req);
4347 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4348 "This check is to prevent corruption of the replicated state.",
4349 ldb_dn_get_linearized(old_msg->dn));
4350 return LDB_ERR_UNWILLING_TO_PERFORM;
4353 rdn_name = ldb_dn_get_rdn_name(old_dn);
4354 rdn_value = ldb_dn_get_rdn_val(old_dn);
4355 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4356 talloc_free(tmp_ctx);
4357 return ldb_operr(ldb);
4360 msg = ldb_msg_new(tmp_ctx);
4362 ldb_module_oom(module);
4363 talloc_free(tmp_ctx);
4364 return LDB_ERR_OPERATIONS_ERROR;
4369 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4370 disallow_move_on_delete =
4371 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4372 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4374 /* work out where we will be renaming this object to */
4375 if (!disallow_move_on_delete) {
4376 struct ldb_dn *deleted_objects_dn;
4377 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4378 &deleted_objects_dn);
4381 * We should not move objects if we can't find the
4382 * deleted objects DN. Not moving (or otherwise
4383 * harming) the Deleted Objects DN itself is handled
4386 if (re_delete && (ret != LDB_SUCCESS)) {
4387 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4388 if (new_dn == NULL) {
4389 ldb_module_oom(module);
4390 talloc_free(tmp_ctx);
4391 return LDB_ERR_OPERATIONS_ERROR;
4393 } else if (ret != LDB_SUCCESS) {
4394 /* this is probably an attempted delete on a partition
4395 * that doesn't allow delete operations, such as the
4396 * schema partition */
4397 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4398 ldb_dn_get_linearized(old_dn));
4399 talloc_free(tmp_ctx);
4400 return LDB_ERR_UNWILLING_TO_PERFORM;
4402 new_dn = deleted_objects_dn;
4405 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4406 if (new_dn == NULL) {
4407 ldb_module_oom(module);
4408 talloc_free(tmp_ctx);
4409 return LDB_ERR_OPERATIONS_ERROR;
4413 /* get the objects GUID from the search we just did */
4414 guid = samdb_result_guid(old_msg, "objectGUID");
4416 if (deletion_state == OBJECT_NOT_DELETED) {
4417 struct ldb_message_element *is_deleted_el;
4419 ret = replmd_make_deleted_child_dn(tmp_ctx,
4422 rdn_name, rdn_value,
4425 if (ret != LDB_SUCCESS) {
4426 talloc_free(tmp_ctx);
4430 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4432 if (ret != LDB_SUCCESS) {
4433 ldb_asprintf_errstring(ldb, __location__
4434 ": Failed to add isDeleted string to the msg");
4435 talloc_free(tmp_ctx);
4438 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4441 * No matter what has happened with other renames etc, try again to
4442 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4445 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4446 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4448 ldb_asprintf_errstring(ldb, __location__
4449 ": Unable to add a prepare rdn of %s",
4450 ldb_dn_get_linearized(rdn));
4451 talloc_free(tmp_ctx);
4452 return LDB_ERR_OPERATIONS_ERROR;
4454 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4456 retb = ldb_dn_add_child(new_dn, rdn);
4458 ldb_asprintf_errstring(ldb, __location__
4459 ": Unable to add rdn %s to base dn: %s",
4460 ldb_dn_get_linearized(rdn),
4461 ldb_dn_get_linearized(new_dn));
4462 talloc_free(tmp_ctx);
4463 return LDB_ERR_OPERATIONS_ERROR;
4468 now we need to modify the object in the following ways:
4470 - add isDeleted=TRUE
4471 - update rDN and name, with new rDN
4472 - remove linked attributes
4473 - remove objectCategory and sAMAccountType
4474 - remove attribs not on the preserved list
4475 - preserved if in above list, or is rDN
4476 - remove all linked attribs from this object
4477 - remove all links from other objects to this object
4478 (note we use the backlinks to do this, so we won't find one-way
4479 links that still point to this object, or deactivated two-way
4480 links, i.e. 'member' after the user has been removed from the
4482 - add lastKnownParent
4483 - update replPropertyMetaData?
4485 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4488 if (deletion_state == OBJECT_NOT_DELETED) {
4489 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4490 char *parent_dn_str = NULL;
4491 struct ldb_message_element *p_el;
4493 /* we need the storage form of the parent GUID */
4494 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4496 DSDB_FLAG_NEXT_MODULE |
4497 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4498 DSDB_SEARCH_REVEAL_INTERNALS|
4499 DSDB_SEARCH_SHOW_RECYCLED, req);
4500 if (ret != LDB_SUCCESS) {
4501 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4502 "repmd_delete: Failed to %s %s, "
4503 "because we failed to find it's parent (%s): %s",
4504 re_delete ? "re-delete" : "delete",
4505 ldb_dn_get_linearized(old_dn),
4506 ldb_dn_get_linearized(parent_dn),
4507 ldb_errstring(ldb_module_get_ctx(module)));
4508 talloc_free(tmp_ctx);
4513 * Now we can use the DB version,
4514 * it will have the extended DN info in it
4516 parent_dn = parent_res->msgs[0]->dn;
4517 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4520 if (parent_dn_str == NULL) {
4521 talloc_free(tmp_ctx);
4522 return ldb_module_oom(module);
4525 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4527 if (ret != LDB_SUCCESS) {
4528 ldb_asprintf_errstring(ldb, __location__
4529 ": Failed to add lastKnownParent "
4530 "string when deleting %s",
4531 ldb_dn_get_linearized(old_dn));
4532 talloc_free(tmp_ctx);
4535 p_el = ldb_msg_find_element(msg,
4538 talloc_free(tmp_ctx);
4539 return ldb_module_operr(module);
4541 p_el->flags = LDB_FLAG_MOD_REPLACE;
4543 if (next_deletion_state == OBJECT_DELETED) {
4544 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4545 if (ret != LDB_SUCCESS) {
4546 ldb_asprintf_errstring(ldb, __location__
4547 ": Failed to add msDS-LastKnownRDN "
4548 "string when deleting %s",
4549 ldb_dn_get_linearized(old_dn));
4550 talloc_free(tmp_ctx);
4553 p_el = ldb_msg_find_element(msg,
4554 "msDS-LastKnownRDN");
4556 talloc_free(tmp_ctx);
4557 return ldb_module_operr(module);
4559 p_el->flags = LDB_FLAG_MOD_ADD;
4563 switch (next_deletion_state) {
4565 case OBJECT_RECYCLED:
4566 case OBJECT_TOMBSTONE:
4569 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4570 * describes what must be removed from a tombstone
4573 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4574 * describes what must be removed from a recycled
4580 * we also mark it as recycled, meaning this object can't be
4581 * recovered (we are stripping its attributes).
4582 * This is done only if we have this schema object of course ...
4583 * This behavior is identical to the one of Windows 2008R2 which
4584 * always set the isRecycled attribute, even if the recycle-bin is
4585 * not activated and what ever the forest level is.
4587 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4588 struct ldb_message_element *is_recycled_el;
4590 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4592 if (ret != LDB_SUCCESS) {
4593 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4594 ldb_module_oom(module);
4595 talloc_free(tmp_ctx);
4598 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4601 replmd_private = talloc_get_type(ldb_module_get_private(module),
4602 struct replmd_private);
4603 /* work out which of the old attributes we will be removing */
4604 for (i=0; i<old_msg->num_elements; i++) {
4605 const struct dsdb_attribute *sa;
4606 el = &old_msg->elements[i];
4607 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4609 talloc_free(tmp_ctx);
4610 return LDB_ERR_OPERATIONS_ERROR;
4612 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4613 /* don't remove the rDN */
4617 if (sa->linkID & 1) {
4619 * we have a backlink in this object
4620 * that needs to be removed. We're not
4621 * allowed to remove it directly
4622 * however, so we instead setup a
4623 * modify to delete the corresponding
4626 ret = replmd_delete_remove_link(module, schema,
4630 if (ret == LDB_SUCCESS) {
4632 * now we continue, which means we
4633 * won't remove this backlink
4639 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4640 const char *old_dn_str
4641 = ldb_dn_get_linearized(old_dn);
4642 ldb_asprintf_errstring(ldb,
4644 ": Failed to remove backlink of "
4645 "%s when deleting %s: %s",
4648 ldb_errstring(ldb));
4649 talloc_free(tmp_ctx);
4650 return LDB_ERR_OPERATIONS_ERROR;
4654 * Otherwise vanish the link, we are
4655 * out of sync and the controlling
4656 * object does not have the source
4660 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4662 } else if (sa->linkID == 0) {
4663 const char * const *attr = NULL;
4664 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4667 BINARY_ARRAY_SEARCH_V(preserved_attrs,
4668 ARRAY_SIZE(preserved_attrs),
4677 * Ensure that we tell the modification to vanish any linked
4678 * attributes (not simply mark them as isDeleted = TRUE)
4680 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4682 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4683 if (ret != LDB_SUCCESS) {
4684 talloc_free(tmp_ctx);
4685 ldb_module_oom(module);
4692 case OBJECT_DELETED:
4694 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4695 * describes what must be removed from a deleted
4699 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4700 if (ret != LDB_SUCCESS) {
4701 talloc_free(tmp_ctx);
4702 ldb_module_oom(module);
4706 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4707 if (ret != LDB_SUCCESS) {
4708 talloc_free(tmp_ctx);
4709 ldb_module_oom(module);
4719 if (deletion_state == OBJECT_NOT_DELETED) {
4720 const struct dsdb_attribute *sa;
4722 /* work out what the new rdn value is, for updating the
4723 rDN and name fields */
4724 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4725 if (new_rdn_value == NULL) {
4726 talloc_free(tmp_ctx);
4727 return ldb_operr(ldb);
4730 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4732 talloc_free(tmp_ctx);
4733 return LDB_ERR_OPERATIONS_ERROR;
4736 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4738 if (ret != LDB_SUCCESS) {
4739 talloc_free(tmp_ctx);
4742 el->flags = LDB_FLAG_MOD_REPLACE;
4744 el = ldb_msg_find_element(old_msg, "name");
4746 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4747 if (ret != LDB_SUCCESS) {
4748 talloc_free(tmp_ctx);
4751 el->flags = LDB_FLAG_MOD_REPLACE;
4756 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4761 * No matter what has happned with other renames, try again to
4762 * get this to be under the deleted DN.
4764 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4765 /* now rename onto the new DN */
4766 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4767 if (ret != LDB_SUCCESS){
4768 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4769 ldb_dn_get_linearized(old_dn),
4770 ldb_dn_get_linearized(new_dn),
4771 ldb_errstring(ldb)));
4772 talloc_free(tmp_ctx);
4778 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4779 if (ret != LDB_SUCCESS) {
4780 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4781 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4782 talloc_free(tmp_ctx);
4786 talloc_free(tmp_ctx);
4788 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4791 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4793 return replmd_delete_internals(module, req, false);
4797 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4802 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4804 int ret = LDB_ERR_OTHER;
4805 /* TODO: do some error mapping */
4807 /* Let the caller know the full WERROR */
4808 ar->objs->error = status;
4814 static struct replPropertyMetaData1 *
4815 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4816 enum drsuapi_DsAttributeId attid)
4819 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4821 for (i = 0; i < rpmd_ctr->count; i++) {
4822 if (rpmd_ctr->array[i].attid == attid) {
4823 return &rpmd_ctr->array[i];
4831 return true if an update is newer than an existing entry
4832 see section 5.11 of MS-ADTS
4834 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4835 const struct GUID *update_invocation_id,
4836 uint32_t current_version,
4837 uint32_t update_version,
4838 NTTIME current_change_time,
4839 NTTIME update_change_time)
4841 if (update_version != current_version) {
4842 return update_version > current_version;
4844 if (update_change_time != current_change_time) {
4845 return update_change_time > current_change_time;
4847 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4850 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4851 struct replPropertyMetaData1 *new_m)
4853 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4854 &new_m->originating_invocation_id,
4857 cur_m->originating_change_time,
4858 new_m->originating_change_time);
4861 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4862 struct replPropertyMetaData1 *cur_m,
4863 struct replPropertyMetaData1 *new_m)
4868 * If the new replPropertyMetaData entry for this attribute is
4869 * not provided (this happens in the case where we look for
4870 * ATTID_name, but the name was not changed), then the local
4871 * state is clearly still current, as the remote
4872 * server didn't send it due to being older the high watermark
4875 if (new_m == NULL) {
4879 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4881 * if we compare equal then do an
4882 * update. This is used when a client
4883 * asks for a FULL_SYNC, and can be
4884 * used to recover a corrupt
4887 * This call is a bit tricky, what we
4888 * are doing it turning the 'is_newer'
4889 * call into a 'not is older' by
4890 * swapping cur_m and new_m, and negating the
4893 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4896 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4904 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4906 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4907 struct ldb_context *ldb,
4909 const char *four_char_prefix,
4910 const char *rdn_name,
4911 const struct ldb_val *rdn_value,
4914 struct ldb_val deleted_child_rdn_val;
4915 struct GUID_txt_buf guid_str;
4919 GUID_buf_string(&guid, &guid_str);
4921 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4923 ldb_asprintf_errstring(ldb, __location__
4924 ": Unable to add a formatted child to dn: %s",
4925 ldb_dn_get_linearized(dn));
4926 return LDB_ERR_OPERATIONS_ERROR;
4930 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4931 * we should truncate this value to ensure the RDN is not more than 255 chars.
4933 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4935 * "Naming constraints are not enforced for replicated
4936 * updates." so this is safe and we don't have to work out not
4937 * splitting a UTF8 char right now.
4939 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4942 * sizeof(guid_str.buf) will always be longer than
4943 * strlen(guid_str.buf) but we allocate using this and
4944 * waste the trailing bytes to avoid scaring folks
4945 * with memcpy() using strlen() below
4948 deleted_child_rdn_val.data
4949 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4951 rdn_value->length + 5
4952 + sizeof(guid_str.buf));
4953 if (!deleted_child_rdn_val.data) {
4954 ldb_asprintf_errstring(ldb, __location__
4955 ": Unable to add a formatted child to dn: %s",
4956 ldb_dn_get_linearized(dn));
4957 return LDB_ERR_OPERATIONS_ERROR;
4960 deleted_child_rdn_val.length =
4961 rdn_value->length + 5
4962 + strlen(guid_str.buf);
4964 SMB_ASSERT(deleted_child_rdn_val.length <
4965 talloc_get_size(deleted_child_rdn_val.data));
4968 * talloc won't allocate more than 256MB so we can't
4969 * overflow but just to be sure
4971 if (deleted_child_rdn_val.length < rdn_value->length) {
4972 return LDB_ERR_OPERATIONS_ERROR;
4975 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4976 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4977 four_char_prefix, 4);
4978 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4980 sizeof(guid_str.buf));
4982 /* Now set the value into the RDN, without parsing it */
4983 ret = ldb_dn_set_component(
4987 deleted_child_rdn_val);
4996 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4997 struct ldb_context *ldb,
5001 const struct ldb_val *rdn_val;
5002 const char *rdn_name;
5003 struct ldb_dn *new_dn;
5006 rdn_val = ldb_dn_get_rdn_val(dn);
5007 rdn_name = ldb_dn_get_rdn_name(dn);
5008 if (!rdn_val || !rdn_name) {
5012 new_dn = ldb_dn_get_parent(mem_ctx, dn);
5017 ret = replmd_make_prefix_child_dn(mem_ctx,
5023 if (ret != LDB_SUCCESS) {
5032 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
5033 struct ldb_context *ldb,
5035 const char *rdn_name,
5036 const struct ldb_val *rdn_value,
5039 return replmd_make_prefix_child_dn(tmp_ctx,
5049 perform a modify operation which sets the rDN and name attributes to
5050 their current values. This has the effect of changing these
5051 attributes to have been last updated by the current DC. This is
5052 needed to ensure that renames performed as part of conflict
5053 resolution are propagated to other DCs
5055 static int replmd_name_modify(struct replmd_replicated_request *ar,
5056 struct ldb_request *req, struct ldb_dn *dn)
5058 struct ldb_message *msg;
5059 const char *rdn_name;
5060 const struct ldb_val *rdn_val;
5061 const struct dsdb_attribute *rdn_attr;
5064 msg = ldb_msg_new(req);
5070 rdn_name = ldb_dn_get_rdn_name(dn);
5071 if (rdn_name == NULL) {
5075 /* normalize the rdn attribute name */
5076 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5077 if (rdn_attr == NULL) {
5080 rdn_name = rdn_attr->lDAPDisplayName;
5082 rdn_val = ldb_dn_get_rdn_val(dn);
5083 if (rdn_val == NULL) {
5087 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5090 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
5093 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5096 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
5101 * We have to mark this as a replicated update otherwise
5102 * schema_data may reject a rename in the schema partition
5105 ret = dsdb_module_modify(ar->module, msg,
5106 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5108 if (ret != LDB_SUCCESS) {
5109 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5110 ldb_dn_get_linearized(dn),
5111 ldb_errstring(ldb_module_get_ctx(ar->module))));
5121 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5122 ldb_dn_get_linearized(dn)));
5123 return LDB_ERR_OPERATIONS_ERROR;
5128 callback for conflict DN handling where we have renamed the incoming
5129 record. After renaming it, we need to ensure the change of name and
5130 rDN for the incoming record is seen as an originating update by this DC.
5132 This also handles updating lastKnownParent for entries sent to lostAndFound
5134 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5136 struct replmd_replicated_request *ar =
5137 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5138 struct ldb_dn *conflict_dn = NULL;
5141 if (ares->error != LDB_SUCCESS) {
5142 /* call the normal callback for everything except success */
5143 return replmd_op_callback(req, ares);
5146 switch (req->operation) {
5148 conflict_dn = req->op.add.message->dn;
5151 conflict_dn = req->op.mod.message->dn;
5154 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5157 /* perform a modify of the rDN and name of the record */
5158 ret = replmd_name_modify(ar, req, conflict_dn);
5159 if (ret != LDB_SUCCESS) {
5161 return replmd_op_callback(req, ares);
5164 if (ar->objs->objects[ar->index_current].last_known_parent) {
5165 struct ldb_message *msg = ldb_msg_new(req);
5167 ldb_module_oom(ar->module);
5168 return LDB_ERR_OPERATIONS_ERROR;
5171 msg->dn = req->op.add.message->dn;
5173 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5174 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5175 if (ret != LDB_SUCCESS) {
5176 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5177 ldb_module_oom(ar->module);
5180 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5182 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5183 if (ret != LDB_SUCCESS) {
5184 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5185 ldb_dn_get_linearized(msg->dn),
5186 ldb_errstring(ldb_module_get_ctx(ar->module))));
5192 return replmd_op_callback(req, ares);
5196 callback for replmd_replicated_apply_add()
5197 This copes with the creation of conflict records in the case where
5198 the DN exists, but with a different objectGUID
5200 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))
5202 struct ldb_dn *conflict_dn;
5203 struct replmd_replicated_request *ar =
5204 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5205 struct ldb_result *res;
5206 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5208 const struct ldb_val *omd_value;
5209 struct replPropertyMetaDataBlob omd, *rmd;
5210 enum ndr_err_code ndr_err;
5211 bool rename_incoming_record, rodc;
5212 struct replPropertyMetaData1 *rmd_name, *omd_name;
5213 struct ldb_message *msg;
5214 struct ldb_request *down_req = NULL;
5216 /* call the normal callback for success */
5217 if (ares->error == LDB_SUCCESS) {
5218 return callback(req, ares);
5222 * we have a conflict, and need to decide if we will keep the
5223 * new record or the old record
5226 msg = ar->objs->objects[ar->index_current].msg;
5227 conflict_dn = msg->dn;
5229 /* For failures other than conflicts, fail the whole operation here */
5230 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5231 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5232 ldb_dn_get_linearized(conflict_dn),
5233 ldb_errstring(ldb_module_get_ctx(ar->module)));
5235 return ldb_module_done(ar->req, NULL, NULL,
5236 LDB_ERR_OPERATIONS_ERROR);
5239 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5240 if (ret != LDB_SUCCESS) {
5241 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)));
5242 return ldb_module_done(ar->req, NULL, NULL,
5243 LDB_ERR_OPERATIONS_ERROR);
5249 * We are on an RODC, or were a GC for this
5250 * partition, so we have to fail this until
5251 * someone who owns the partition sorts it
5254 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5255 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5256 " - We must fail the operation until a master for this partition resolves the conflict",
5257 ldb_dn_get_linearized(conflict_dn));
5258 ret = LDB_ERR_OPERATIONS_ERROR;
5263 * first we need the replPropertyMetaData attribute from the
5264 * local, conflicting record
5266 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5268 DSDB_FLAG_NEXT_MODULE |
5269 DSDB_SEARCH_SHOW_DELETED |
5270 DSDB_SEARCH_SHOW_RECYCLED, req);
5271 if (ret != LDB_SUCCESS) {
5272 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5273 ldb_dn_get_linearized(conflict_dn)));
5277 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5278 if (omd_value == NULL) {
5279 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5280 ldb_dn_get_linearized(conflict_dn)));
5284 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5285 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5286 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5287 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5288 ldb_dn_get_linearized(conflict_dn)));
5292 rmd = ar->objs->objects[ar->index_current].meta_data;
5295 * we decide which is newer based on the RPMD on the name
5296 * attribute. See [MS-DRSR] ResolveNameConflict.
5298 * We expect omd_name to be present, as this is from a local
5299 * search, but while rmd_name should have been given to us by
5300 * the remote server, if it is missing we just prefer the
5302 * replmd_replPropertyMetaData1_new_should_be_taken()
5304 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5305 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5307 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5308 ldb_dn_get_linearized(conflict_dn)));
5313 * Should we preserve the current record, and so rename the
5314 * incoming record to be a conflict?
5316 rename_incoming_record
5317 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5318 omd_name, rmd_name);
5320 if (rename_incoming_record) {
5322 struct ldb_dn *new_dn;
5324 guid = samdb_result_guid(msg, "objectGUID");
5325 if (GUID_all_zero(&guid)) {
5326 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5327 ldb_dn_get_linearized(conflict_dn)));
5330 new_dn = replmd_conflict_dn(req,
5331 ldb_module_get_ctx(ar->module),
5332 conflict_dn, &guid);
5333 if (new_dn == NULL) {
5334 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5335 ldb_dn_get_linearized(conflict_dn)));
5339 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5340 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5342 /* re-submit the request, but with the new DN */
5343 callback = replmd_op_name_modify_callback;
5346 /* we are renaming the existing record */
5348 struct ldb_dn *new_dn;
5350 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5351 if (GUID_all_zero(&guid)) {
5352 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5353 ldb_dn_get_linearized(conflict_dn)));
5357 new_dn = replmd_conflict_dn(req,
5358 ldb_module_get_ctx(ar->module),
5359 conflict_dn, &guid);
5360 if (new_dn == NULL) {
5361 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5362 ldb_dn_get_linearized(conflict_dn)));
5366 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5367 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5369 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5370 DSDB_FLAG_OWN_MODULE, req);
5371 if (ret != LDB_SUCCESS) {
5372 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5373 ldb_dn_get_linearized(conflict_dn),
5374 ldb_dn_get_linearized(new_dn),
5375 ldb_errstring(ldb_module_get_ctx(ar->module))));
5380 * now we need to ensure that the rename is seen as an
5381 * originating update. We do that with a modify.
5383 ret = replmd_name_modify(ar, req, new_dn);
5384 if (ret != LDB_SUCCESS) {
5388 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5389 ldb_dn_get_linearized(req->op.add.message->dn)));
5392 ret = ldb_build_add_req(&down_req,
5393 ldb_module_get_ctx(ar->module),
5400 if (ret != LDB_SUCCESS) {
5403 LDB_REQ_SET_LOCATION(down_req);
5405 /* current partition control needed by "repmd_op_callback" */
5406 ret = ldb_request_add_control(down_req,
5407 DSDB_CONTROL_CURRENT_PARTITION_OID,
5409 if (ret != LDB_SUCCESS) {
5410 return replmd_replicated_request_error(ar, ret);
5413 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5414 /* this tells the partition module to make it a
5415 partial replica if creating an NC */
5416 ret = ldb_request_add_control(down_req,
5417 DSDB_CONTROL_PARTIAL_REPLICA,
5419 if (ret != LDB_SUCCESS) {
5420 return replmd_replicated_request_error(ar, ret);
5425 * Finally we re-run the add, otherwise the new record won't
5426 * exist, as we are here because of that exact failure!
5428 return ldb_next_request(ar->module, down_req);
5431 /* on failure make the caller get the error. This means
5432 * replication will stop with an error, but there is not much
5435 if (ret == LDB_SUCCESS) {
5436 ret = LDB_ERR_OPERATIONS_ERROR;
5438 return ldb_module_done(ar->req, NULL, NULL,
5443 callback for replmd_replicated_apply_add()
5444 This copes with the creation of conflict records in the case where
5445 the DN exists, but with a different objectGUID
5447 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5449 struct replmd_replicated_request *ar =
5450 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5452 if (ar->objs->objects[ar->index_current].last_known_parent) {
5453 /* This is like a conflict DN, where we put the object in LostAndFound
5454 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5455 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5458 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5462 this is called when a new object comes in over DRS
5464 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5466 struct ldb_context *ldb;
5467 struct ldb_request *change_req;
5468 enum ndr_err_code ndr_err;
5469 struct ldb_message *msg;
5470 struct replPropertyMetaDataBlob *md;
5471 struct ldb_val md_value;
5474 bool remote_isDeleted = false;
5477 time_t t = time(NULL);
5478 const struct ldb_val *rdn_val;
5479 struct replmd_private *replmd_private =
5480 talloc_get_type(ldb_module_get_private(ar->module),
5481 struct replmd_private);
5482 unix_to_nt_time(&now, t);
5484 ldb = ldb_module_get_ctx(ar->module);
5485 msg = ar->objs->objects[ar->index_current].msg;
5486 md = ar->objs->objects[ar->index_current].meta_data;
5487 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5489 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5490 if (ret != LDB_SUCCESS) {
5491 return replmd_replicated_request_error(ar, ret);
5494 ret = dsdb_msg_add_guid(msg,
5495 &ar->objs->objects[ar->index_current].object_guid,
5497 if (ret != LDB_SUCCESS) {
5498 return replmd_replicated_request_error(ar, ret);
5501 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5502 if (ret != LDB_SUCCESS) {
5503 return replmd_replicated_request_error(ar, ret);
5506 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5507 if (ret != LDB_SUCCESS) {
5508 return replmd_replicated_request_error(ar, ret);
5511 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5512 if (ret != LDB_SUCCESS) {
5513 return replmd_replicated_request_error(ar, ret);
5516 /* remove any message elements that have zero values */
5517 for (i=0; i<msg->num_elements; i++) {
5518 struct ldb_message_element *el = &msg->elements[i];
5520 if (el->num_values == 0) {
5521 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5522 ldb_asprintf_errstring(ldb, __location__
5523 ": empty objectClass sent on %s, aborting replication\n",
5524 ldb_dn_get_linearized(msg->dn));
5525 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5528 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5530 ldb_msg_remove_element(msg, &msg->elements[i]);
5537 struct GUID_txt_buf guid_txt;
5539 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5542 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5543 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5546 } else if (DEBUGLVL(4)) {
5547 struct GUID_txt_buf guid_txt;
5548 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5549 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5550 ldb_dn_get_linearized(msg->dn)));
5552 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5553 "isDeleted", false);
5556 * the meta data array is already sorted by the caller, except
5557 * for the RDN, which needs to be added.
5561 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5562 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5563 md, ar, now, is_schema_nc,
5565 if (ret != LDB_SUCCESS) {
5566 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5567 return replmd_replicated_request_error(ar, ret);
5570 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5571 if (ret != LDB_SUCCESS) {
5572 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5573 return replmd_replicated_request_error(ar, ret);
5576 for (i=0; i < md->ctr.ctr1.count; i++) {
5577 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5579 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5580 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5581 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5582 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5583 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5585 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5586 if (ret != LDB_SUCCESS) {
5587 return replmd_replicated_request_error(ar, ret);
5590 replmd_ldb_message_sort(msg, ar->schema);
5592 if (!remote_isDeleted) {
5593 ret = dsdb_module_schedule_sd_propagation(ar->module,
5594 ar->objs->partition_dn,
5596 if (ret != LDB_SUCCESS) {
5597 return replmd_replicated_request_error(ar, ret);
5601 ar->isDeleted = remote_isDeleted;
5603 ret = ldb_build_add_req(&change_req,
5609 replmd_op_add_callback,
5611 LDB_REQ_SET_LOCATION(change_req);
5612 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5614 /* current partition control needed by "repmd_op_callback" */
5615 ret = ldb_request_add_control(change_req,
5616 DSDB_CONTROL_CURRENT_PARTITION_OID,
5618 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5620 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5621 /* this tells the partition module to make it a
5622 partial replica if creating an NC */
5623 ret = ldb_request_add_control(change_req,
5624 DSDB_CONTROL_PARTIAL_REPLICA,
5626 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5629 return ldb_next_request(ar->module, change_req);
5632 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5633 struct ldb_reply *ares)
5635 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5636 struct replmd_replicated_request);
5640 return ldb_module_done(ar->req, NULL, NULL,
5641 LDB_ERR_OPERATIONS_ERROR);
5645 * The error NO_SUCH_OBJECT is not expected, unless the search
5646 * base is the partition DN, and that case doesn't happen here
5647 * because then we wouldn't get a parent_guid_value in any
5650 if (ares->error != LDB_SUCCESS) {
5651 return ldb_module_done(ar->req, ares->controls,
5652 ares->response, ares->error);
5655 switch (ares->type) {
5656 case LDB_REPLY_ENTRY:
5658 struct ldb_message *parent_msg = ares->message;
5659 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5660 struct ldb_dn *parent_dn = NULL;
5663 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5664 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5665 /* Per MS-DRSR 4.1.10.6.10
5666 * FindBestParentObject we need to move this
5667 * new object under a deleted object to
5669 struct ldb_dn *nc_root;
5671 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5672 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5673 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5674 "No suitable NC root found for %s. "
5675 "We need to move this object because parent object %s "
5676 "is deleted, but this object is not.",
5677 ldb_dn_get_linearized(msg->dn),
5678 ldb_dn_get_linearized(parent_msg->dn));
5679 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5680 } else if (ret != LDB_SUCCESS) {
5681 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5682 "Unable to find NC root for %s: %s. "
5683 "We need to move this object because parent object %s "
5684 "is deleted, but this object is not.",
5685 ldb_dn_get_linearized(msg->dn),
5686 ldb_errstring(ldb_module_get_ctx(ar->module)),
5687 ldb_dn_get_linearized(parent_msg->dn));
5688 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5691 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5693 DS_GUID_LOSTANDFOUND_CONTAINER,
5695 if (ret != LDB_SUCCESS) {
5696 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5697 "Unable to find LostAndFound Container for %s "
5698 "in partition %s: %s. "
5699 "We need to move this object because parent object %s "
5700 "is deleted, but this object is not.",
5701 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5702 ldb_errstring(ldb_module_get_ctx(ar->module)),
5703 ldb_dn_get_linearized(parent_msg->dn));
5704 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5706 ar->objs->objects[ar->index_current].last_known_parent
5707 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5711 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5714 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5716 comp_num = ldb_dn_get_comp_num(msg->dn);
5718 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5720 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5723 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5725 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5729 case LDB_REPLY_REFERRAL:
5730 /* we ignore referrals */
5733 case LDB_REPLY_DONE:
5735 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5736 struct GUID_txt_buf str_buf;
5737 if (ar->search_msg != NULL) {
5738 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5739 "No parent with GUID %s found for object locally known as %s",
5740 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5741 ldb_dn_get_linearized(ar->search_msg->dn));
5743 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5744 "No parent with GUID %s found for object remotely known as %s",
5745 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5746 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5750 * This error code is really important, as it
5751 * is the flag back to the callers to retry
5752 * this with DRSUAPI_DRS_GET_ANC, and so get
5753 * the parent objects before the child
5756 return ldb_module_done(ar->req, NULL, NULL,
5757 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5760 if (ar->search_msg != NULL) {
5761 ret = replmd_replicated_apply_merge(ar);
5763 ret = replmd_replicated_apply_add(ar);
5765 if (ret != LDB_SUCCESS) {
5766 return ldb_module_done(ar->req, NULL, NULL, ret);
5775 * Look for the parent object, so we put the new object in the right
5776 * place This is akin to NameObject in MS-DRSR - this routine and the
5777 * callbacks find the right parent name, and correct name for this
5781 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5783 struct ldb_context *ldb;
5787 struct ldb_request *search_req;
5788 static const char *attrs[] = {"isDeleted", NULL};
5789 struct GUID_txt_buf guid_str_buf;
5791 ldb = ldb_module_get_ctx(ar->module);
5793 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5794 if (ar->search_msg != NULL) {
5795 return replmd_replicated_apply_merge(ar);
5797 return replmd_replicated_apply_add(ar);
5801 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5804 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5805 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5807 ret = ldb_build_search_req(&search_req,
5810 ar->objs->partition_dn,
5816 replmd_replicated_apply_search_for_parent_callback,
5818 LDB_REQ_SET_LOCATION(search_req);
5820 ret = dsdb_request_add_controls(search_req,
5821 DSDB_SEARCH_SHOW_RECYCLED|
5822 DSDB_SEARCH_SHOW_DELETED|
5823 DSDB_SEARCH_SHOW_EXTENDED_DN);
5824 if (ret != LDB_SUCCESS) {
5828 return ldb_next_request(ar->module, search_req);
5832 handle renames that come in over DRS replication
5834 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5835 struct ldb_message *msg,
5836 struct ldb_request *parent,
5840 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5841 struct ldb_result *res;
5842 struct ldb_dn *conflict_dn;
5843 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5844 const struct ldb_val *omd_value;
5845 struct replPropertyMetaDataBlob omd, *rmd;
5846 enum ndr_err_code ndr_err;
5847 bool rename_incoming_record, rodc;
5848 struct replPropertyMetaData1 *rmd_name, *omd_name;
5849 struct ldb_dn *new_dn;
5852 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5853 ldb_dn_get_linearized(ar->search_msg->dn),
5854 ldb_dn_get_linearized(msg->dn)));
5857 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5858 DSDB_FLAG_NEXT_MODULE, ar->req);
5859 if (ret == LDB_SUCCESS) {
5860 talloc_free(tmp_ctx);
5865 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5866 talloc_free(tmp_ctx);
5867 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5868 ldb_dn_get_linearized(ar->search_msg->dn),
5869 ldb_dn_get_linearized(msg->dn),
5870 ldb_errstring(ldb_module_get_ctx(ar->module)));
5874 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5875 if (ret != LDB_SUCCESS) {
5876 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5877 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5878 ldb_errstring(ldb_module_get_ctx(ar->module)));
5879 return LDB_ERR_OPERATIONS_ERROR;
5882 * we have a conflict, and need to decide if we will keep the
5883 * new record or the old record
5886 conflict_dn = msg->dn;
5890 * We are on an RODC, or were a GC for this
5891 * partition, so we have to fail this until
5892 * someone who owns the partition sorts it
5895 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5896 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5897 " - We must fail the operation until a master for this partition resolves the conflict",
5898 ldb_dn_get_linearized(conflict_dn));
5899 ret = LDB_ERR_OPERATIONS_ERROR;
5904 * first we need the replPropertyMetaData attribute from the
5907 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5909 DSDB_FLAG_NEXT_MODULE |
5910 DSDB_SEARCH_SHOW_DELETED |
5911 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5912 if (ret != LDB_SUCCESS) {
5913 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5914 ldb_dn_get_linearized(conflict_dn)));
5918 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5919 if (omd_value == NULL) {
5920 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5921 ldb_dn_get_linearized(conflict_dn)));
5925 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5926 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5927 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5928 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5929 ldb_dn_get_linearized(conflict_dn)));
5933 rmd = ar->objs->objects[ar->index_current].meta_data;
5936 * we decide which is newer based on the RPMD on the name
5937 * attribute. See [MS-DRSR] ResolveNameConflict.
5939 * We expect omd_name to be present, as this is from a local
5940 * search, but while rmd_name should have been given to us by
5941 * the remote server, if it is missing we just prefer the
5943 * replmd_replPropertyMetaData1_new_should_be_taken()
5945 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5946 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5948 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5949 ldb_dn_get_linearized(conflict_dn)));
5954 * Should we preserve the current record, and so rename the
5955 * incoming record to be a conflict?
5957 rename_incoming_record =
5958 !replmd_replPropertyMetaData1_new_should_be_taken(
5959 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5960 omd_name, rmd_name);
5962 if (rename_incoming_record) {
5964 new_dn = replmd_conflict_dn(msg,
5965 ldb_module_get_ctx(ar->module),
5967 &ar->objs->objects[ar->index_current].object_guid);
5968 if (new_dn == NULL) {
5969 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5970 "Failed to form conflict DN for %s\n",
5971 ldb_dn_get_linearized(msg->dn));
5973 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5976 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5977 DSDB_FLAG_NEXT_MODULE, ar->req);
5978 if (ret != LDB_SUCCESS) {
5979 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5980 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5981 ldb_dn_get_linearized(conflict_dn),
5982 ldb_dn_get_linearized(ar->search_msg->dn),
5983 ldb_dn_get_linearized(new_dn),
5984 ldb_errstring(ldb_module_get_ctx(ar->module)));
5985 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5993 /* we are renaming the existing record */
5995 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5996 if (GUID_all_zero(&guid)) {
5997 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5998 ldb_dn_get_linearized(conflict_dn)));
6002 new_dn = replmd_conflict_dn(tmp_ctx,
6003 ldb_module_get_ctx(ar->module),
6004 conflict_dn, &guid);
6005 if (new_dn == NULL) {
6006 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
6007 ldb_dn_get_linearized(conflict_dn)));
6011 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
6012 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
6014 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
6015 DSDB_FLAG_OWN_MODULE, ar->req);
6016 if (ret != LDB_SUCCESS) {
6017 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
6018 ldb_dn_get_linearized(conflict_dn),
6019 ldb_dn_get_linearized(new_dn),
6020 ldb_errstring(ldb_module_get_ctx(ar->module))));
6025 * now we need to ensure that the rename is seen as an
6026 * originating update. We do that with a modify.
6028 ret = replmd_name_modify(ar, ar->req, new_dn);
6029 if (ret != LDB_SUCCESS) {
6033 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
6034 ldb_dn_get_linearized(ar->search_msg->dn),
6035 ldb_dn_get_linearized(msg->dn)));
6038 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6039 DSDB_FLAG_NEXT_MODULE, ar->req);
6040 if (ret != LDB_SUCCESS) {
6041 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6042 ldb_dn_get_linearized(ar->search_msg->dn),
6043 ldb_dn_get_linearized(msg->dn),
6044 ldb_errstring(ldb_module_get_ctx(ar->module))));
6048 talloc_free(tmp_ctx);
6052 * On failure make the caller get the error
6053 * This means replication will stop with an error,
6054 * but there is not much else we can do. In the
6055 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6058 if (ret == LDB_SUCCESS) {
6059 ret = LDB_ERR_OPERATIONS_ERROR;
6062 talloc_free(tmp_ctx);
6067 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6069 struct ldb_context *ldb;
6070 struct ldb_request *change_req;
6071 enum ndr_err_code ndr_err;
6072 struct ldb_message *msg;
6073 struct replPropertyMetaDataBlob *rmd;
6074 struct replPropertyMetaDataBlob omd;
6075 const struct ldb_val *omd_value;
6076 struct replPropertyMetaDataBlob nmd;
6077 struct ldb_val nmd_value;
6078 struct GUID remote_parent_guid;
6081 unsigned int removed_attrs = 0;
6083 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6084 bool isDeleted = false;
6085 bool local_isDeleted = false;
6086 bool remote_isDeleted = false;
6087 bool take_remote_isDeleted = false;
6088 bool sd_updated = false;
6089 bool renamed = false;
6090 bool is_schema_nc = false;
6092 const struct ldb_val *old_rdn, *new_rdn;
6093 struct replmd_private *replmd_private =
6094 talloc_get_type(ldb_module_get_private(ar->module),
6095 struct replmd_private);
6097 time_t t = time(NULL);
6098 unix_to_nt_time(&now, t);
6100 ldb = ldb_module_get_ctx(ar->module);
6101 msg = ar->objs->objects[ar->index_current].msg;
6103 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6105 rmd = ar->objs->objects[ar->index_current].meta_data;
6109 /* find existing meta data */
6110 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6112 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6113 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6114 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6115 nt_status = ndr_map_error2ntstatus(ndr_err);
6116 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6119 if (omd.version != 1) {
6120 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6125 struct GUID_txt_buf guid_txt;
6127 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6128 LDB_CHANGETYPE_MODIFY, msg);
6129 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6132 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6134 ndr_print_struct_string(s,
6135 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6136 "existing replPropertyMetaData",
6138 ndr_print_struct_string(s,
6139 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6140 "incoming replPropertyMetaData",
6143 } else if (DEBUGLVL(4)) {
6144 struct GUID_txt_buf guid_txt;
6146 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6147 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6149 ldb_dn_get_linearized(msg->dn)));
6152 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6153 "isDeleted", false);
6154 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6155 "isDeleted", false);
6158 * Fill in the remote_parent_guid with the GUID or an all-zero
6161 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6162 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6164 remote_parent_guid = GUID_zero();
6168 * To ensure we follow a complex rename chain around, we have
6169 * to confirm that the DN is the same (mostly to confirm the
6170 * RDN) and the parentGUID is the same.
6172 * This ensures we keep things under the correct parent, which
6173 * replmd_replicated_handle_rename() will do.
6176 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6177 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6181 * handle renames, even just by case that come in over
6182 * DRS. Changes in the parent DN don't hit us here,
6183 * because the search for a parent will clean up those
6186 * We also have already filtered out the case where
6187 * the peer has an older name to what we have (see
6188 * replmd_replicated_apply_search_callback())
6190 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6193 if (ret != LDB_SUCCESS) {
6194 ldb_debug(ldb, LDB_DEBUG_FATAL,
6195 "replmd_replicated_request rename %s => %s failed - %s\n",
6196 ldb_dn_get_linearized(ar->search_msg->dn),
6197 ldb_dn_get_linearized(msg->dn),
6198 ldb_errstring(ldb));
6199 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6202 if (renamed == true) {
6204 * Set the callback to one that will fix up the name
6205 * metadata on the new conflict DN
6207 callback = replmd_op_name_modify_callback;
6212 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6213 nmd.ctr.ctr1.array = talloc_array(ar,
6214 struct replPropertyMetaData1,
6215 nmd.ctr.ctr1.count);
6216 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6218 /* first copy the old meta data */
6219 for (i=0; i < omd.ctr.ctr1.count; i++) {
6220 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6225 /* now merge in the new meta data */
6226 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6229 for (j=0; j < ni; j++) {
6232 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6236 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6237 ar->objs->dsdb_repl_flags,
6238 &nmd.ctr.ctr1.array[j],
6239 &rmd->ctr.ctr1.array[i]);
6241 /* replace the entry */
6242 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6243 if (ar->seq_num == 0) {
6244 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6245 if (ret != LDB_SUCCESS) {
6246 return replmd_replicated_request_error(ar, ret);
6249 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6250 switch (nmd.ctr.ctr1.array[j].attid) {
6251 case DRSUAPI_ATTID_ntSecurityDescriptor:
6254 case DRSUAPI_ATTID_isDeleted:
6255 take_remote_isDeleted = true;
6264 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6265 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6266 msg->elements[i-removed_attrs].name,
6267 ldb_dn_get_linearized(msg->dn),
6268 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6271 /* we don't want to apply this change so remove the attribute */
6272 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6279 if (found) continue;
6281 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6282 if (ar->seq_num == 0) {
6283 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6284 if (ret != LDB_SUCCESS) {
6285 return replmd_replicated_request_error(ar, ret);
6288 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6289 switch (nmd.ctr.ctr1.array[ni].attid) {
6290 case DRSUAPI_ATTID_ntSecurityDescriptor:
6293 case DRSUAPI_ATTID_isDeleted:
6294 take_remote_isDeleted = true;
6303 * finally correct the size of the meta_data array
6305 nmd.ctr.ctr1.count = ni;
6307 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6308 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6311 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6312 &nmd, ar, now, is_schema_nc,
6314 if (ret != LDB_SUCCESS) {
6315 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6316 return replmd_replicated_request_error(ar, ret);
6320 * sort the new meta data array
6322 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6323 if (ret != LDB_SUCCESS) {
6324 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6329 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6332 * This also controls SD propagation below
6334 if (take_remote_isDeleted) {
6335 isDeleted = remote_isDeleted;
6337 isDeleted = local_isDeleted;
6340 ar->isDeleted = isDeleted;
6343 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6345 if (msg->num_elements == 0) {
6346 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6349 return replmd_replicated_apply_isDeleted(ar);
6352 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6353 ar->index_current, msg->num_elements);
6359 if (sd_updated && !isDeleted) {
6360 ret = dsdb_module_schedule_sd_propagation(ar->module,
6361 ar->objs->partition_dn,
6363 if (ret != LDB_SUCCESS) {
6364 return ldb_operr(ldb);
6368 /* create the meta data value */
6369 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6370 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6371 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6372 nt_status = ndr_map_error2ntstatus(ndr_err);
6373 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6377 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6378 * and replPopertyMetaData attributes
6380 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6381 if (ret != LDB_SUCCESS) {
6382 return replmd_replicated_request_error(ar, ret);
6384 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6385 if (ret != LDB_SUCCESS) {
6386 return replmd_replicated_request_error(ar, ret);
6388 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6389 if (ret != LDB_SUCCESS) {
6390 return replmd_replicated_request_error(ar, ret);
6393 replmd_ldb_message_sort(msg, ar->schema);
6395 /* we want to replace the old values */
6396 for (i=0; i < msg->num_elements; i++) {
6397 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6398 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6399 if (msg->elements[i].num_values == 0) {
6400 ldb_asprintf_errstring(ldb, __location__
6401 ": objectClass removed on %s, aborting replication\n",
6402 ldb_dn_get_linearized(msg->dn));
6403 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6409 struct GUID_txt_buf guid_txt;
6411 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6412 LDB_CHANGETYPE_MODIFY,
6414 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6415 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6419 } else if (DEBUGLVL(4)) {
6420 struct GUID_txt_buf guid_txt;
6422 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6423 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6425 ldb_dn_get_linearized(msg->dn)));
6428 ret = ldb_build_mod_req(&change_req,
6436 LDB_REQ_SET_LOCATION(change_req);
6437 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6439 /* current partition control needed by "repmd_op_callback" */
6440 ret = ldb_request_add_control(change_req,
6441 DSDB_CONTROL_CURRENT_PARTITION_OID,
6443 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6445 return ldb_next_request(ar->module, change_req);
6448 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6449 struct ldb_reply *ares)
6451 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6452 struct replmd_replicated_request);
6456 return ldb_module_done(ar->req, NULL, NULL,
6457 LDB_ERR_OPERATIONS_ERROR);
6459 if (ares->error != LDB_SUCCESS &&
6460 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6461 return ldb_module_done(ar->req, ares->controls,
6462 ares->response, ares->error);
6465 switch (ares->type) {
6466 case LDB_REPLY_ENTRY:
6467 ar->search_msg = talloc_steal(ar, ares->message);
6470 case LDB_REPLY_REFERRAL:
6471 /* we ignore referrals */
6474 case LDB_REPLY_DONE:
6476 struct replPropertyMetaData1 *md_remote;
6477 struct replPropertyMetaData1 *md_local;
6479 struct replPropertyMetaDataBlob omd;
6480 const struct ldb_val *omd_value;
6481 struct replPropertyMetaDataBlob *rmd;
6482 struct ldb_message *msg;
6484 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6485 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6488 * This is the ADD case, find the appropriate parent,
6489 * as this object doesn't exist locally:
6491 if (ar->search_msg == NULL) {
6492 ret = replmd_replicated_apply_search_for_parent(ar);
6493 if (ret != LDB_SUCCESS) {
6494 return ldb_module_done(ar->req, NULL, NULL, ret);
6501 * Otherwise, in the MERGE case, work out if we are
6502 * attempting a rename, and if so find the parent the
6503 * newly renamed object wants to belong under (which
6504 * may not be the parent in it's attached string DN
6506 rmd = ar->objs->objects[ar->index_current].meta_data;
6510 /* find existing meta data */
6511 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6513 enum ndr_err_code ndr_err;
6514 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6515 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6516 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6517 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6518 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6521 if (omd.version != 1) {
6522 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6526 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6528 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6529 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6530 && GUID_all_zero(&ar->local_parent_guid)) {
6531 DEBUG(0, ("Refusing to replicate new version of %s "
6532 "as local object has an all-zero parentGUID attribute, "
6533 "despite not being an NC root\n",
6534 ldb_dn_get_linearized(ar->search_msg->dn)));
6535 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6539 * now we need to check for double renames. We could have a
6540 * local rename pending which our replication partner hasn't
6541 * received yet. We choose which one wins by looking at the
6542 * attribute stamps on the two objects, the newer one wins.
6544 * This also simply applies the correct algorithms for
6545 * determining if a change was made to name at all, or
6546 * if the object has just been renamed under the same
6549 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6550 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6552 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6553 ldb_dn_get_linearized(ar->search_msg->dn)));
6554 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6558 * if there is no name attribute given then we have to assume the
6559 * object we've received has the older name
6561 if (replmd_replPropertyMetaData1_new_should_be_taken(
6562 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6563 md_local, md_remote)) {
6564 struct GUID_txt_buf p_guid_local;
6565 struct GUID_txt_buf p_guid_remote;
6566 msg = ar->objs->objects[ar->index_current].msg;
6568 /* Merge on the existing object, with rename */
6570 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6571 "as incoming object changing to %s under %s\n",
6572 ldb_dn_get_linearized(ar->search_msg->dn),
6573 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6574 ldb_dn_get_linearized(msg->dn),
6575 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6577 ret = replmd_replicated_apply_search_for_parent(ar);
6579 struct GUID_txt_buf p_guid_local;
6580 struct GUID_txt_buf p_guid_remote;
6581 msg = ar->objs->objects[ar->index_current].msg;
6584 * Merge on the existing object, force no
6585 * rename (code below just to explain why in
6589 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6590 ldb_dn_get_linearized(msg->dn)) == 0) {
6591 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6592 GUID_equal(&ar->local_parent_guid,
6593 ar->objs->objects[ar->index_current].parent_guid)
6595 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6596 "despite incoming object changing parent to %s\n",
6597 ldb_dn_get_linearized(ar->search_msg->dn),
6598 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6599 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6603 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6604 " and rejecting older rename to %s under %s\n",
6605 ldb_dn_get_linearized(ar->search_msg->dn),
6606 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6607 ldb_dn_get_linearized(msg->dn),
6608 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6612 * This assignment ensures that the strcmp()
6613 * and GUID_equal() calls in
6614 * replmd_replicated_apply_merge() avoids the
6617 ar->objs->objects[ar->index_current].parent_guid =
6618 &ar->local_parent_guid;
6620 msg->dn = ar->search_msg->dn;
6621 ret = replmd_replicated_apply_merge(ar);
6623 if (ret != LDB_SUCCESS) {
6624 return ldb_module_done(ar->req, NULL, NULL, ret);
6634 * Returns true if we can group together processing this link attribute,
6635 * i.e. it has the same source-object and attribute ID as other links
6636 * already in the group
6638 static bool la_entry_matches_group(struct la_entry *la_entry,
6639 struct la_group *la_group)
6641 struct la_entry *prev = la_group->la_entries;
6643 return (la_entry->la->attid == prev->la->attid &&
6644 GUID_equal(&la_entry->la->identifier->guid,
6645 &prev->la->identifier->guid));
6649 * Creates a new la_entry to store replication info for a single
6652 static struct la_entry *
6653 create_la_entry(struct replmd_private *replmd_private,
6654 struct drsuapi_DsReplicaLinkedAttribute *la,
6655 uint32_t dsdb_repl_flags)
6657 struct la_entry *la_entry;
6659 if (replmd_private->la_ctx == NULL) {
6660 replmd_private->la_ctx = talloc_new(replmd_private);
6662 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6663 if (la_entry == NULL) {
6666 la_entry->la = talloc(la_entry,
6667 struct drsuapi_DsReplicaLinkedAttribute);
6668 if (la_entry->la == NULL) {
6669 talloc_free(la_entry);
6672 *la_entry->la = *la;
6673 la_entry->dsdb_repl_flags = dsdb_repl_flags;
6676 * we need to steal the non-scalars so they stay
6677 * around until the end of the transaction
6679 talloc_steal(la_entry->la, la_entry->la->identifier);
6680 talloc_steal(la_entry->la, la_entry->la->value.blob);
6686 * Stores the linked attributes received in the replication chunk - these get
6687 * applied at the end of the transaction. We also check that each linked
6688 * attribute is valid, i.e. source and target objects are known.
6690 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6692 int ret = LDB_SUCCESS;
6694 struct ldb_module *module = ar->module;
6695 struct replmd_private *replmd_private =
6696 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6697 struct la_group *la_group = NULL;
6698 struct ldb_context *ldb;
6699 TALLOC_CTX *tmp_ctx = NULL;
6700 struct ldb_message *src_msg = NULL;
6701 const struct dsdb_attribute *attr = NULL;
6703 ldb = ldb_module_get_ctx(module);
6705 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6707 /* save away the linked attributes for the end of the transaction */
6708 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6709 struct la_entry *la_entry;
6712 /* create an entry to store the received link attribute info */
6713 la_entry = create_la_entry(replmd_private,
6714 &ar->objs->linked_attributes[i],
6715 ar->objs->dsdb_repl_flags);
6716 if (la_entry == NULL) {
6718 return LDB_ERR_OPERATIONS_ERROR;
6722 * check if we're still dealing with the same source object
6725 new_srcobj = (la_group == NULL ||
6726 !la_entry_matches_group(la_entry, la_group));
6730 /* get a new mem_ctx to lookup the source object */
6731 TALLOC_FREE(tmp_ctx);
6732 tmp_ctx = talloc_new(ar);
6733 if (tmp_ctx == NULL) {
6735 return LDB_ERR_OPERATIONS_ERROR;
6738 /* verify the link source exists */
6739 ret = replmd_get_la_entry_source(module, la_entry,
6744 * When we fail to find the source object, the error
6745 * code we pass back here is really important. It flags
6746 * back to the callers to retry this request with
6747 * DRSUAPI_DRS_GET_ANC. This case should never happen
6748 * if we're replicating from a Samba DC, but it is
6749 * needed to talk to a Windows DC
6751 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6752 WERROR err = WERR_DS_DRA_MISSING_PARENT;
6753 ret = replmd_replicated_request_werror(ar,
6759 ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6761 if (ret != LDB_SUCCESS) {
6765 /* group the links together by source-object for efficiency */
6767 la_group = talloc_zero(replmd_private->la_ctx,
6769 if (la_group == NULL) {
6771 return LDB_ERR_OPERATIONS_ERROR;
6773 DLIST_ADD(replmd_private->la_list, la_group);
6775 DLIST_ADD(la_group->la_entries, la_entry);
6776 replmd_private->total_links++;
6779 TALLOC_FREE(tmp_ctx);
6783 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6785 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6787 struct ldb_context *ldb;
6791 struct ldb_request *search_req;
6792 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6793 "parentGUID", "instanceType",
6794 "replPropertyMetaData", "nTSecurityDescriptor",
6795 "isDeleted", NULL };
6796 struct GUID_txt_buf guid_str_buf;
6798 if (ar->index_current >= ar->objs->num_objects) {
6801 * Now that we've applied all the objects, check the new linked
6802 * attributes and store them (we apply them in .prepare_commit)
6804 ret = replmd_store_linked_attributes(ar);
6806 if (ret != LDB_SUCCESS) {
6810 /* done applying objects, move on to the next stage */
6811 return replmd_replicated_uptodate_vector(ar);
6814 ldb = ldb_module_get_ctx(ar->module);
6815 ar->search_msg = NULL;
6816 ar->isDeleted = false;
6818 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6821 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6822 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6824 ret = ldb_build_search_req(&search_req,
6827 ar->objs->partition_dn,
6833 replmd_replicated_apply_search_callback,
6835 LDB_REQ_SET_LOCATION(search_req);
6837 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6839 if (ret != LDB_SUCCESS) {
6843 return ldb_next_request(ar->module, search_req);
6847 * Returns true if we need to do extra processing to handle deleted object
6848 * changes received via replication
6850 static bool replmd_should_apply_isDeleted(struct replmd_replicated_request *ar,
6851 struct ldb_message *msg)
6853 struct ldb_dn *deleted_objects_dn;
6856 if (!ar->isDeleted) {
6858 /* not a deleted object, so don't set isDeleted */
6862 ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module),
6864 &deleted_objects_dn);
6867 * if the Deleted Object container lookup failed, then just apply
6868 * isDeleted (note that it doesn't exist for the Schema partition)
6870 if (ret != LDB_SUCCESS) {
6875 * the Deleted Objects container has isDeleted set but is not entirely
6876 * a deleted object, so DON'T re-apply isDeleted to it
6878 if (ldb_dn_compare(msg->dn, deleted_objects_dn) == 0) {
6886 * This is essentially a wrapper for replmd_replicated_apply_next()
6888 * This is needed to ensure that both codepaths call this handler.
6890 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6892 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6894 bool apply_isDeleted;
6895 struct ldb_request *del_req = NULL;
6896 struct ldb_result *res = NULL;
6897 TALLOC_CTX *tmp_ctx = NULL;
6899 apply_isDeleted = replmd_should_apply_isDeleted(ar, msg);
6901 if (!apply_isDeleted) {
6904 ar->index_current++;
6905 return replmd_replicated_apply_next(ar);
6909 * Do a delete here again, so that if there is
6910 * anything local that conflicts with this
6911 * object being deleted, it is removed. This
6912 * includes links. See MS-DRSR 4.1.10.6.9
6915 * If the object is already deleted, and there
6916 * is no more work required, it doesn't do
6920 /* This has been updated to point to the DN we eventually did the modify on */
6922 tmp_ctx = talloc_new(ar);
6924 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6928 res = talloc_zero(tmp_ctx, struct ldb_result);
6930 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6931 talloc_free(tmp_ctx);
6935 /* Build a delete request, which hopefully will artually turn into nothing */
6936 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6940 ldb_modify_default_callback,
6942 LDB_REQ_SET_LOCATION(del_req);
6943 if (ret != LDB_SUCCESS) {
6944 talloc_free(tmp_ctx);
6949 * This is the guts of the call, call back
6950 * into our delete code, but setting the
6951 * re_delete flag so we delete anything that
6952 * shouldn't be there on a deleted or recycled
6955 ret = replmd_delete_internals(ar->module, del_req, true);
6956 if (ret == LDB_SUCCESS) {
6957 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6960 talloc_free(tmp_ctx);
6961 if (ret != LDB_SUCCESS) {
6965 ar->index_current++;
6966 return replmd_replicated_apply_next(ar);
6969 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6970 struct ldb_reply *ares)
6972 struct ldb_context *ldb;
6973 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6974 struct replmd_replicated_request);
6975 ldb = ldb_module_get_ctx(ar->module);
6978 return ldb_module_done(ar->req, NULL, NULL,
6979 LDB_ERR_OPERATIONS_ERROR);
6981 if (ares->error != LDB_SUCCESS) {
6982 return ldb_module_done(ar->req, ares->controls,
6983 ares->response, ares->error);
6986 if (ares->type != LDB_REPLY_DONE) {
6987 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6988 return ldb_module_done(ar->req, NULL, NULL,
6989 LDB_ERR_OPERATIONS_ERROR);
6994 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6997 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6999 struct ldb_context *ldb;
7000 struct ldb_request *change_req;
7001 enum ndr_err_code ndr_err;
7002 struct ldb_message *msg;
7003 struct replUpToDateVectorBlob ouv;
7004 const struct ldb_val *ouv_value;
7005 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
7006 struct replUpToDateVectorBlob nuv;
7007 struct ldb_val nuv_value;
7008 struct ldb_message_element *nuv_el = NULL;
7009 struct ldb_message_element *orf_el = NULL;
7010 struct repsFromToBlob nrf;
7011 struct ldb_val *nrf_value = NULL;
7012 struct ldb_message_element *nrf_el = NULL;
7016 time_t t = time(NULL);
7019 uint32_t instanceType;
7021 ldb = ldb_module_get_ctx(ar->module);
7022 ruv = ar->objs->uptodateness_vector;
7028 unix_to_nt_time(&now, t);
7030 if (ar->search_msg == NULL) {
7031 /* this happens for a REPL_OBJ call where we are
7032 creating the target object by replicating it. The
7033 subdomain join code does this for the partition DN
7035 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
7036 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7039 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
7040 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
7041 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
7042 ldb_dn_get_linearized(ar->search_msg->dn)));
7043 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7047 * first create the new replUpToDateVector
7049 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
7051 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
7052 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
7053 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7054 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7055 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7058 if (ouv.version != 2) {
7059 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7064 * the new uptodateness vector will at least
7065 * contain 1 entry, one for the source_dsa
7067 * plus optional values from our old vector and the one from the source_dsa
7069 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
7070 if (ruv) nuv.ctr.ctr2.count += ruv->count;
7071 nuv.ctr.ctr2.cursors = talloc_array(ar,
7072 struct drsuapi_DsReplicaCursor2,
7073 nuv.ctr.ctr2.count);
7074 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7076 /* first copy the old vector */
7077 for (i=0; i < ouv.ctr.ctr2.count; i++) {
7078 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
7082 /* merge in the source_dsa vector is available */
7083 for (i=0; (ruv && i < ruv->count); i++) {
7086 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7087 &ar->our_invocation_id)) {
7091 for (j=0; j < ni; j++) {
7092 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7093 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7099 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7100 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7105 if (found) continue;
7107 /* if it's not there yet, add it */
7108 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7113 * finally correct the size of the cursors array
7115 nuv.ctr.ctr2.count = ni;
7120 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7123 * create the change ldb_message
7125 msg = ldb_msg_new(ar);
7126 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7127 msg->dn = ar->search_msg->dn;
7129 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7130 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7131 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7132 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7133 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7135 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7136 if (ret != LDB_SUCCESS) {
7137 return replmd_replicated_request_error(ar, ret);
7139 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7142 * now create the new repsFrom value from the given repsFromTo1 structure
7146 nrf.ctr.ctr1 = *ar->objs->source_dsa;
7147 nrf.ctr.ctr1.last_attempt = now;
7148 nrf.ctr.ctr1.last_success = now;
7149 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7152 * first see if we already have a repsFrom value for the current source dsa
7153 * if so we'll later replace this value
7155 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7157 for (i=0; i < orf_el->num_values; i++) {
7158 struct repsFromToBlob *trf;
7160 trf = talloc(ar, struct repsFromToBlob);
7161 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7163 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7164 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7165 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7166 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7167 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7170 if (trf->version != 1) {
7171 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7175 * we compare the source dsa objectGUID not the invocation_id
7176 * because we want only one repsFrom value per source dsa
7177 * and when the invocation_id of the source dsa has changed we don't need
7178 * the old repsFrom with the old invocation_id
7180 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7181 &ar->objs->source_dsa->source_dsa_obj_guid)) {
7187 nrf_value = &orf_el->values[i];
7192 * copy over all old values to the new ldb_message
7194 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7195 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7200 * if we haven't found an old repsFrom value for the current source dsa
7201 * we'll add a new value
7204 struct ldb_val zero_value;
7205 ZERO_STRUCT(zero_value);
7206 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7207 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7209 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7212 /* we now fill the value which is already attached to ldb_message */
7213 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7215 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7216 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7217 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7218 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7222 * the ldb_message_element for the attribute, has all the old values and the new one
7223 * so we'll replace the whole attribute with all values
7225 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7227 if (CHECK_DEBUGLVL(4)) {
7228 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7229 LDB_CHANGETYPE_MODIFY,
7231 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7235 /* prepare the ldb_modify() request */
7236 ret = ldb_build_mod_req(&change_req,
7242 replmd_replicated_uptodate_modify_callback,
7244 LDB_REQ_SET_LOCATION(change_req);
7245 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7247 return ldb_next_request(ar->module, change_req);
7250 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7251 struct ldb_reply *ares)
7253 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7254 struct replmd_replicated_request);
7258 return ldb_module_done(ar->req, NULL, NULL,
7259 LDB_ERR_OPERATIONS_ERROR);
7261 if (ares->error != LDB_SUCCESS &&
7262 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7263 return ldb_module_done(ar->req, ares->controls,
7264 ares->response, ares->error);
7267 switch (ares->type) {
7268 case LDB_REPLY_ENTRY:
7269 ar->search_msg = talloc_steal(ar, ares->message);
7272 case LDB_REPLY_REFERRAL:
7273 /* we ignore referrals */
7276 case LDB_REPLY_DONE:
7277 ret = replmd_replicated_uptodate_modify(ar);
7278 if (ret != LDB_SUCCESS) {
7279 return ldb_module_done(ar->req, NULL, NULL, ret);
7288 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7290 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7291 struct replmd_private *replmd_private =
7292 talloc_get_type_abort(ldb_module_get_private(ar->module),
7293 struct replmd_private);
7295 static const char *attrs[] = {
7296 "replUpToDateVector",
7301 struct ldb_request *search_req;
7303 ar->search_msg = NULL;
7306 * Let the caller know that we did an originating updates
7308 ar->objs->originating_updates = replmd_private->originating_updates;
7310 ret = ldb_build_search_req(&search_req,
7313 ar->objs->partition_dn,
7319 replmd_replicated_uptodate_search_callback,
7321 LDB_REQ_SET_LOCATION(search_req);
7322 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7324 return ldb_next_request(ar->module, search_req);
7329 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7331 struct ldb_context *ldb;
7332 struct dsdb_extended_replicated_objects *objs;
7333 struct replmd_replicated_request *ar;
7334 struct ldb_control **ctrls;
7337 ldb = ldb_module_get_ctx(module);
7339 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7341 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7343 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7344 return LDB_ERR_PROTOCOL_ERROR;
7347 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7348 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7349 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7350 return LDB_ERR_PROTOCOL_ERROR;
7353 ar = replmd_ctx_init(module, req);
7355 return LDB_ERR_OPERATIONS_ERROR;
7357 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7358 ar->apply_mode = true;
7360 ar->schema = dsdb_get_schema(ldb, ar);
7362 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7364 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7365 return LDB_ERR_CONSTRAINT_VIOLATION;
7368 ctrls = req->controls;
7370 if (req->controls) {
7371 req->controls = talloc_memdup(ar, req->controls,
7372 talloc_get_size(req->controls));
7373 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7376 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7377 if (ret != LDB_SUCCESS) {
7381 /* If this change contained linked attributes in the body
7382 * (rather than in the links section) we need to update
7383 * backlinks in linked_attributes */
7384 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7385 if (ret != LDB_SUCCESS) {
7389 ar->controls = req->controls;
7390 req->controls = ctrls;
7392 return replmd_replicated_apply_next(ar);
7396 * Checks how to handle an missing target - either we need to fail the
7397 * replication and retry with GET_TGT, ignore the link and continue, or try to
7398 * add a partial link to an unknown target.
7400 static int replmd_allow_missing_target(struct ldb_module *module,
7401 TALLOC_CTX *mem_ctx,
7402 struct ldb_dn *target_dn,
7403 struct ldb_dn *source_dn,
7406 uint32_t dsdb_repl_flags,
7408 const char * missing_str)
7410 struct ldb_context *ldb = ldb_module_get_ctx(module);
7414 * we may not be able to resolve link targets properly when
7415 * dealing with subsets of objects, e.g. the source is a
7416 * critical object and the target isn't
7419 * When we implement Trusted Domains we need to consider
7420 * whether they get treated as an incomplete replica here or not
7422 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7425 * Ignore the link. We don't increase the highwater-mark in
7426 * the object subset cases, so subsequent replications should
7427 * resolve any missing links
7429 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7430 ldb_dn_get_linearized(target_dn),
7431 ldb_dn_get_linearized(source_dn)));
7432 *ignore_link = true;
7436 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7440 if (is_in_same_nc) {
7443 * if the target is already be up-to-date there's no point in
7444 * retrying. This could be due to bad timing, or if a target
7445 * on a one-way link was deleted. We ignore the link rather
7446 * than failing the replication cycle completely
7448 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7449 *ignore_link = true;
7450 DBG_WARNING("%s is %s "
7451 "but up to date. Ignoring link from %s\n",
7452 ldb_dn_get_linearized(target_dn), missing_str,
7453 ldb_dn_get_linearized(source_dn));
7457 /* otherwise fail the replication and retry with GET_TGT */
7458 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7460 ldb_dn_get_linearized(target_dn),
7461 GUID_string(mem_ctx, guid),
7462 ldb_dn_get_linearized(source_dn));
7463 return LDB_ERR_NO_SUCH_OBJECT;
7467 * The target of the cross-partition link is missing. Continue
7468 * and try to at least add the forward-link. This isn't great,
7469 * but a partial link can be fixed by dbcheck, so it's better
7470 * than dropping the link completely.
7472 *ignore_link = false;
7474 if (is_obj_commit) {
7477 * Only log this when we're actually committing the objects.
7478 * This avoids spurious logs, i.e. if we're just verifying the
7479 * received link during a join.
7481 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7482 missing_str, ldb_dn_get_linearized(target_dn),
7483 ldb_dn_get_linearized(source_dn));
7490 * Checks that the target object for a linked attribute exists.
7491 * @param guid returns the target object's GUID (is returned)if it exists)
7492 * @param ignore_link set to true if the linked attribute should be ignored
7493 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7495 static int replmd_check_target_exists(struct ldb_module *module,
7496 struct dsdb_dn *dsdb_dn,
7497 struct la_entry *la_entry,
7498 struct ldb_dn *source_dn,
7503 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7504 struct ldb_context *ldb = ldb_module_get_ctx(module);
7505 struct ldb_result *target_res;
7506 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7507 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7510 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7511 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7513 *ignore_link = false;
7514 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7516 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7519 * This strange behaviour (allowing a NULL/missing
7520 * GUID) originally comes from:
7522 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7523 * Author: Andrew Tridgell <tridge@samba.org>
7524 * Date: Mon Dec 21 21:21:55 2009 +1100
7526 * s4-drs: cope better with NULL GUIDS from DRS
7528 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7529 * need to match by DN if possible when seeing if we should update an
7532 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7534 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7536 DSDB_FLAG_NEXT_MODULE |
7537 DSDB_SEARCH_SHOW_RECYCLED |
7538 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7539 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7541 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7542 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7544 ldb_dn_get_linearized(dsdb_dn->dn),
7545 ldb_dn_get_linearized(source_dn));
7546 talloc_free(tmp_ctx);
7547 return LDB_ERR_OPERATIONS_ERROR;
7549 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7550 NULL, LDB_SCOPE_SUBTREE,
7552 DSDB_FLAG_NEXT_MODULE |
7553 DSDB_SEARCH_SHOW_RECYCLED |
7554 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7555 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7558 GUID_string(tmp_ctx, guid));
7561 if (ret != LDB_SUCCESS) {
7562 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7563 GUID_string(tmp_ctx, guid),
7564 ldb_errstring(ldb));
7565 talloc_free(tmp_ctx);
7569 if (target_res->count == 0) {
7572 * target object is unknown. Check whether to ignore the link,
7573 * fail the replication, or add a partial link
7575 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7576 source_dn, is_obj_commit, guid,
7577 la_entry->dsdb_repl_flags,
7578 ignore_link, "Unknown");
7580 } else if (target_res->count != 1) {
7581 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7582 GUID_string(tmp_ctx, guid));
7583 ret = LDB_ERR_OPERATIONS_ERROR;
7585 struct ldb_message *target_msg = target_res->msgs[0];
7587 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7589 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7590 replmd_deletion_state(module, target_msg,
7591 &target_deletion_state, NULL);
7594 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7595 * ProcessLinkValue(). Link updates should not be sent for
7596 * recycled and tombstone objects (deleting the links should
7597 * happen when we delete the object). This probably means our
7598 * copy of the target object isn't up to date.
7600 if (target_deletion_state >= OBJECT_RECYCLED) {
7603 * target object is deleted. Check whether to ignore the
7604 * link, fail the replication, or add a partial link
7606 ret = replmd_allow_missing_target(module, tmp_ctx,
7607 dsdb_dn->dn, source_dn,
7608 is_obj_commit, guid,
7609 la_entry->dsdb_repl_flags,
7610 ignore_link, "Deleted");
7614 talloc_free(tmp_ctx);
7619 * Extracts the key details about the source object for a
7620 * linked-attribute entry.
7621 * This returns the following details:
7622 * @param ret_attr the schema details for the linked attribute
7623 * @param source_msg the search result for the source object
7625 static int replmd_get_la_entry_source(struct ldb_module *module,
7626 struct la_entry *la_entry,
7627 TALLOC_CTX *mem_ctx,
7628 const struct dsdb_attribute **ret_attr,
7629 struct ldb_message **source_msg)
7631 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7632 struct ldb_context *ldb = ldb_module_get_ctx(module);
7633 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7635 const struct dsdb_attribute *attr;
7636 struct ldb_result *res;
7637 const char *attrs[4];
7640 linked_attributes[0]:
7641 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7643 identifier: struct drsuapi_DsReplicaObjectIdentifier
7644 __ndr_size : 0x0000003a (58)
7645 __ndr_size_sid : 0x00000000 (0)
7646 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7648 __ndr_size_dn : 0x00000000 (0)
7650 attid : DRSUAPI_ATTID_member (0x1F)
7651 value: struct drsuapi_DsAttributeValue
7652 __ndr_size : 0x0000007e (126)
7654 blob : DATA_BLOB length=126
7655 flags : 0x00000001 (1)
7656 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7657 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7658 meta_data: struct drsuapi_DsReplicaMetaData
7659 version : 0x00000015 (21)
7660 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7661 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7662 originating_usn : 0x000000000001e19c (123292)
7664 (for cases where the link is to a normal DN)
7665 &target: struct drsuapi_DsReplicaObjectIdentifier3
7666 __ndr_size : 0x0000007e (126)
7667 __ndr_size_sid : 0x0000001c (28)
7668 guid : 7639e594-db75-4086-b0d4-67890ae46031
7669 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7670 __ndr_size_dn : 0x00000022 (34)
7671 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7674 /* find the attribute being modified */
7675 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7677 struct GUID_txt_buf guid_str;
7678 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7680 GUID_buf_string(&la->identifier->guid,
7682 return LDB_ERR_OPERATIONS_ERROR;
7686 * All attributes listed here must be dealt with in some way
7687 * by replmd_process_linked_attribute() otherwise in the case
7688 * of isDeleted: FALSE the modify will fail with:
7690 * Failed to apply linked attribute change 'attribute 'isDeleted':
7691 * invalid modify flags on
7692 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7695 * This is becaue isDeleted is a Boolean, so FALSE is a
7696 * legitimate value (set by Samba's deletetest.py)
7698 attrs[0] = attr->lDAPDisplayName;
7699 attrs[1] = "isDeleted";
7700 attrs[2] = "isRecycled";
7704 * get the existing message from the db for the object with
7705 * this GUID, returning attribute being modified. We will then
7706 * use this msg as the basis for a modify call
7708 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7709 DSDB_FLAG_NEXT_MODULE |
7710 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7711 DSDB_SEARCH_SHOW_RECYCLED |
7712 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7713 DSDB_SEARCH_REVEAL_INTERNALS,
7715 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7716 if (ret != LDB_SUCCESS) {
7719 if (res->count != 1) {
7720 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7721 GUID_string(mem_ctx, &la->identifier->guid));
7722 return LDB_ERR_NO_SUCH_OBJECT;
7725 *source_msg = res->msgs[0];
7732 * Verifies the target object is known for a linked attribute
7734 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7735 TALLOC_CTX *mem_ctx,
7736 struct la_entry *la_entry,
7737 struct ldb_dn *src_dn,
7738 const struct dsdb_attribute *attr)
7740 int ret = LDB_SUCCESS;
7741 struct ldb_module *module = ar->module;
7742 struct dsdb_dn *tgt_dsdb_dn = NULL;
7743 struct GUID guid = GUID_zero();
7746 struct ldb_context *ldb = ldb_module_get_ctx(module);
7747 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7748 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7750 /* the value blob for the attribute holds the target object DN */
7751 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7752 la->value.blob, &tgt_dsdb_dn);
7753 if (!W_ERROR_IS_OK(status)) {
7754 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7755 attr->lDAPDisplayName,
7756 ldb_dn_get_linearized(src_dn),
7757 win_errstr(status));
7758 return LDB_ERR_OPERATIONS_ERROR;
7762 * We can skip the target object checks if we're only syncing critical
7763 * objects, or we know the target is up-to-date. If either case, we
7764 * still continue even if the target doesn't exist
7766 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7767 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7769 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7770 src_dn, false, &guid, &dummy);
7774 * When we fail to find the target object, the error code we pass
7775 * back here is really important. It flags back to the callers to
7776 * retry this request with DRSUAPI_DRS_GET_TGT
7778 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7779 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7786 * Finds the current active Parsed-DN value for a single-valued linked
7787 * attribute, if one exists.
7788 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7789 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7792 static int replmd_get_active_singleval_link(struct ldb_module *module,
7793 TALLOC_CTX *mem_ctx,
7794 struct parsed_dn pdn_list[],
7796 const struct dsdb_attribute *attr,
7797 struct parsed_dn **ret_pdn)
7803 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7805 /* nothing to do for multi-valued linked attributes */
7809 for (i = 0; i < count; i++) {
7810 int ret = LDB_SUCCESS;
7811 struct parsed_dn *pdn = &pdn_list[i];
7813 /* skip any inactive links */
7814 if (dsdb_dn_is_deleted_val(pdn->v)) {
7818 /* we've found an active value for this attribute */
7821 if (pdn->dsdb_dn == NULL) {
7822 struct ldb_context *ldb = ldb_module_get_ctx(module);
7824 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7825 attr->syntax->ldap_oid);
7831 /* no active link found */
7836 * @returns true if the replication linked attribute info is newer than we
7837 * already have in our DB
7838 * @param pdn the existing linked attribute info in our DB
7839 * @param la the new linked attribute info received during replication
7841 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7842 struct drsuapi_DsReplicaLinkedAttribute *la)
7844 /* see if this update is newer than what we have already */
7845 struct GUID invocation_id = GUID_zero();
7846 uint32_t version = 0;
7847 NTTIME change_time = 0;
7851 /* no existing info so update is newer */
7855 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7856 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7857 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7859 return replmd_update_is_newer(&invocation_id,
7860 &la->meta_data.originating_invocation_id,
7862 la->meta_data.version,
7864 la->meta_data.originating_change_time);
7868 * Marks an existing linked attribute value as deleted in the DB
7869 * @param pdn the parsed-DN of the target-value to delete
7871 static int replmd_delete_link_value(struct ldb_module *module,
7872 struct replmd_private *replmd_private,
7873 TALLOC_CTX *mem_ctx,
7874 struct ldb_dn *src_obj_dn,
7875 const struct dsdb_schema *schema,
7876 const struct dsdb_attribute *attr,
7879 struct GUID *target_guid,
7880 struct dsdb_dn *target_dsdb_dn,
7881 struct ldb_val *output_val)
7883 struct ldb_context *ldb = ldb_module_get_ctx(module);
7886 const struct GUID *invocation_id = NULL;
7890 unix_to_nt_time(&now, t);
7892 invocation_id = samdb_ntds_invocation_id(ldb);
7893 if (invocation_id == NULL) {
7894 return LDB_ERR_OPERATIONS_ERROR;
7897 /* if the existing link is active, remove its backlink */
7901 * NOTE WELL: After this we will never (at runtime) be
7902 * able to find this forward link (for instant
7903 * removal) if/when the link target is deleted.
7905 * We have dbcheck rules to cover this and cope otherwise
7906 * by filtering at runtime (i.e. in the extended_dn module).
7908 ret = replmd_add_backlink(module, replmd_private, schema,
7909 src_obj_dn, target_guid, false,
7911 if (ret != LDB_SUCCESS) {
7916 /* mark the existing value as deleted */
7917 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7918 target_dsdb_dn, invocation_id, seq_num,
7919 seq_num, now, true);
7924 * Checks for a conflict in single-valued link attributes, and tries to
7925 * resolve the problem if possible.
7927 * Single-valued links should only ever have one active value. If we already
7928 * have an active link value, and during replication we receive an active link
7929 * value for a different target DN, then we need to resolve this inconsistency
7930 * and determine which value should be active. If the received info is better/
7931 * newer than the existing link attribute, then we need to set our existing
7932 * link as deleted. If the received info is worse/older, then we should continue
7933 * to add it, but set it as an inactive link.
7935 * Note that this is a corner-case that is unlikely to happen (but if it does
7936 * happen, we don't want it to break replication completely).
7938 * @param pdn_being_modified the parsed DN corresponding to the received link
7939 * target (note this is NULL if the link does not already exist in our DB)
7940 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7941 * any existing active or inactive values for the attribute in our DB.
7942 * @param dsdb_dn the target DN for the received link attribute
7943 * @param add_as_inactive gets set to true if the received link is worse than
7944 * the existing link - it should still be added, but as an inactive link.
7946 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7947 struct replmd_private *replmd_private,
7948 TALLOC_CTX *mem_ctx,
7949 struct ldb_dn *src_obj_dn,
7950 struct drsuapi_DsReplicaLinkedAttribute *la,
7951 struct dsdb_dn *dsdb_dn,
7952 struct parsed_dn *pdn_being_modified,
7953 struct parsed_dn *pdn_list,
7954 struct ldb_message_element *old_el,
7955 const struct dsdb_schema *schema,
7956 const struct dsdb_attribute *attr,
7958 bool *add_as_inactive)
7960 struct parsed_dn *active_pdn = NULL;
7961 bool update_is_newer = false;
7965 * check if there's a conflict for single-valued links, i.e. an active
7966 * linked attribute already exists, but it has a different target value
7968 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7969 old_el->num_values, attr,
7972 if (ret != LDB_SUCCESS) {
7977 * If no active value exists (or the received info is for the currently
7978 * active value), then no conflict exists
7980 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7984 DBG_WARNING("Link conflict for %s attribute on %s\n",
7985 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7987 /* Work out how to resolve the conflict based on which info is better */
7988 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7990 if (update_is_newer) {
7991 DBG_WARNING("Using received value %s, over existing target %s\n",
7992 ldb_dn_get_linearized(dsdb_dn->dn),
7993 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7996 * Delete our existing active link. The received info will then
7997 * be added (through normal link processing) as the active value
7999 ret = replmd_delete_link_value(module, replmd_private, old_el,
8000 src_obj_dn, schema, attr,
8001 seq_num, true, &active_pdn->guid,
8002 active_pdn->dsdb_dn,
8005 if (ret != LDB_SUCCESS) {
8009 DBG_WARNING("Using existing target %s, over received value %s\n",
8010 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
8011 ldb_dn_get_linearized(dsdb_dn->dn));
8014 * we want to keep our existing active link and add the
8015 * received link as inactive
8017 *add_as_inactive = true;
8024 * Processes one linked attribute received via replication.
8025 * @param src_dn the DN of the source object for the link
8026 * @param attr schema info for the linked attribute
8027 * @param la_entry the linked attribute info received via DRS
8028 * @param element_ctx mem context for msg->element[] (when adding a new value
8029 * we need to realloc old_el->values)
8030 * @param old_el the corresponding msg->element[] for the linked attribute
8031 * @param pdn_list a (binary-searchable) parsed DN array for the existing link
8032 * values in the msg. E.g. for a group, this is the existing members.
8033 * @param change what got modified: either nothing, an existing link value was
8034 * modified, or a new link value was added.
8035 * @returns LDB_SUCCESS if OK, an error otherwise
8037 static int replmd_process_linked_attribute(struct ldb_module *module,
8038 TALLOC_CTX *mem_ctx,
8039 struct replmd_private *replmd_private,
8040 struct ldb_dn *src_dn,
8041 const struct dsdb_attribute *attr,
8042 struct la_entry *la_entry,
8043 struct ldb_request *parent,
8044 struct ldb_message_element *old_el,
8045 TALLOC_CTX *element_ctx,
8046 struct parsed_dn *pdn_list,
8047 replmd_link_changed *change)
8049 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
8050 struct ldb_context *ldb = ldb_module_get_ctx(module);
8051 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
8053 struct dsdb_dn *dsdb_dn = NULL;
8054 uint64_t seq_num = 0;
8055 struct parsed_dn *pdn, *next;
8056 struct GUID guid = GUID_zero();
8057 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
8059 struct dsdb_dn *old_dsdb_dn = NULL;
8060 struct ldb_val *val_to_update = NULL;
8061 bool add_as_inactive = false;
8064 *change = LINK_CHANGE_NONE;
8066 /* the value blob for the attribute holds the target object DN */
8067 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
8068 la->value.blob, &dsdb_dn);
8069 if (!W_ERROR_IS_OK(status)) {
8070 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8071 attr->lDAPDisplayName,
8072 ldb_dn_get_linearized(src_dn),
8073 win_errstr(status));
8074 return LDB_ERR_OPERATIONS_ERROR;
8077 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
8078 true, &guid, &ignore_link);
8080 if (ret != LDB_SUCCESS) {
8085 * there are some cases where the target object doesn't exist, but it's
8086 * OK to ignore the linked attribute
8092 /* see if this link already exists */
8093 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8096 dsdb_dn->extra_part, 0,
8098 attr->syntax->ldap_oid,
8100 if (ret != LDB_SUCCESS) {
8104 if (!replmd_link_update_is_newer(pdn, la)) {
8105 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8106 old_el->name, ldb_dn_get_linearized(src_dn),
8107 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8111 /* get a seq_num for this change */
8112 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8113 if (ret != LDB_SUCCESS) {
8118 * check for single-valued link conflicts, i.e. an active linked
8119 * attribute already exists, but it has a different target value
8122 ret = replmd_check_singleval_la_conflict(module, replmd_private,
8123 mem_ctx, src_dn, la,
8124 dsdb_dn, pdn, pdn_list,
8125 old_el, schema, attr,
8128 if (ret != LDB_SUCCESS) {
8134 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8136 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8137 /* remove the existing backlink */
8138 ret = replmd_add_backlink(module, replmd_private,
8141 &pdn->guid, false, attr,
8143 if (ret != LDB_SUCCESS) {
8148 val_to_update = pdn->v;
8149 old_dsdb_dn = pdn->dsdb_dn;
8150 *change = LINK_CHANGE_MODIFIED;
8156 * We know where the new one needs to be, from the *next
8157 * pointer into pdn_list.
8160 offset = old_el->num_values;
8162 if (next->dsdb_dn == NULL) {
8163 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8164 attr->syntax->ldap_oid);
8165 if (ret != LDB_SUCCESS) {
8169 offset = next - pdn_list;
8170 if (offset > old_el->num_values) {
8171 return LDB_ERR_OPERATIONS_ERROR;
8175 old_el->values = talloc_realloc(element_ctx, old_el->values,
8176 struct ldb_val, old_el->num_values+1);
8177 if (!old_el->values) {
8178 ldb_module_oom(module);
8179 return LDB_ERR_OPERATIONS_ERROR;
8182 if (offset != old_el->num_values) {
8183 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8184 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8187 old_el->num_values++;
8189 val_to_update = &old_el->values[offset];
8191 *change = LINK_CHANGE_ADDED;
8194 /* set the link attribute's value to the info that was received */
8195 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8196 &la->meta_data.originating_invocation_id,
8197 la->meta_data.originating_usn, seq_num,
8198 la->meta_data.originating_change_time,
8199 la->meta_data.version,
8201 if (ret != LDB_SUCCESS) {
8205 if (add_as_inactive) {
8207 /* Set the new link as inactive/deleted to avoid conflicts */
8208 ret = replmd_delete_link_value(module, replmd_private, old_el,
8209 src_dn, schema, attr, seq_num,
8210 false, &guid, dsdb_dn,
8213 if (ret != LDB_SUCCESS) {
8217 } else if (active) {
8219 /* if the new link is active, then add the new backlink */
8220 ret = replmd_add_backlink(module, replmd_private,
8225 if (ret != LDB_SUCCESS) {
8230 ret = dsdb_check_single_valued_link(attr, old_el);
8231 if (ret != LDB_SUCCESS) {
8235 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8240 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8242 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8243 return replmd_extended_replicated_objects(module, req);
8246 return ldb_next_request(module, req);
8251 we hook into the transaction operations to allow us to
8252 perform the linked attribute updates at the end of the whole
8253 transaction. This allows a forward linked attribute to be created
8254 before the object is created. During a vampire, w2k8 sends us linked
8255 attributes before the objects they are part of.
8257 static int replmd_start_transaction(struct ldb_module *module)
8259 /* create our private structure for this transaction */
8260 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8261 struct replmd_private);
8262 replmd_txn_cleanup(replmd_private);
8264 /* free any leftover mod_usn records from cancelled
8266 while (replmd_private->ncs) {
8267 struct nc_entry *e = replmd_private->ncs;
8268 DLIST_REMOVE(replmd_private->ncs, e);
8272 replmd_private->originating_updates = false;
8274 return ldb_next_start_trans(module);
8278 * Processes a group of linked attributes that apply to the same source-object
8279 * and attribute-ID (and were received in the same replication chunk).
8281 static int replmd_process_la_group(struct ldb_module *module,
8282 struct replmd_private *replmd_private,
8283 struct la_group *la_group)
8285 struct la_entry *la = NULL;
8286 struct la_entry *prev = NULL;
8288 TALLOC_CTX *tmp_ctx = NULL;
8289 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8290 struct ldb_message *msg = NULL;
8291 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8292 struct ldb_context *ldb = ldb_module_get_ctx(module);
8293 const struct dsdb_attribute *attr = NULL;
8294 struct ldb_message_element *old_el = NULL;
8295 struct parsed_dn *pdn_list = NULL;
8296 replmd_link_changed change_type;
8297 uint32_t num_changes = 0;
8299 uint64_t seq_num = 0;
8301 tmp_ctx = talloc_new(la_group);
8302 if (tmp_ctx == NULL) {
8303 return ldb_oom(ldb);
8307 * get the attribute being modified and the search result for the
8310 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8313 if (ret != LDB_SUCCESS) {
8318 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8319 * ProcessLinkValue, because link updates are not applied to
8320 * recycled and tombstone objects. We don't have to delete
8321 * any existing link, that should have happened when the
8322 * object deletion was replicated or initiated.
8324 * This needs isDeleted and isRecycled to be included as
8325 * attributes in the search and so in msg if set.
8327 replmd_deletion_state(module, msg, &deletion_state, NULL);
8329 if (deletion_state >= OBJECT_RECYCLED) {
8330 TALLOC_FREE(tmp_ctx);
8335 * Now that we know the deletion_state, remove the extra
8336 * attributes added for that purpose. We need to do this
8337 * otherwise in the case of isDeleted: FALSE the modify will
8340 * Failed to apply linked attribute change 'attribute 'isDeleted':
8341 * invalid modify flags on
8342 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8345 * This is becaue isDeleted is a Boolean, so FALSE is a
8346 * legitimate value (set by Samba's deletetest.py)
8348 ldb_msg_remove_attr(msg, "isDeleted");
8349 ldb_msg_remove_attr(msg, "isRecycled");
8351 /* get the msg->element[] for the link attribute being processed */
8352 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8353 if (old_el == NULL) {
8354 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
8355 LDB_FLAG_MOD_REPLACE, &old_el);
8356 if (ret != LDB_SUCCESS) {
8357 ldb_module_oom(module);
8358 return LDB_ERR_OPERATIONS_ERROR;
8361 old_el->flags = LDB_FLAG_MOD_REPLACE;
8365 * go through and process the link target value(s) for this particular
8366 * source object and attribute. For optimization, the same msg is used
8367 * across multiple calls to replmd_process_linked_attribute().
8368 * Note that we should not add or remove any msg attributes inside the
8369 * loop (we should only add/modify *values* for the attribute being
8370 * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8371 * pointers will be invalidated
8373 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8374 prev = DLIST_PREV(la);
8375 DLIST_REMOVE(la_group->la_entries, la);
8378 * parse the existing links (this can be costly for a large
8379 * group, so we try to minimize the times we do it)
8381 if (pdn_list == NULL) {
8382 ret = get_parsed_dns_trusted_fallback(module,
8386 attr->syntax->ldap_oid,
8389 if (ret != LDB_SUCCESS) {
8393 ret = replmd_process_linked_attribute(module, tmp_ctx,
8395 msg->dn, attr, la, NULL,
8396 msg->elements, old_el,
8397 pdn_list, &change_type);
8398 if (ret != LDB_SUCCESS) {
8399 replmd_txn_cleanup(replmd_private);
8404 * Adding a link reallocs memory, and so invalidates all the
8405 * pointers in pdn_list. Reparse the PDNs on the next loop
8407 if (change_type == LINK_CHANGE_ADDED) {
8408 TALLOC_FREE(pdn_list);
8411 if (change_type != LINK_CHANGE_NONE) {
8415 if ((++replmd_private->num_processed % 8192) == 0) {
8416 DBG_NOTICE("Processed %u/%u linked attributes\n",
8417 replmd_private->num_processed,
8418 replmd_private->total_links);
8423 * it's possible we're already up-to-date and so don't need to modify
8424 * the object at all (e.g. doing a 'drs replicate --full-sync')
8426 if (num_changes == 0) {
8427 TALLOC_FREE(tmp_ctx);
8432 * Note that adding the whenChanged/etc attributes below will realloc
8433 * msg->elements, invalidating the existing element/parsed-DN pointers
8436 TALLOC_FREE(pdn_list);
8438 /* update whenChanged/uSNChanged as the object has changed */
8440 ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
8442 if (ret != LDB_SUCCESS) {
8446 ret = add_time_element(msg, "whenChanged", t);
8447 if (ret != LDB_SUCCESS) {
8452 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8453 if (ret != LDB_SUCCESS) {
8458 /* apply the link changes to the source object */
8459 ret = linked_attr_modify(module, msg, NULL);
8460 if (ret != LDB_SUCCESS) {
8461 ldb_debug(ldb, LDB_DEBUG_WARNING,
8462 "Failed to apply linked attribute change "
8463 "Error: '%s' DN: '%s' Attribute: '%s'\n",
8465 ldb_dn_get_linearized(msg->dn),
8466 attr->lDAPDisplayName);
8467 TALLOC_FREE(tmp_ctx);
8471 TALLOC_FREE(tmp_ctx);
8476 on prepare commit we loop over our queued la_context structures and
8479 static int replmd_prepare_commit(struct ldb_module *module)
8481 struct replmd_private *replmd_private =
8482 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8483 struct la_group *la_group, *prev;
8486 if (replmd_private->la_list != NULL) {
8487 DBG_NOTICE("Processing linked attributes\n");
8491 * Walk the list of linked attributes from DRS replication.
8493 * We walk backwards, to do the first entry first, as we
8494 * added the entries with DLIST_ADD() which puts them at the
8497 * Links are grouped together so we process links for the same
8498 * source object in one go.
8500 for (la_group = DLIST_TAIL(replmd_private->la_list);
8504 prev = DLIST_PREV(la_group);
8505 DLIST_REMOVE(replmd_private->la_list, la_group);
8506 ret = replmd_process_la_group(module, replmd_private,
8508 if (ret != LDB_SUCCESS) {
8509 replmd_txn_cleanup(replmd_private);
8514 replmd_txn_cleanup(replmd_private);
8516 /* possibly change @REPLCHANGED */
8517 ret = replmd_notify_store(module, NULL);
8518 if (ret != LDB_SUCCESS) {
8522 return ldb_next_prepare_commit(module);
8525 static int replmd_del_transaction(struct ldb_module *module)
8527 struct replmd_private *replmd_private =
8528 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8529 replmd_txn_cleanup(replmd_private);
8531 return ldb_next_del_trans(module);
8535 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8536 .name = "repl_meta_data",
8537 .init_context = replmd_init,
8539 .modify = replmd_modify,
8540 .rename = replmd_rename,
8541 .del = replmd_delete,
8542 .extended = replmd_extended,
8543 .start_transaction = replmd_start_transaction,
8544 .prepare_commit = replmd_prepare_commit,
8545 .del_transaction = replmd_del_transaction,
8548 int ldb_repl_meta_data_module_init(const char *version)
8550 LDB_MODULE_CHECK_VERSION(version);
8551 return ldb_register_module(&ldb_repl_meta_data_module_ops);