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"
55 #define DBGC_CLASS DBGC_DRS_REPL
58 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
59 * Deleted Objects Container
61 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
63 struct replmd_private {
65 struct la_entry *la_list;
67 struct nc_entry *prev, *next;
70 uint64_t mod_usn_urgent;
72 struct ldb_dn *schema_dn;
73 bool originating_updates;
78 struct la_entry *next, *prev;
79 struct drsuapi_DsReplicaLinkedAttribute *la;
80 uint32_t dsdb_repl_flags;
83 struct replmd_replicated_request {
84 struct ldb_module *module;
85 struct ldb_request *req;
87 const struct dsdb_schema *schema;
88 struct GUID our_invocation_id;
90 /* the controls we pass down */
91 struct ldb_control **controls;
94 * Backlinks for the replmd_add() case (we want to create
95 * backlinks after creating the user, but before the end of
98 struct la_backlink *la_backlinks;
100 /* details for the mode where we apply a bunch of inbound replication meessages */
102 uint32_t index_current;
103 struct dsdb_extended_replicated_objects *objs;
105 struct ldb_message *search_msg;
106 struct GUID local_parent_guid;
114 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
115 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
116 static int replmd_check_upgrade_links(struct ldb_context *ldb,
117 struct parsed_dn *dns, uint32_t count,
118 struct ldb_message_element *el,
119 const char *ldap_oid);
120 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
121 struct la_entry *la);
123 enum urgent_situation {
124 REPL_URGENT_ON_CREATE = 1,
125 REPL_URGENT_ON_UPDATE = 2,
126 REPL_URGENT_ON_DELETE = 4
129 enum deletion_state {
130 OBJECT_NOT_DELETED=1,
137 static void replmd_deletion_state(struct ldb_module *module,
138 const struct ldb_message *msg,
139 enum deletion_state *current_state,
140 enum deletion_state *next_state)
143 bool enabled = false;
146 *current_state = OBJECT_REMOVED;
147 if (next_state != NULL) {
148 *next_state = OBJECT_REMOVED;
153 ret = dsdb_recyclebin_enabled(module, &enabled);
154 if (ret != LDB_SUCCESS) {
158 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
160 *current_state = OBJECT_TOMBSTONE;
161 if (next_state != NULL) {
162 *next_state = OBJECT_REMOVED;
167 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
168 *current_state = OBJECT_RECYCLED;
169 if (next_state != NULL) {
170 *next_state = OBJECT_REMOVED;
175 *current_state = OBJECT_DELETED;
176 if (next_state != NULL) {
177 *next_state = OBJECT_RECYCLED;
182 *current_state = OBJECT_NOT_DELETED;
183 if (next_state == NULL) {
188 *next_state = OBJECT_DELETED;
190 *next_state = OBJECT_TOMBSTONE;
194 static const struct {
195 const char *update_name;
196 enum urgent_situation repl_situation;
197 } urgent_objects[] = {
198 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
199 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
200 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
201 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
202 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
203 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
207 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
208 static const char *urgent_attrs[] = {
211 "userAccountControl",
216 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
217 enum urgent_situation situation)
220 for (i=0; urgent_objects[i].update_name; i++) {
222 if ((situation & urgent_objects[i].repl_situation) == 0) {
226 for (j=0; j<objectclass_el->num_values; j++) {
227 const struct ldb_val *v = &objectclass_el->values[j];
228 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
236 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
238 if (ldb_attr_in_list(urgent_attrs, el->name)) {
244 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
247 initialise the module
248 allocate the private structure and build the list
249 of partition DNs for use by replmd_notify()
251 static int replmd_init(struct ldb_module *module)
253 struct replmd_private *replmd_private;
254 struct ldb_context *ldb = ldb_module_get_ctx(module);
255 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
256 struct ldb_dn *samba_dsdb_dn;
257 struct ldb_result *res;
259 TALLOC_CTX *frame = talloc_stackframe();
260 replmd_private = talloc_zero(module, struct replmd_private);
261 if (replmd_private == NULL) {
264 return LDB_ERR_OPERATIONS_ERROR;
266 ldb_module_set_private(module, replmd_private);
268 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
270 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
271 if (!samba_dsdb_dn) {
276 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
277 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
278 if (ret == LDB_SUCCESS) {
279 replmd_private->sorted_links
280 = ldb_msg_check_string_attribute(res->msgs[0],
281 SAMBA_COMPATIBLE_FEATURES_ATTR,
282 SAMBA_SORTED_LINKS_FEATURE);
286 return ldb_next_init(module);
290 cleanup our per-transaction contexts
292 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
294 talloc_free(replmd_private->la_ctx);
295 replmd_private->la_list = NULL;
296 replmd_private->la_ctx = NULL;
302 struct la_backlink *next, *prev;
303 const char *attr_name;
304 struct ldb_dn *forward_dn;
305 struct GUID target_guid;
310 a ldb_modify request operating on modules below the
313 static int linked_attr_modify(struct ldb_module *module,
314 const struct ldb_message *message,
315 struct ldb_request *parent)
317 struct ldb_request *mod_req;
319 struct ldb_context *ldb = ldb_module_get_ctx(module);
320 TALLOC_CTX *tmp_ctx = talloc_new(module);
321 struct ldb_result *res;
323 res = talloc_zero(tmp_ctx, struct ldb_result);
325 talloc_free(tmp_ctx);
326 return ldb_oom(ldb_module_get_ctx(module));
329 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
333 ldb_modify_default_callback,
335 LDB_REQ_SET_LOCATION(mod_req);
336 if (ret != LDB_SUCCESS) {
337 talloc_free(tmp_ctx);
341 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
343 if (ret != LDB_SUCCESS) {
347 /* Run the new request */
348 ret = ldb_next_request(module, mod_req);
350 if (ret == LDB_SUCCESS) {
351 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
354 talloc_free(tmp_ctx);
359 process a backlinks we accumulated during a transaction, adding and
360 deleting the backlinks from the target objects
362 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
364 struct ldb_dn *target_dn, *source_dn;
366 struct ldb_context *ldb = ldb_module_get_ctx(module);
367 struct ldb_message *msg;
368 TALLOC_CTX *frame = talloc_stackframe();
374 - construct ldb_message
375 - either an add or a delete
377 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
378 if (ret != LDB_SUCCESS) {
379 struct GUID_txt_buf guid_str;
380 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
381 GUID_buf_string(&bl->target_guid, &guid_str));
382 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
387 msg = ldb_msg_new(frame);
389 ldb_module_oom(module);
391 return LDB_ERR_OPERATIONS_ERROR;
394 source_dn = ldb_dn_copy(frame, bl->forward_dn);
396 ldb_module_oom(module);
398 return LDB_ERR_OPERATIONS_ERROR;
400 /* Filter down to the attributes we want in the backlink */
401 const char *accept[] = { "GUID", "SID", NULL };
402 ldb_dn_extended_filter(source_dn, accept);
405 /* construct a ldb_message for adding/deleting the backlink */
407 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
409 ldb_module_oom(module);
411 return LDB_ERR_OPERATIONS_ERROR;
413 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
414 if (ret != LDB_SUCCESS) {
418 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
420 /* a backlink should never be single valued. Unfortunately the
421 exchange schema has a attribute
422 msExchBridgeheadedLocalConnectorsDNBL which is single
423 valued and a backlink. We need to cope with that by
424 ignoring the single value flag */
425 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
427 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
428 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
429 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
430 cope with possible corruption where the backlink has
431 already been removed */
432 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
433 ldb_dn_get_linearized(target_dn),
434 ldb_dn_get_linearized(source_dn),
435 ldb_errstring(ldb)));
437 } else if (ret != LDB_SUCCESS) {
438 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
439 bl->active?"add":"remove",
440 ldb_dn_get_linearized(source_dn),
441 ldb_dn_get_linearized(target_dn),
451 add a backlink to the list of backlinks to add/delete in the prepare
454 forward_dn is stolen onto the defereed context
456 static int replmd_defer_add_backlink(struct ldb_module *module,
457 struct replmd_private *replmd_private,
458 const struct dsdb_schema *schema,
459 struct replmd_replicated_request *ac,
460 struct ldb_dn *forward_dn,
461 struct GUID *target_guid, bool active,
462 const struct dsdb_attribute *schema_attr,
463 struct ldb_request *parent)
465 const struct dsdb_attribute *target_attr;
466 struct la_backlink *bl;
468 bl = talloc(ac, struct la_backlink);
470 ldb_module_oom(module);
471 return LDB_ERR_OPERATIONS_ERROR;
474 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
477 * windows 2003 has a broken schema where the
478 * definition of msDS-IsDomainFor is missing (which is
479 * supposed to be the backlink of the
480 * msDS-HasDomainNCs attribute
485 bl->attr_name = target_attr->lDAPDisplayName;
486 bl->forward_dn = talloc_steal(bl, forward_dn);
487 bl->target_guid = *target_guid;
490 DLIST_ADD(ac->la_backlinks, bl);
496 add a backlink to the list of backlinks to add/delete in the prepare
499 static int replmd_add_backlink(struct ldb_module *module,
500 struct replmd_private *replmd_private,
501 const struct dsdb_schema *schema,
502 struct ldb_dn *forward_dn,
503 struct GUID *target_guid, bool active,
504 const struct dsdb_attribute *schema_attr,
505 struct ldb_request *parent)
507 const struct dsdb_attribute *target_attr;
508 struct la_backlink bl;
511 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
514 * windows 2003 has a broken schema where the
515 * definition of msDS-IsDomainFor is missing (which is
516 * supposed to be the backlink of the
517 * msDS-HasDomainNCs attribute
522 bl.attr_name = target_attr->lDAPDisplayName;
523 bl.forward_dn = forward_dn;
524 bl.target_guid = *target_guid;
527 ret = replmd_process_backlink(module, &bl, parent);
533 * Callback for most write operations in this module:
535 * notify the repl task that a object has changed. The notifies are
536 * gathered up in the replmd_private structure then written to the
537 * @REPLCHANGED object in each partition during the prepare_commit
539 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
542 struct replmd_replicated_request *ac =
543 talloc_get_type_abort(req->context, struct replmd_replicated_request);
544 struct replmd_private *replmd_private =
545 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
546 struct nc_entry *modified_partition;
547 struct ldb_control *partition_ctrl;
548 const struct dsdb_control_current_partition *partition;
550 struct ldb_control **controls;
552 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
554 controls = ares->controls;
555 if (ldb_request_get_control(ac->req,
556 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
558 * Remove the current partition control from what we pass up
559 * the chain if it hasn't been requested manually.
561 controls = ldb_controls_except_specified(ares->controls, ares,
565 if (ares->error != LDB_SUCCESS) {
566 struct GUID_txt_buf guid_txt;
567 struct ldb_message *msg = NULL;
570 if (ac->apply_mode == false) {
571 DBG_NOTICE("Originating update failure. Error is: %s\n",
572 ldb_strerror(ares->error));
573 return ldb_module_done(ac->req, controls,
574 ares->response, ares->error);
577 msg = ac->objs->objects[ac->index_current].msg;
579 * Set at DBG_NOTICE as once these start to happe, they
580 * will happen a lot until resolved, due to repeated
581 * replication. The caller will probably print the
582 * ldb error string anyway.
584 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
585 ldb_dn_get_linearized(msg->dn),
586 ldb_strerror(ares->error));
588 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
593 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
594 ac->search_msg == NULL ? "ADD" : "MODIFY",
595 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
599 return ldb_module_done(ac->req, controls,
600 ares->response, ares->error);
603 if (ares->type != LDB_REPLY_DONE) {
604 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
605 return ldb_module_done(ac->req, NULL,
606 NULL, LDB_ERR_OPERATIONS_ERROR);
609 if (ac->apply_mode == false) {
610 struct la_backlink *bl;
612 * process our backlink list after an replmd_add(),
613 * creating and deleting backlinks as necessary (this
614 * code is sync). The other cases are handled inline
617 for (bl=ac->la_backlinks; bl; bl=bl->next) {
618 ret = replmd_process_backlink(ac->module, bl, ac->req);
619 if (ret != LDB_SUCCESS) {
620 return ldb_module_done(ac->req, NULL,
626 if (!partition_ctrl) {
627 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
628 return ldb_module_done(ac->req, NULL,
629 NULL, LDB_ERR_OPERATIONS_ERROR);
632 partition = talloc_get_type_abort(partition_ctrl->data,
633 struct dsdb_control_current_partition);
635 if (ac->seq_num > 0) {
636 for (modified_partition = replmd_private->ncs; modified_partition;
637 modified_partition = modified_partition->next) {
638 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
643 if (modified_partition == NULL) {
644 modified_partition = talloc_zero(replmd_private, struct nc_entry);
645 if (!modified_partition) {
646 ldb_oom(ldb_module_get_ctx(ac->module));
647 return ldb_module_done(ac->req, NULL,
648 NULL, LDB_ERR_OPERATIONS_ERROR);
650 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
651 if (!modified_partition->dn) {
652 ldb_oom(ldb_module_get_ctx(ac->module));
653 return ldb_module_done(ac->req, NULL,
654 NULL, LDB_ERR_OPERATIONS_ERROR);
656 DLIST_ADD(replmd_private->ncs, modified_partition);
659 if (ac->seq_num > modified_partition->mod_usn) {
660 modified_partition->mod_usn = ac->seq_num;
662 modified_partition->mod_usn_urgent = ac->seq_num;
665 if (!ac->apply_mode) {
666 replmd_private->originating_updates = true;
670 if (ac->apply_mode) {
671 ret = replmd_replicated_apply_isDeleted(ac);
672 if (ret != LDB_SUCCESS) {
673 return ldb_module_done(ac->req, NULL, NULL, ret);
677 /* free the partition control container here, for the
678 * common path. Other cases will have it cleaned up
679 * eventually with the ares */
680 talloc_free(partition_ctrl);
681 return ldb_module_done(ac->req, controls,
682 ares->response, LDB_SUCCESS);
688 * update a @REPLCHANGED record in each partition if there have been
689 * any writes of replicated data in the partition
691 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
693 struct replmd_private *replmd_private =
694 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
696 while (replmd_private->ncs) {
698 struct nc_entry *modified_partition = replmd_private->ncs;
700 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
701 modified_partition->mod_usn,
702 modified_partition->mod_usn_urgent, parent);
703 if (ret != LDB_SUCCESS) {
704 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
705 ldb_dn_get_linearized(modified_partition->dn)));
709 if (ldb_dn_compare(modified_partition->dn,
710 replmd_private->schema_dn) == 0) {
711 struct ldb_result *ext_res;
712 ret = dsdb_module_extended(module,
713 replmd_private->schema_dn,
715 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
717 DSDB_FLAG_NEXT_MODULE,
719 if (ret != LDB_SUCCESS) {
722 talloc_free(ext_res);
725 DLIST_REMOVE(replmd_private->ncs, modified_partition);
726 talloc_free(modified_partition);
734 created a replmd_replicated_request context
736 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
737 struct ldb_request *req)
739 struct ldb_context *ldb;
740 struct replmd_replicated_request *ac;
741 const struct GUID *our_invocation_id;
743 ldb = ldb_module_get_ctx(module);
745 ac = talloc_zero(req, struct replmd_replicated_request);
754 ac->schema = dsdb_get_schema(ldb, ac);
756 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
757 "replmd_modify: no dsdb_schema loaded");
758 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
763 /* get our invocationId */
764 our_invocation_id = samdb_ntds_invocation_id(ldb);
765 if (!our_invocation_id) {
766 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
767 "replmd_add: unable to find invocationId\n");
771 ac->our_invocation_id = *our_invocation_id;
777 add a time element to a record
779 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
781 struct ldb_message_element *el;
785 if (ldb_msg_find_element(msg, attr) != NULL) {
789 s = ldb_timestring(msg, t);
791 return LDB_ERR_OPERATIONS_ERROR;
794 ret = ldb_msg_add_string(msg, attr, s);
795 if (ret != LDB_SUCCESS) {
799 el = ldb_msg_find_element(msg, attr);
800 /* always set as replace. This works because on add ops, the flag
802 el->flags = LDB_FLAG_MOD_REPLACE;
808 add a uint64_t element to a record
810 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
811 const char *attr, uint64_t v)
813 struct ldb_message_element *el;
816 if (ldb_msg_find_element(msg, attr) != NULL) {
820 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
821 if (ret != LDB_SUCCESS) {
825 el = ldb_msg_find_element(msg, attr);
826 /* always set as replace. This works because on add ops, the flag
828 el->flags = LDB_FLAG_MOD_REPLACE;
833 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
834 const struct replPropertyMetaData1 *m2,
835 const uint32_t *rdn_attid)
838 * This assignment seems inoccous, but it is critical for the
839 * system, as we need to do the comparisons as a unsigned
840 * quantity, not signed (enums are signed integers)
842 uint32_t attid_1 = m1->attid;
843 uint32_t attid_2 = m2->attid;
845 if (attid_1 == attid_2) {
850 * See above regarding this being an unsigned comparison.
851 * Otherwise when the high bit is set on non-standard
852 * attributes, they would end up first, before objectClass
855 return attid_1 > attid_2 ? 1 : -1;
858 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
859 struct replPropertyMetaDataCtr1 *ctr1,
862 if (ctr1->count == 0) {
863 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
864 "No elements found in replPropertyMetaData for %s!\n",
865 ldb_dn_get_linearized(dn));
866 return LDB_ERR_CONSTRAINT_VIOLATION;
869 /* the objectClass attribute is value 0x00000000, so must be first */
870 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
871 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
872 "No objectClass found in replPropertyMetaData for %s!\n",
873 ldb_dn_get_linearized(dn));
874 return LDB_ERR_OBJECT_CLASS_VIOLATION;
880 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
881 struct replPropertyMetaDataCtr1 *ctr1,
884 /* Note this is O(n^2) for the almost-sorted case, which this is */
885 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
886 replmd_replPropertyMetaData1_attid_sort);
887 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
890 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
891 const struct ldb_message_element *e2,
892 const struct dsdb_schema *schema)
894 const struct dsdb_attribute *a1;
895 const struct dsdb_attribute *a2;
898 * TODO: make this faster by caching the dsdb_attribute pointer
899 * on the ldb_messag_element
902 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
903 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
906 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
910 return strcasecmp(e1->name, e2->name);
912 if (a1->attributeID_id == a2->attributeID_id) {
915 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
918 static void replmd_ldb_message_sort(struct ldb_message *msg,
919 const struct dsdb_schema *schema)
921 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
924 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
925 const struct GUID *invocation_id, uint64_t seq_num,
926 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
928 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
930 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
931 struct ldb_message_element *el, struct parsed_dn **pdn,
932 const char *ldap_oid, struct ldb_request *parent);
935 fix up linked attributes in replmd_add.
936 This involves setting up the right meta-data in extended DN
937 components, and creating backlinks to the object
939 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
940 struct replmd_private *replmd_private,
941 struct ldb_message_element *el,
942 struct replmd_replicated_request *ac,
944 struct ldb_dn *forward_dn,
945 const struct dsdb_attribute *sa,
946 struct ldb_request *parent)
949 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
950 struct ldb_context *ldb = ldb_module_get_ctx(module);
951 struct parsed_dn *pdn;
952 /* We will take a reference to the schema in replmd_add_backlink */
953 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
954 struct ldb_val *new_values = NULL;
957 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
958 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
960 ldb_asprintf_errstring(ldb,
961 "Attribute %s is single valued but "
962 "more than one value has been supplied",
964 talloc_free(tmp_ctx);
965 return LDB_ERR_CONSTRAINT_VIOLATION;
968 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
969 sa->syntax->ldap_oid, parent);
970 if (ret != LDB_SUCCESS) {
971 talloc_free(tmp_ctx);
975 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
976 if (new_values == NULL) {
977 ldb_module_oom(module);
978 talloc_free(tmp_ctx);
979 return LDB_ERR_OPERATIONS_ERROR;
982 for (i = 0; i < el->num_values; i++) {
983 struct parsed_dn *p = &pdn[i];
984 if (i > 0 && parsed_dn_compare(p, &pdn[i - 1]) == 0) {
985 ldb_asprintf_errstring(ldb,
986 "Linked attribute %s has "
987 "multiple identical values", el->name);
988 talloc_free(tmp_ctx);
989 if (ldb_attr_cmp(el->name, "member") == 0) {
990 return LDB_ERR_ENTRY_ALREADY_EXISTS;
992 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
995 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
996 &ac->our_invocation_id,
997 ac->seq_num, ac->seq_num, now, 0, false);
998 if (ret != LDB_SUCCESS) {
999 talloc_free(tmp_ctx);
1003 ret = replmd_defer_add_backlink(module, replmd_private,
1005 forward_dn, &p->guid, true, sa,
1007 if (ret != LDB_SUCCESS) {
1008 talloc_free(tmp_ctx);
1012 new_values[i] = *p->v;
1014 el->values = talloc_steal(mem_ctx, new_values);
1016 talloc_free(tmp_ctx);
1020 static int replmd_add_make_extended_dn(struct ldb_request *req,
1021 const DATA_BLOB *guid_blob,
1022 struct ldb_dn **_extended_dn)
1025 const DATA_BLOB *sid_blob;
1026 /* Calculate an extended DN for any linked attributes */
1027 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1029 return LDB_ERR_OPERATIONS_ERROR;
1031 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1032 if (ret != LDB_SUCCESS) {
1036 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1037 if (sid_blob != NULL) {
1038 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1039 if (ret != LDB_SUCCESS) {
1043 *_extended_dn = extended_dn;
1048 intercept add requests
1050 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1052 struct ldb_context *ldb;
1053 struct ldb_control *control;
1054 struct replmd_replicated_request *ac;
1055 enum ndr_err_code ndr_err;
1056 struct ldb_request *down_req;
1057 struct ldb_message *msg;
1058 const DATA_BLOB *guid_blob;
1059 DATA_BLOB guid_blob_stack;
1061 uint8_t guid_data[16];
1062 struct replPropertyMetaDataBlob nmd;
1063 struct ldb_val nmd_value;
1064 struct ldb_dn *extended_dn = NULL;
1067 * The use of a time_t here seems odd, but as the NTTIME
1068 * elements are actually declared as NTTIME_1sec in the IDL,
1069 * getting a higher resolution timestamp is not required.
1071 time_t t = time(NULL);
1076 unsigned int functional_level;
1078 bool allow_add_guid = false;
1079 bool remove_current_guid = false;
1080 bool is_urgent = false;
1081 bool is_schema_nc = false;
1082 struct ldb_message_element *objectclass_el;
1083 struct replmd_private *replmd_private =
1084 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1086 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1087 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1089 allow_add_guid = true;
1092 /* do not manipulate our control entries */
1093 if (ldb_dn_is_special(req->op.add.message->dn)) {
1094 return ldb_next_request(module, req);
1097 ldb = ldb_module_get_ctx(module);
1099 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1101 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1102 if (guid_blob != NULL) {
1103 if (!allow_add_guid) {
1104 ldb_set_errstring(ldb,
1105 "replmd_add: it's not allowed to add an object with objectGUID!");
1106 return LDB_ERR_UNWILLING_TO_PERFORM;
1108 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1109 if (!NT_STATUS_IS_OK(status)) {
1110 ldb_set_errstring(ldb,
1111 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1112 return LDB_ERR_UNWILLING_TO_PERFORM;
1114 /* we remove this attribute as it can be a string and
1115 * will not be treated correctly and then we will re-add
1116 * it later on in the good format */
1117 remove_current_guid = true;
1121 guid = GUID_random();
1123 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1125 /* This can't fail */
1126 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1127 (ndr_push_flags_fn_t)ndr_push_GUID);
1128 guid_blob = &guid_blob_stack;
1131 ac = replmd_ctx_init(module, req);
1133 return ldb_module_oom(module);
1136 functional_level = dsdb_functional_level(ldb);
1138 /* Get a sequence number from the backend */
1139 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1140 if (ret != LDB_SUCCESS) {
1145 /* we have to copy the message as the caller might have it as a const */
1146 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1150 return LDB_ERR_OPERATIONS_ERROR;
1153 /* generated times */
1154 unix_to_nt_time(&now, t);
1155 time_str = ldb_timestring(msg, t);
1159 return LDB_ERR_OPERATIONS_ERROR;
1161 if (remove_current_guid) {
1162 ldb_msg_remove_attr(msg,"objectGUID");
1166 * remove autogenerated attributes
1168 ldb_msg_remove_attr(msg, "whenCreated");
1169 ldb_msg_remove_attr(msg, "whenChanged");
1170 ldb_msg_remove_attr(msg, "uSNCreated");
1171 ldb_msg_remove_attr(msg, "uSNChanged");
1172 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1175 * readd replicated attributes
1177 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1178 if (ret != LDB_SUCCESS) {
1184 /* build the replication meta_data */
1187 nmd.ctr.ctr1.count = msg->num_elements;
1188 nmd.ctr.ctr1.array = talloc_array(msg,
1189 struct replPropertyMetaData1,
1190 nmd.ctr.ctr1.count);
1191 if (!nmd.ctr.ctr1.array) {
1194 return LDB_ERR_OPERATIONS_ERROR;
1197 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1199 for (i=0; i < msg->num_elements;) {
1200 struct ldb_message_element *e = &msg->elements[i];
1201 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1202 const struct dsdb_attribute *sa;
1204 if (e->name[0] == '@') {
1209 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1211 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1212 "replmd_add: attribute '%s' not defined in schema\n",
1215 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1218 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1219 /* if the attribute is not replicated (0x00000001)
1220 * or constructed (0x00000004) it has no metadata
1226 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1227 if (extended_dn == NULL) {
1228 ret = replmd_add_make_extended_dn(req,
1231 if (ret != LDB_SUCCESS) {
1238 * Prepare the context for the backlinks and
1239 * create metadata for the forward links. The
1240 * backlinks are created in
1241 * replmd_op_callback() after the successful
1242 * ADD of the object.
1244 ret = replmd_add_fix_la(module, msg->elements,
1249 if (ret != LDB_SUCCESS) {
1253 /* linked attributes are not stored in
1254 replPropertyMetaData in FL above w2k */
1259 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1261 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1262 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1265 if (rdn_val == NULL) {
1268 return LDB_ERR_OPERATIONS_ERROR;
1271 rdn = (const char*)rdn_val->data;
1272 if (strcmp(rdn, "Deleted Objects") == 0) {
1274 * Set the originating_change_time to 29/12/9999 at 23:59:59
1275 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1277 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1279 m->originating_change_time = now;
1282 m->originating_change_time = now;
1284 m->originating_invocation_id = ac->our_invocation_id;
1285 m->originating_usn = ac->seq_num;
1286 m->local_usn = ac->seq_num;
1289 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1294 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1296 if (e->num_values != 0) {
1301 ldb_msg_remove_element(msg, e);
1304 /* fix meta data count */
1305 nmd.ctr.ctr1.count = ni;
1308 * sort meta data array
1310 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1311 if (ret != LDB_SUCCESS) {
1312 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1317 /* generated NDR encoded values */
1318 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1320 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1321 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1324 return LDB_ERR_OPERATIONS_ERROR;
1328 * add the autogenerated values
1330 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1331 if (ret != LDB_SUCCESS) {
1336 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1337 if (ret != LDB_SUCCESS) {
1342 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1343 if (ret != LDB_SUCCESS) {
1348 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1349 if (ret != LDB_SUCCESS) {
1354 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1355 if (ret != LDB_SUCCESS) {
1362 * sort the attributes by attid before storing the object
1364 replmd_ldb_message_sort(msg, ac->schema);
1367 * Assert that we do have an objectClass
1369 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1370 if (objectclass_el == NULL) {
1371 ldb_asprintf_errstring(ldb, __location__
1372 ": objectClass missing on %s\n",
1373 ldb_dn_get_linearized(msg->dn));
1375 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1377 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1378 REPL_URGENT_ON_CREATE);
1380 ac->is_urgent = is_urgent;
1381 ret = ldb_build_add_req(&down_req, ldb, ac,
1384 ac, replmd_op_callback,
1387 LDB_REQ_SET_LOCATION(down_req);
1388 if (ret != LDB_SUCCESS) {
1393 /* current partition control is needed by "replmd_op_callback" */
1394 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1395 ret = ldb_request_add_control(down_req,
1396 DSDB_CONTROL_CURRENT_PARTITION_OID,
1398 if (ret != LDB_SUCCESS) {
1404 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1405 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1406 if (ret != LDB_SUCCESS) {
1412 /* mark the control done */
1414 control->critical = 0;
1416 /* go on with the call chain */
1417 return ldb_next_request(module, down_req);
1422 * update the replPropertyMetaData for one element
1424 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1425 struct ldb_message *msg,
1426 struct ldb_message_element *el,
1427 struct ldb_message_element *old_el,
1428 struct replPropertyMetaDataBlob *omd,
1429 const struct dsdb_schema *schema,
1431 const struct GUID *our_invocation_id,
1434 bool is_forced_rodc,
1435 struct ldb_request *req)
1438 const struct dsdb_attribute *a;
1439 struct replPropertyMetaData1 *md1;
1440 bool may_skip = false;
1443 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1445 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1446 /* allow this to make it possible for dbcheck
1447 to remove bad attributes */
1451 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1453 return LDB_ERR_OPERATIONS_ERROR;
1456 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1458 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1463 * if the attribute's value haven't changed, and this isn't
1464 * just a delete of everything then return LDB_SUCCESS Unless
1465 * we have the provision control or if the attribute is
1466 * interSiteTopologyGenerator as this page explain:
1467 * http://support.microsoft.com/kb/224815 this attribute is
1468 * periodicaly written by the DC responsible for the intersite
1469 * generation in a given site
1471 * Unchanged could be deleting or replacing an already-gone
1472 * thing with an unconstrained delete/empty replace or a
1473 * replace with the same value, but not an add with the same
1474 * value because that could be about adding a duplicate (which
1475 * is for someone else to error out on).
1477 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1478 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1481 } else if (old_el == NULL && el->num_values == 0) {
1482 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1484 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1487 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1488 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1490 * We intentionally skip the version bump when attempting to
1493 * The control is set by dbcheck and expunge-tombstones which
1494 * both attempt to be non-replicating. Otherwise, making an
1495 * alteration to the replication state would trigger a
1496 * broadcast of all expunged objects.
1501 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1503 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1507 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1508 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1510 * allow this to make it possible for dbcheck
1511 * to rebuild broken metadata
1517 for (i=0; i<omd->ctr.ctr1.count; i++) {
1519 * First check if we find it under the msDS-IntID,
1520 * then check if we find it under the OID and
1523 * This allows the administrator to simply re-write
1524 * the attributes and so restore replication, which is
1525 * likely what they will try to do.
1527 if (attid == omd->ctr.ctr1.array[i].attid) {
1531 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1536 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1537 /* linked attributes are not stored in
1538 replPropertyMetaData in FL above w2k, but we do
1539 raise the seqnum for the object */
1540 if (*seq_num == 0 &&
1541 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1542 return LDB_ERR_OPERATIONS_ERROR;
1547 if (i == omd->ctr.ctr1.count) {
1548 /* we need to add a new one */
1549 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1550 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1551 if (omd->ctr.ctr1.array == NULL) {
1553 return LDB_ERR_OPERATIONS_ERROR;
1555 omd->ctr.ctr1.count++;
1556 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1559 /* Get a new sequence number from the backend. We only do this
1560 * if we have a change that requires a new
1561 * replPropertyMetaData element
1563 if (*seq_num == 0) {
1564 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1565 if (ret != LDB_SUCCESS) {
1566 return LDB_ERR_OPERATIONS_ERROR;
1570 md1 = &omd->ctr.ctr1.array[i];
1574 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1575 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1578 if (rdn_val == NULL) {
1580 return LDB_ERR_OPERATIONS_ERROR;
1583 rdn = (const char*)rdn_val->data;
1584 if (strcmp(rdn, "Deleted Objects") == 0) {
1586 * Set the originating_change_time to 29/12/9999 at 23:59:59
1587 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1589 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1591 md1->originating_change_time = now;
1594 md1->originating_change_time = now;
1596 md1->originating_invocation_id = *our_invocation_id;
1597 md1->originating_usn = *seq_num;
1598 md1->local_usn = *seq_num;
1600 if (is_forced_rodc) {
1601 /* Force version to 0 to be overriden later via replication */
1609 * Bump the replPropertyMetaData version on an attribute, and if it
1610 * has changed (or forced by leaving rdn_old NULL), update the value
1613 * This is important, as calling a modify operation may not change the
1614 * version number if the values appear unchanged, but a rename between
1615 * parents bumps this value.
1618 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1619 struct ldb_message *msg,
1620 const struct ldb_val *rdn_new,
1621 const struct ldb_val *rdn_old,
1622 struct replPropertyMetaDataBlob *omd,
1623 struct replmd_replicated_request *ar,
1626 bool is_forced_rodc)
1628 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1629 const struct dsdb_attribute *rdn_attr =
1630 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1631 const char *attr_name = rdn_attr != NULL ?
1632 rdn_attr->lDAPDisplayName :
1634 struct ldb_message_element new_el = {
1635 .flags = LDB_FLAG_MOD_REPLACE,
1638 .values = discard_const_p(struct ldb_val, rdn_new)
1640 struct ldb_message_element old_el = {
1641 .flags = LDB_FLAG_MOD_REPLACE,
1643 .num_values = rdn_old ? 1 : 0,
1644 .values = discard_const_p(struct ldb_val, rdn_old)
1647 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1648 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1649 if (ret != LDB_SUCCESS) {
1650 return ldb_oom(ldb);
1654 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1655 omd, ar->schema, &ar->seq_num,
1656 &ar->our_invocation_id,
1657 now, is_schema_nc, is_forced_rodc,
1662 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1664 uint32_t count = omd.ctr.ctr1.count;
1667 for (i=0; i < count; i++) {
1668 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1669 if (max < m.local_usn) {
1677 * update the replPropertyMetaData object each time we modify an
1678 * object. This is needed for DRS replication, as the merge on the
1679 * client is based on this object
1681 static int replmd_update_rpmd(struct ldb_module *module,
1682 const struct dsdb_schema *schema,
1683 struct ldb_request *req,
1684 const char * const *rename_attrs,
1685 struct ldb_message *msg, uint64_t *seq_num,
1686 time_t t, bool is_schema_nc,
1687 bool *is_urgent, bool *rodc)
1689 const struct ldb_val *omd_value;
1690 enum ndr_err_code ndr_err;
1691 struct replPropertyMetaDataBlob omd;
1694 const struct GUID *our_invocation_id;
1696 const char * const *attrs = NULL;
1697 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1698 struct ldb_result *res;
1699 struct ldb_context *ldb;
1700 struct ldb_message_element *objectclass_el;
1701 enum urgent_situation situation;
1702 bool rmd_is_provided;
1703 bool rmd_is_just_resorted = false;
1704 const char *not_rename_attrs[4 + msg->num_elements];
1705 bool is_forced_rodc = false;
1708 attrs = rename_attrs;
1710 for (i = 0; i < msg->num_elements; i++) {
1711 not_rename_attrs[i] = msg->elements[i].name;
1713 not_rename_attrs[i] = "replPropertyMetaData";
1714 not_rename_attrs[i+1] = "objectClass";
1715 not_rename_attrs[i+2] = "instanceType";
1716 not_rename_attrs[i+3] = NULL;
1717 attrs = not_rename_attrs;
1720 ldb = ldb_module_get_ctx(module);
1722 ret = samdb_rodc(ldb, rodc);
1723 if (ret != LDB_SUCCESS) {
1724 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1729 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1730 is_forced_rodc = true;
1733 our_invocation_id = samdb_ntds_invocation_id(ldb);
1734 if (!our_invocation_id) {
1735 /* this happens during an initial vampire while
1736 updating the schema */
1737 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1741 unix_to_nt_time(&now, t);
1743 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1744 rmd_is_provided = true;
1745 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1746 rmd_is_just_resorted = true;
1749 rmd_is_provided = false;
1752 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1753 * otherwise we consider we are updating */
1754 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1755 situation = REPL_URGENT_ON_DELETE;
1756 } else if (rename_attrs) {
1757 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1759 situation = REPL_URGENT_ON_UPDATE;
1762 if (rmd_is_provided) {
1763 /* In this case the change_replmetadata control was supplied */
1764 /* We check that it's the only attribute that is provided
1765 * (it's a rare case so it's better to keep the code simplier)
1766 * We also check that the highest local_usn is bigger or the same as
1769 if( msg->num_elements != 1 ||
1770 strncmp(msg->elements[0].name,
1771 "replPropertyMetaData", 20) ) {
1772 DEBUG(0,(__location__ ": changereplmetada control called without "\
1773 "a specified replPropertyMetaData attribute or with others\n"));
1774 return LDB_ERR_OPERATIONS_ERROR;
1776 if (situation != REPL_URGENT_ON_UPDATE) {
1777 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1778 return LDB_ERR_OPERATIONS_ERROR;
1780 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1782 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1783 ldb_dn_get_linearized(msg->dn)));
1784 return LDB_ERR_OPERATIONS_ERROR;
1786 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1787 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1788 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1789 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1790 ldb_dn_get_linearized(msg->dn)));
1791 return LDB_ERR_OPERATIONS_ERROR;
1794 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1795 DSDB_FLAG_NEXT_MODULE |
1796 DSDB_SEARCH_SHOW_RECYCLED |
1797 DSDB_SEARCH_SHOW_EXTENDED_DN |
1798 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1799 DSDB_SEARCH_REVEAL_INTERNALS, req);
1801 if (ret != LDB_SUCCESS) {
1805 if (rmd_is_just_resorted == false) {
1806 *seq_num = find_max_local_usn(omd);
1808 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1811 * The test here now allows for a new
1812 * replPropertyMetaData with no change, if was
1813 * just dbcheck re-sorting the values.
1815 if (*seq_num <= db_seq) {
1816 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1817 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1818 (long long)*seq_num, (long long)db_seq));
1819 return LDB_ERR_OPERATIONS_ERROR;
1824 /* search for the existing replPropertyMetaDataBlob. We need
1825 * to use REVEAL and ask for DNs in storage format to support
1826 * the check for values being the same in
1827 * replmd_update_rpmd_element()
1829 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1830 DSDB_FLAG_NEXT_MODULE |
1831 DSDB_SEARCH_SHOW_RECYCLED |
1832 DSDB_SEARCH_SHOW_EXTENDED_DN |
1833 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1834 DSDB_SEARCH_REVEAL_INTERNALS, req);
1835 if (ret != LDB_SUCCESS) {
1839 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1841 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1842 ldb_dn_get_linearized(msg->dn)));
1843 return LDB_ERR_OPERATIONS_ERROR;
1846 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1847 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1848 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1849 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1850 ldb_dn_get_linearized(msg->dn)));
1851 return LDB_ERR_OPERATIONS_ERROR;
1854 if (omd.version != 1) {
1855 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1856 omd.version, ldb_dn_get_linearized(msg->dn)));
1857 return LDB_ERR_OPERATIONS_ERROR;
1860 for (i=0; i<msg->num_elements;) {
1861 struct ldb_message_element *el = &msg->elements[i];
1862 struct ldb_message_element *old_el;
1864 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1865 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1866 &omd, schema, seq_num,
1871 if (ret != LDB_SUCCESS) {
1875 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1876 *is_urgent = replmd_check_urgent_attribute(el);
1879 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1884 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1886 if (el->num_values != 0) {
1891 ldb_msg_remove_element(msg, el);
1896 * Assert that we have an objectClass attribute - this is major
1897 * corruption if we don't have this!
1899 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1900 if (objectclass_el != NULL) {
1902 * Now check if this objectClass means we need to do urgent replication
1904 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1908 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1909 ldb_asprintf_errstring(ldb, __location__
1910 ": objectClass missing on %s\n",
1911 ldb_dn_get_linearized(msg->dn));
1912 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1916 * replmd_update_rpmd_element has done an update if the
1919 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1920 struct ldb_val *md_value;
1921 struct ldb_message_element *el;
1923 /*if we are RODC and this is a DRSR update then its ok*/
1924 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1925 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1926 && !is_forced_rodc) {
1927 unsigned instanceType;
1930 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1931 return LDB_ERR_REFERRAL;
1934 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1935 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1936 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1937 "cannot change replicated attribute on partial replica");
1941 md_value = talloc(msg, struct ldb_val);
1942 if (md_value == NULL) {
1944 return LDB_ERR_OPERATIONS_ERROR;
1947 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1948 if (ret != LDB_SUCCESS) {
1949 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1953 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1954 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1955 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1956 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1957 ldb_dn_get_linearized(msg->dn)));
1958 return LDB_ERR_OPERATIONS_ERROR;
1961 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1962 if (ret != LDB_SUCCESS) {
1963 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1964 ldb_dn_get_linearized(msg->dn)));
1969 el->values = md_value;
1975 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1977 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1979 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1980 &pdn2->dsdb_dn->extra_part);
1986 get a series of message element values as an array of DNs and GUIDs
1987 the result is sorted by GUID
1989 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1990 struct ldb_message_element *el, struct parsed_dn **pdn,
1991 const char *ldap_oid, struct ldb_request *parent)
1994 bool values_are_sorted = true;
1995 struct ldb_context *ldb = ldb_module_get_ctx(module);
2002 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2004 ldb_module_oom(module);
2005 return LDB_ERR_OPERATIONS_ERROR;
2008 for (i=0; i<el->num_values; i++) {
2009 struct ldb_val *v = &el->values[i];
2012 struct parsed_dn *p;
2016 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2017 if (p->dsdb_dn == NULL) {
2018 return LDB_ERR_INVALID_DN_SYNTAX;
2021 dn = p->dsdb_dn->dn;
2023 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2024 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2025 unlikely(GUID_all_zero(&p->guid))) {
2026 /* we got a DN without a GUID - go find the GUID */
2027 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2028 if (ret != LDB_SUCCESS) {
2029 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2030 ldb_dn_get_linearized(dn));
2031 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2032 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2033 ldb_attr_cmp(el->name, "member") == 0) {
2034 return LDB_ERR_UNWILLING_TO_PERFORM;
2038 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2039 if (ret != LDB_SUCCESS) {
2042 } else if (!NT_STATUS_IS_OK(status)) {
2043 return LDB_ERR_OPERATIONS_ERROR;
2045 if (i > 0 && values_are_sorted) {
2046 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2048 values_are_sorted = false;
2051 /* keep a pointer to the original ldb_val */
2054 if (! values_are_sorted) {
2055 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2061 * Get a series of trusted message element values. The result is sorted by
2062 * GUID, even though the GUIDs might not be known. That works because we trust
2063 * the database to give us the elements like that if the
2064 * replmd_private->sorted_links flag is set.
2066 * We also ensure that the links are in the Functional Level 2003
2067 * linked attributes format.
2069 static int get_parsed_dns_trusted(struct ldb_module *module,
2070 struct replmd_private *replmd_private,
2071 TALLOC_CTX *mem_ctx,
2072 struct ldb_message_element *el,
2073 struct parsed_dn **pdn,
2074 const char *ldap_oid,
2075 struct ldb_request *parent)
2084 if (!replmd_private->sorted_links) {
2085 /* We need to sort the list. This is the slow old path we want
2088 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2090 if (ret != LDB_SUCCESS) {
2094 /* Here we get a list of 'struct parsed_dns' without the parsing */
2095 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2098 ldb_module_oom(module);
2099 return LDB_ERR_OPERATIONS_ERROR;
2102 for (i = 0; i < el->num_values; i++) {
2103 (*pdn)[i].v = &el->values[i];
2108 * This upgrades links to FL2003 style, and sorts the result
2109 * if that was needed.
2111 * TODO: Add a database feature that asserts we have no FL2000
2112 * style links to avoid this check or add a feature that
2113 * uses a similar check to find sorted/unsorted links
2114 * for an on-the-fly upgrade.
2117 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2118 *pdn, el->num_values,
2121 if (ret != LDB_SUCCESS) {
2129 build a new extended DN, including all meta data fields
2131 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2132 RMD_ADDTIME = originating_add_time
2133 RMD_INVOCID = originating_invocation_id
2134 RMD_CHANGETIME = originating_change_time
2135 RMD_ORIGINATING_USN = originating_usn
2136 RMD_LOCAL_USN = local_usn
2137 RMD_VERSION = version
2139 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2140 const struct GUID *invocation_id, uint64_t seq_num,
2141 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
2143 struct ldb_dn *dn = dsdb_dn->dn;
2144 const char *tstring, *usn_string, *flags_string;
2145 struct ldb_val tval;
2147 struct ldb_val usnv, local_usnv;
2148 struct ldb_val vers, flagsv;
2151 const char *dnstring;
2153 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2155 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2157 return LDB_ERR_OPERATIONS_ERROR;
2159 tval = data_blob_string_const(tstring);
2161 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
2163 return LDB_ERR_OPERATIONS_ERROR;
2165 usnv = data_blob_string_const(usn_string);
2167 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2169 return LDB_ERR_OPERATIONS_ERROR;
2171 local_usnv = data_blob_string_const(usn_string);
2173 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2175 return LDB_ERR_OPERATIONS_ERROR;
2177 vers = data_blob_string_const(vstring);
2179 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2180 if (!NT_STATUS_IS_OK(status)) {
2181 return LDB_ERR_OPERATIONS_ERROR;
2184 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2185 if (!flags_string) {
2186 return LDB_ERR_OPERATIONS_ERROR;
2188 flagsv = data_blob_string_const(flags_string);
2190 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2191 if (ret != LDB_SUCCESS) return ret;
2192 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
2193 if (ret != LDB_SUCCESS) return ret;
2194 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2195 if (ret != LDB_SUCCESS) return ret;
2196 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2197 if (ret != LDB_SUCCESS) return ret;
2198 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2199 if (ret != LDB_SUCCESS) return ret;
2200 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2201 if (ret != LDB_SUCCESS) return ret;
2202 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2203 if (ret != LDB_SUCCESS) return ret;
2205 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2206 if (dnstring == NULL) {
2207 return LDB_ERR_OPERATIONS_ERROR;
2209 *v = data_blob_string_const(dnstring);
2214 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2215 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2216 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2220 check if any links need upgrading from w2k format
2222 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2223 struct parsed_dn *dns, uint32_t count,
2224 struct ldb_message_element *el,
2225 const char *ldap_oid)
2228 const struct GUID *invocation_id = NULL;
2229 for (i=0; i<count; i++) {
2233 if (dns[i].dsdb_dn == NULL) {
2234 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2236 if (ret != LDB_SUCCESS) {
2237 return LDB_ERR_INVALID_DN_SYNTAX;
2241 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2242 &version, "RMD_VERSION");
2243 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2245 * We optimistically assume they are all the same; if
2246 * the first one is fixed, they are all fixed.
2248 * If the first one was *not* fixed and we find a
2249 * later one that is, that is an occasion to shout
2255 DEBUG(0, ("Mixed w2k and fixed format "
2256 "linked attributes\n"));
2260 if (invocation_id == NULL) {
2261 invocation_id = samdb_ntds_invocation_id(ldb);
2262 if (invocation_id == NULL) {
2263 return LDB_ERR_OPERATIONS_ERROR;
2268 /* it's an old one that needs upgrading */
2269 ret = replmd_update_la_val(el->values, dns[i].v,
2270 dns[i].dsdb_dn, dns[i].dsdb_dn,
2271 invocation_id, 1, 1, 0, false);
2272 if (ret != LDB_SUCCESS) {
2278 * This sort() is critical for the operation of
2279 * get_parsed_dns_trusted() because callers of this function
2280 * expect a sorted list, and FL2000 style links are not
2281 * sorted. In particular, as well as the upgrade case,
2282 * get_parsed_dns_trusted() is called from
2283 * replmd_delete_remove_link() even in FL2000 mode
2285 * We do not normally pay the cost of the qsort() due to the
2286 * early return in the RMD_VERSION found case.
2288 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2293 Sets the value for a linked attribute, including all meta data fields
2295 see replmd_build_la_val for value names
2297 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2298 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2299 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2300 uint32_t version, bool deleted)
2302 struct ldb_dn *dn = dsdb_dn->dn;
2303 const char *tstring, *usn_string, *flags_string;
2304 struct ldb_val tval;
2306 struct ldb_val usnv, local_usnv;
2307 struct ldb_val vers, flagsv;
2308 const struct ldb_val *old_addtime;
2311 const char *dnstring;
2313 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2315 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2317 return LDB_ERR_OPERATIONS_ERROR;
2319 tval = data_blob_string_const(tstring);
2321 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2323 return LDB_ERR_OPERATIONS_ERROR;
2325 usnv = data_blob_string_const(usn_string);
2327 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2329 return LDB_ERR_OPERATIONS_ERROR;
2331 local_usnv = data_blob_string_const(usn_string);
2333 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2334 if (!NT_STATUS_IS_OK(status)) {
2335 return LDB_ERR_OPERATIONS_ERROR;
2338 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2339 if (!flags_string) {
2340 return LDB_ERR_OPERATIONS_ERROR;
2342 flagsv = data_blob_string_const(flags_string);
2344 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2345 if (ret != LDB_SUCCESS) return ret;
2347 /* get the ADDTIME from the original */
2348 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2349 if (old_addtime == NULL) {
2350 old_addtime = &tval;
2352 if (dsdb_dn != old_dsdb_dn ||
2353 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2354 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2355 if (ret != LDB_SUCCESS) return ret;
2358 /* use our invocation id */
2359 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2360 if (ret != LDB_SUCCESS) return ret;
2362 /* changetime is the current time */
2363 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2364 if (ret != LDB_SUCCESS) return ret;
2366 /* update the USN */
2367 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2368 if (ret != LDB_SUCCESS) return ret;
2370 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2371 if (ret != LDB_SUCCESS) return ret;
2373 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2374 vers = data_blob_string_const(vstring);
2375 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2376 if (ret != LDB_SUCCESS) return ret;
2378 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2379 if (dnstring == NULL) {
2380 return LDB_ERR_OPERATIONS_ERROR;
2382 *v = data_blob_string_const(dnstring);
2388 * Updates the value for a linked attribute, including all meta data fields
2390 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2391 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2392 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2395 uint32_t old_version;
2396 uint32_t version = 0;
2400 * We're updating the linked attribute locally, so increase the version
2401 * by 1 so that other DCs will see the change when it gets replicated out
2403 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2406 if (NT_STATUS_IS_OK(status)) {
2407 version = old_version + 1;
2410 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2411 usn, local_usn, nttime, version, deleted);
2415 handle adding a linked attribute
2417 static int replmd_modify_la_add(struct ldb_module *module,
2418 struct replmd_private *replmd_private,
2419 const struct dsdb_schema *schema,
2420 struct ldb_message *msg,
2421 struct ldb_message_element *el,
2422 struct ldb_message_element *old_el,
2423 const struct dsdb_attribute *schema_attr,
2426 struct ldb_dn *msg_dn,
2427 struct ldb_request *parent)
2430 struct parsed_dn *dns, *old_dns;
2431 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2433 struct ldb_val *new_values = NULL;
2434 unsigned old_num_values = old_el ? old_el->num_values : 0;
2435 unsigned num_values = 0;
2436 unsigned max_num_values;
2437 const struct GUID *invocation_id;
2438 struct ldb_context *ldb = ldb_module_get_ctx(module);
2440 unix_to_nt_time(&now, t);
2442 invocation_id = samdb_ntds_invocation_id(ldb);
2443 if (!invocation_id) {
2444 talloc_free(tmp_ctx);
2445 return LDB_ERR_OPERATIONS_ERROR;
2448 /* get the DNs to be added, fully parsed.
2450 * We need full parsing because they came off the wire and we don't
2451 * trust them, besides which we need their details to know where to put
2454 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2455 schema_attr->syntax->ldap_oid, parent);
2456 if (ret != LDB_SUCCESS) {
2457 talloc_free(tmp_ctx);
2461 /* get the existing DNs, lazily parsed */
2462 ret = get_parsed_dns_trusted(module, replmd_private,
2463 tmp_ctx, old_el, &old_dns,
2464 schema_attr->syntax->ldap_oid, parent);
2466 if (ret != LDB_SUCCESS) {
2467 talloc_free(tmp_ctx);
2471 max_num_values = old_num_values + el->num_values;
2472 if (max_num_values < old_num_values) {
2473 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2474 "old values: %u, new values: %u, sum: %u",
2475 old_num_values, el->num_values, max_num_values));
2476 talloc_free(tmp_ctx);
2477 return LDB_ERR_OPERATIONS_ERROR;
2480 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2482 if (new_values == NULL) {
2483 ldb_module_oom(module);
2484 talloc_free(tmp_ctx);
2485 return LDB_ERR_OPERATIONS_ERROR;
2489 * For each new value, find where it would go in the list. If there is
2490 * a matching GUID there, we update the existing value; otherwise we
2494 for (i = 0; i < el->num_values; i++) {
2495 struct parsed_dn *exact;
2496 struct parsed_dn *next;
2498 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2501 dns[i].dsdb_dn->extra_part, 0,
2503 schema_attr->syntax->ldap_oid,
2505 if (err != LDB_SUCCESS) {
2506 talloc_free(tmp_ctx);
2510 if (exact != NULL) {
2512 * We are trying to add one that exists, which is only
2513 * allowed if it was previously deleted.
2515 * When we do undelete a link we change it in place.
2516 * It will be copied across into the right spot in due
2520 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2522 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2523 struct GUID_txt_buf guid_str;
2524 ldb_asprintf_errstring(ldb,
2525 "Attribute %s already "
2526 "exists for target GUID %s",
2528 GUID_buf_string(&exact->guid,
2530 talloc_free(tmp_ctx);
2531 /* error codes for 'member' need to be
2533 if (ldb_attr_cmp(el->name, "member") == 0) {
2534 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2536 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2540 ret = replmd_update_la_val(new_values, exact->v,
2543 invocation_id, seq_num,
2544 seq_num, now, false);
2545 if (ret != LDB_SUCCESS) {
2546 talloc_free(tmp_ctx);
2550 ret = replmd_add_backlink(module, replmd_private,
2557 if (ret != LDB_SUCCESS) {
2558 talloc_free(tmp_ctx);
2564 * Here we don't have an exact match.
2566 * If next is NULL, this one goes beyond the end of the
2567 * existing list, so we need to add all of those ones first.
2569 * If next is not NULL, we need to add all the ones before
2573 offset = old_num_values;
2575 /* next should have been parsed, but let's make sure */
2576 if (next->dsdb_dn == NULL) {
2577 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2578 schema_attr->syntax->ldap_oid);
2579 if (ret != LDB_SUCCESS) {
2583 offset = MIN(next - old_dns, old_num_values);
2586 /* put all the old ones before next on the list */
2587 for (; j < offset; j++) {
2588 new_values[num_values] = *old_dns[j].v;
2592 ret = replmd_add_backlink(module, replmd_private,
2597 /* Make the new linked attribute ldb_val. */
2598 ret = replmd_build_la_val(new_values, &new_values[num_values],
2599 dns[i].dsdb_dn, invocation_id,
2602 if (ret != LDB_SUCCESS) {
2603 talloc_free(tmp_ctx);
2607 if (ret != LDB_SUCCESS) {
2608 talloc_free(tmp_ctx);
2612 /* copy the rest of the old ones (if any) */
2613 for (; j < old_num_values; j++) {
2614 new_values[num_values] = *old_dns[j].v;
2618 talloc_steal(msg->elements, new_values);
2619 if (old_el != NULL) {
2620 talloc_steal(msg->elements, old_el->values);
2622 el->values = new_values;
2623 el->num_values = num_values;
2625 talloc_free(tmp_ctx);
2627 /* we now tell the backend to replace all existing values
2628 with the one we have constructed */
2629 el->flags = LDB_FLAG_MOD_REPLACE;
2636 handle deleting all active linked attributes
2638 static int replmd_modify_la_delete(struct ldb_module *module,
2639 struct replmd_private *replmd_private,
2640 const struct dsdb_schema *schema,
2641 struct ldb_message *msg,
2642 struct ldb_message_element *el,
2643 struct ldb_message_element *old_el,
2644 const struct dsdb_attribute *schema_attr,
2647 struct ldb_dn *msg_dn,
2648 struct ldb_request *parent)
2651 struct parsed_dn *dns, *old_dns;
2652 TALLOC_CTX *tmp_ctx = NULL;
2654 struct ldb_context *ldb = ldb_module_get_ctx(module);
2655 struct ldb_control *vanish_links_ctrl = NULL;
2656 bool vanish_links = false;
2657 unsigned int num_to_delete = el->num_values;
2659 const struct GUID *invocation_id;
2662 unix_to_nt_time(&now, t);
2664 invocation_id = samdb_ntds_invocation_id(ldb);
2665 if (!invocation_id) {
2666 return LDB_ERR_OPERATIONS_ERROR;
2669 if (old_el == NULL || old_el->num_values == 0) {
2670 /* there is nothing to delete... */
2671 if (num_to_delete == 0) {
2672 /* and we're deleting nothing, so that's OK */
2675 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2678 tmp_ctx = talloc_new(msg);
2679 if (tmp_ctx == NULL) {
2680 return LDB_ERR_OPERATIONS_ERROR;
2683 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2684 schema_attr->syntax->ldap_oid, parent);
2685 if (ret != LDB_SUCCESS) {
2686 talloc_free(tmp_ctx);
2690 ret = get_parsed_dns_trusted(module, replmd_private,
2691 tmp_ctx, old_el, &old_dns,
2692 schema_attr->syntax->ldap_oid, parent);
2694 if (ret != LDB_SUCCESS) {
2695 talloc_free(tmp_ctx);
2700 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2701 if (vanish_links_ctrl) {
2702 vanish_links = true;
2703 vanish_links_ctrl->critical = false;
2707 /* we empty out el->values here to avoid damage if we return early. */
2712 * If vanish links is set, we are actually removing members of
2713 * old_el->values; otherwise we are just marking them deleted.
2715 * There is a special case when no values are given: we remove them
2716 * all. When we have the vanish_links control we just have to remove
2717 * the backlinks and change our element to replace the existing values
2718 * with the empty list.
2721 if (num_to_delete == 0) {
2722 for (i = 0; i < old_el->num_values; i++) {
2723 struct parsed_dn *p = &old_dns[i];
2724 if (p->dsdb_dn == NULL) {
2725 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2726 schema_attr->syntax->ldap_oid);
2727 if (ret != LDB_SUCCESS) {
2731 ret = replmd_add_backlink(module, replmd_private,
2732 schema, msg_dn, &p->guid,
2735 if (ret != LDB_SUCCESS) {
2736 talloc_free(tmp_ctx);
2743 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2744 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2748 ret = replmd_update_la_val(old_el->values, p->v,
2749 p->dsdb_dn, p->dsdb_dn,
2750 invocation_id, seq_num,
2751 seq_num, now, true);
2752 if (ret != LDB_SUCCESS) {
2753 talloc_free(tmp_ctx);
2759 el->flags = LDB_FLAG_MOD_REPLACE;
2760 talloc_free(tmp_ctx);
2766 for (i = 0; i < num_to_delete; i++) {
2767 struct parsed_dn *p = &dns[i];
2768 struct parsed_dn *exact = NULL;
2769 struct parsed_dn *next = NULL;
2770 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2773 p->dsdb_dn->extra_part, 0,
2775 schema_attr->syntax->ldap_oid,
2777 if (ret != LDB_SUCCESS) {
2778 talloc_free(tmp_ctx);
2781 if (exact == NULL) {
2782 struct GUID_txt_buf buf;
2783 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2784 "exist for target GUID %s",
2786 GUID_buf_string(&p->guid, &buf));
2787 if (ldb_attr_cmp(el->name, "member") == 0) {
2788 talloc_free(tmp_ctx);
2789 return LDB_ERR_UNWILLING_TO_PERFORM;
2791 talloc_free(tmp_ctx);
2792 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2797 if (CHECK_DEBUGLVL(5)) {
2798 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2799 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2800 struct GUID_txt_buf buf;
2801 const char *guid_str = \
2802 GUID_buf_string(&p->guid, &buf);
2803 DEBUG(5, ("Deleting deleted linked "
2804 "attribute %s to %s, because "
2805 "vanish_links control is set\n",
2806 el->name, guid_str));
2810 /* remove the backlink */
2811 ret = replmd_add_backlink(module,
2818 if (ret != LDB_SUCCESS) {
2819 talloc_free(tmp_ctx);
2823 /* We flag the deletion and tidy it up later. */
2828 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2830 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2831 struct GUID_txt_buf buf;
2832 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2833 ldb_asprintf_errstring(ldb, "Attribute %s already "
2834 "deleted for target GUID %s",
2835 el->name, guid_str);
2836 if (ldb_attr_cmp(el->name, "member") == 0) {
2837 talloc_free(tmp_ctx);
2838 return LDB_ERR_UNWILLING_TO_PERFORM;
2840 talloc_free(tmp_ctx);
2841 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2845 ret = replmd_update_la_val(old_el->values, exact->v,
2846 exact->dsdb_dn, exact->dsdb_dn,
2847 invocation_id, seq_num, seq_num,
2849 if (ret != LDB_SUCCESS) {
2850 talloc_free(tmp_ctx);
2853 ret = replmd_add_backlink(module, replmd_private,
2858 if (ret != LDB_SUCCESS) {
2859 talloc_free(tmp_ctx);
2866 for (i = 0; i < old_el->num_values; i++) {
2867 if (old_dns[i].v != NULL) {
2868 old_el->values[j] = *old_dns[i].v;
2872 old_el->num_values = j;
2875 el->values = talloc_steal(msg->elements, old_el->values);
2876 el->num_values = old_el->num_values;
2878 talloc_free(tmp_ctx);
2880 /* we now tell the backend to replace all existing values
2881 with the one we have constructed */
2882 el->flags = LDB_FLAG_MOD_REPLACE;
2888 handle replacing a linked attribute
2890 static int replmd_modify_la_replace(struct ldb_module *module,
2891 struct replmd_private *replmd_private,
2892 const struct dsdb_schema *schema,
2893 struct ldb_message *msg,
2894 struct ldb_message_element *el,
2895 struct ldb_message_element *old_el,
2896 const struct dsdb_attribute *schema_attr,
2899 struct ldb_dn *msg_dn,
2900 struct ldb_request *parent)
2902 unsigned int i, old_i, new_i;
2903 struct parsed_dn *dns, *old_dns;
2904 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2906 const struct GUID *invocation_id;
2907 struct ldb_context *ldb = ldb_module_get_ctx(module);
2908 struct ldb_val *new_values = NULL;
2909 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2910 unsigned int old_num_values;
2911 unsigned int repl_num_values;
2912 unsigned int max_num_values;
2915 unix_to_nt_time(&now, t);
2917 invocation_id = samdb_ntds_invocation_id(ldb);
2918 if (!invocation_id) {
2919 return LDB_ERR_OPERATIONS_ERROR;
2923 * The replace operation is unlike the replace and delete cases in that
2924 * we need to look at every existing link to see whether it is being
2925 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2927 * As we are trying to combine two sorted lists, the algorithm we use
2928 * is akin to the merge phase of a merge sort. We interleave the two
2929 * lists, doing different things depending on which side the current
2932 * There are three main cases, with some sub-cases.
2934 * - a DN is in the old list but not the new one. It needs to be
2935 * marked as deleted (but left in the list).
2936 * - maybe it is already deleted, and we have less to do.
2938 * - a DN is in both lists. The old data gets replaced by the new,
2939 * and the list doesn't grow. The old link may have been marked as
2940 * deleted, in which case we undelete it.
2942 * - a DN is in the new list only. We add it in the right place.
2945 old_num_values = old_el ? old_el->num_values : 0;
2946 repl_num_values = el->num_values;
2947 max_num_values = old_num_values + repl_num_values;
2949 if (max_num_values == 0) {
2950 /* There is nothing to do! */
2954 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2955 if (ret != LDB_SUCCESS) {
2956 talloc_free(tmp_ctx);
2960 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2962 if (ret != LDB_SUCCESS) {
2963 talloc_free(tmp_ctx);
2967 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2969 if (ret != LDB_SUCCESS) {
2970 talloc_free(tmp_ctx);
2974 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2975 if (new_values == NULL) {
2976 ldb_module_oom(module);
2977 talloc_free(tmp_ctx);
2978 return LDB_ERR_OPERATIONS_ERROR;
2983 for (i = 0; i < max_num_values; i++) {
2985 struct parsed_dn *old_p, *new_p;
2986 if (old_i < old_num_values && new_i < repl_num_values) {
2987 old_p = &old_dns[old_i];
2988 new_p = &dns[new_i];
2989 cmp = parsed_dn_compare(old_p, new_p);
2990 } else if (old_i < old_num_values) {
2991 /* the new list is empty, read the old list */
2992 old_p = &old_dns[old_i];
2995 } else if (new_i < repl_num_values) {
2996 /* the old list is empty, read new list */
2998 new_p = &dns[new_i];
3006 * An old ones that come before the next replacement
3007 * (if any). We mark it as deleted and add it to the
3010 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3011 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3012 ret = replmd_update_la_val(new_values, old_p->v,
3018 if (ret != LDB_SUCCESS) {
3019 talloc_free(tmp_ctx);
3023 ret = replmd_add_backlink(module, replmd_private,
3026 &old_p->guid, false,
3029 if (ret != LDB_SUCCESS) {
3030 talloc_free(tmp_ctx);
3034 new_values[i] = *old_p->v;
3036 } else if (cmp == 0) {
3038 * We are overwriting one. If it was previously
3039 * deleted, we need to add a backlink.
3041 * Note that if any RMD_FLAGs in an extended new DN
3046 ret = replmd_update_la_val(new_values, old_p->v,
3052 if (ret != LDB_SUCCESS) {
3053 talloc_free(tmp_ctx);
3057 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3058 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3059 ret = replmd_add_backlink(module, replmd_private,
3065 if (ret != LDB_SUCCESS) {
3066 talloc_free(tmp_ctx);
3071 new_values[i] = *old_p->v;
3076 * Replacements that don't match an existing one. We
3077 * just add them to the final list.
3079 ret = replmd_build_la_val(new_values,
3085 if (ret != LDB_SUCCESS) {
3086 talloc_free(tmp_ctx);
3089 ret = replmd_add_backlink(module, replmd_private,
3095 if (ret != LDB_SUCCESS) {
3096 talloc_free(tmp_ctx);
3099 new_values[i] = *new_p->v;
3103 if (old_el != NULL) {
3104 talloc_steal(msg->elements, old_el->values);
3106 el->values = talloc_steal(msg->elements, new_values);
3108 talloc_free(tmp_ctx);
3110 el->flags = LDB_FLAG_MOD_REPLACE;
3117 handle linked attributes in modify requests
3119 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3120 struct replmd_private *replmd_private,
3121 struct ldb_message *msg,
3122 uint64_t seq_num, time_t t,
3123 struct ldb_request *parent)
3125 struct ldb_result *res;
3128 struct ldb_context *ldb = ldb_module_get_ctx(module);
3129 struct ldb_message *old_msg;
3131 const struct dsdb_schema *schema;
3133 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3135 * Nothing special is required for modifying or vanishing links
3136 * in fl2000 since they are just strings in a multi-valued
3139 struct ldb_control *ctrl = ldb_request_get_control(parent,
3140 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3142 ctrl->critical = false;
3150 * We should restrict this to the intersection of the list of
3151 * linked attributes in the schema and the list of attributes
3154 * This will help performance a little, as otherwise we have
3155 * to allocate the entire object value-by-value.
3157 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3158 DSDB_FLAG_NEXT_MODULE |
3159 DSDB_SEARCH_SHOW_RECYCLED |
3160 DSDB_SEARCH_REVEAL_INTERNALS |
3161 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3163 if (ret != LDB_SUCCESS) {
3166 schema = dsdb_get_schema(ldb, res);
3168 return LDB_ERR_OPERATIONS_ERROR;
3171 old_msg = res->msgs[0];
3173 for (i=0; i<msg->num_elements; i++) {
3174 struct ldb_message_element *el = &msg->elements[i];
3175 struct ldb_message_element *old_el, *new_el;
3176 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3177 const struct dsdb_attribute *schema_attr
3178 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3180 ldb_asprintf_errstring(ldb,
3181 "%s: attribute %s is not a valid attribute in schema",
3182 __FUNCTION__, el->name);
3183 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3185 if (schema_attr->linkID == 0) {
3188 if ((schema_attr->linkID & 1) == 1) {
3189 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3192 /* Odd is for the target. Illegal to modify */
3193 ldb_asprintf_errstring(ldb,
3194 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3195 return LDB_ERR_UNWILLING_TO_PERFORM;
3197 old_el = ldb_msg_find_element(old_msg, el->name);
3199 case LDB_FLAG_MOD_REPLACE:
3200 ret = replmd_modify_la_replace(module, replmd_private,
3201 schema, msg, el, old_el,
3202 schema_attr, seq_num, t,
3206 case LDB_FLAG_MOD_DELETE:
3207 ret = replmd_modify_la_delete(module, replmd_private,
3208 schema, msg, el, old_el,
3209 schema_attr, seq_num, t,
3213 case LDB_FLAG_MOD_ADD:
3214 ret = replmd_modify_la_add(module, replmd_private,
3215 schema, msg, el, old_el,
3216 schema_attr, seq_num, t,
3221 ldb_asprintf_errstring(ldb,
3222 "invalid flags 0x%x for %s linked attribute",
3223 el->flags, el->name);
3224 return LDB_ERR_UNWILLING_TO_PERFORM;
3226 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3227 ldb_asprintf_errstring(ldb,
3228 "Attribute %s is single valued but more than one value has been supplied",
3230 /* Return codes as found on Windows 2012r2 */
3231 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3232 return LDB_ERR_CONSTRAINT_VIOLATION;
3234 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3237 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3240 if (ret != LDB_SUCCESS) {
3244 ldb_msg_remove_attr(old_msg, el->name);
3246 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3247 new_el->num_values = el->num_values;
3248 new_el->values = talloc_steal(msg->elements, el->values);
3250 /* TODO: this relises a bit too heavily on the exact
3251 behaviour of ldb_msg_find_element and
3252 ldb_msg_remove_element */
3253 old_el = ldb_msg_find_element(msg, el->name);
3255 ldb_msg_remove_element(msg, old_el);
3265 static int send_rodc_referral(struct ldb_request *req,
3266 struct ldb_context *ldb,
3269 char *referral = NULL;
3270 struct loadparm_context *lp_ctx = NULL;
3271 struct ldb_dn *fsmo_role_dn = NULL;
3272 struct ldb_dn *role_owner_dn = NULL;
3273 const char *domain = NULL;
3276 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3277 struct loadparm_context);
3279 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3280 &fsmo_role_dn, &role_owner_dn);
3282 if (W_ERROR_IS_OK(werr)) {
3283 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3284 if (server_dn != NULL) {
3285 ldb_dn_remove_child_components(server_dn, 1);
3286 domain = samdb_dn_to_dnshostname(ldb, req,
3291 if (domain == NULL) {
3292 domain = lpcfg_dnsdomain(lp_ctx);
3295 referral = talloc_asprintf(req, "ldap://%s/%s",
3297 ldb_dn_get_linearized(dn));
3298 if (referral == NULL) {
3300 return LDB_ERR_OPERATIONS_ERROR;
3303 return ldb_module_send_referral(req, referral);
3307 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3309 struct ldb_context *ldb;
3310 struct replmd_replicated_request *ac;
3311 struct ldb_request *down_req;
3312 struct ldb_message *msg;
3313 time_t t = time(NULL);
3315 bool is_urgent = false, rodc = false;
3316 bool is_schema_nc = false;
3317 unsigned int functional_level;
3318 const struct ldb_message_element *guid_el = NULL;
3319 struct ldb_control *sd_propagation_control;
3320 struct replmd_private *replmd_private =
3321 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3323 /* do not manipulate our control entries */
3324 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3325 return ldb_next_request(module, req);
3328 sd_propagation_control = ldb_request_get_control(req,
3329 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3330 if (sd_propagation_control != NULL) {
3331 if (req->op.mod.message->num_elements != 1) {
3332 return ldb_module_operr(module);
3334 ret = strcmp(req->op.mod.message->elements[0].name,
3335 "nTSecurityDescriptor");
3337 return ldb_module_operr(module);
3340 return ldb_next_request(module, req);
3343 ldb = ldb_module_get_ctx(module);
3345 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3347 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3348 if (guid_el != NULL) {
3349 ldb_set_errstring(ldb,
3350 "replmd_modify: it's not allowed to change the objectGUID!");
3351 return LDB_ERR_CONSTRAINT_VIOLATION;
3354 ac = replmd_ctx_init(module, req);
3356 return ldb_module_oom(module);
3359 functional_level = dsdb_functional_level(ldb);
3361 /* we have to copy the message as the caller might have it as a const */
3362 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3366 return LDB_ERR_OPERATIONS_ERROR;
3369 ldb_msg_remove_attr(msg, "whenChanged");
3370 ldb_msg_remove_attr(msg, "uSNChanged");
3372 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3374 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3375 msg, &ac->seq_num, t, is_schema_nc,
3377 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3378 ret = send_rodc_referral(req, ldb, msg->dn);
3384 if (ret != LDB_SUCCESS) {
3389 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3390 msg, ac->seq_num, t, req);
3391 if (ret != LDB_SUCCESS) {
3397 * - replace the old object with the newly constructed one
3400 ac->is_urgent = is_urgent;
3402 ret = ldb_build_mod_req(&down_req, ldb, ac,
3405 ac, replmd_op_callback,
3407 LDB_REQ_SET_LOCATION(down_req);
3408 if (ret != LDB_SUCCESS) {
3413 /* current partition control is needed by "replmd_op_callback" */
3414 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3415 ret = ldb_request_add_control(down_req,
3416 DSDB_CONTROL_CURRENT_PARTITION_OID,
3418 if (ret != LDB_SUCCESS) {
3424 /* If we are in functional level 2000, then
3425 * replmd_modify_handle_linked_attribs will have done
3427 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3428 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3429 if (ret != LDB_SUCCESS) {
3435 talloc_steal(down_req, msg);
3437 /* we only change whenChanged and uSNChanged if the seq_num
3439 if (ac->seq_num != 0) {
3440 ret = add_time_element(msg, "whenChanged", t);
3441 if (ret != LDB_SUCCESS) {
3447 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3448 if (ret != LDB_SUCCESS) {
3455 /* go on with the call chain */
3456 return ldb_next_request(module, down_req);
3459 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3462 handle a rename request
3464 On a rename we need to do an extra ldb_modify which sets the
3465 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3467 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3469 struct ldb_context *ldb;
3470 struct replmd_replicated_request *ac;
3472 struct ldb_request *down_req;
3474 /* do not manipulate our control entries */
3475 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3476 return ldb_next_request(module, req);
3479 ldb = ldb_module_get_ctx(module);
3481 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3483 ac = replmd_ctx_init(module, req);
3485 return ldb_module_oom(module);
3488 ret = ldb_build_rename_req(&down_req, ldb, ac,
3489 ac->req->op.rename.olddn,
3490 ac->req->op.rename.newdn,
3492 ac, replmd_rename_callback,
3494 LDB_REQ_SET_LOCATION(down_req);
3495 if (ret != LDB_SUCCESS) {
3500 /* go on with the call chain */
3501 return ldb_next_request(module, down_req);
3504 /* After the rename is compleated, update the whenchanged etc */
3505 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3507 struct ldb_context *ldb;
3508 struct ldb_request *down_req;
3509 struct ldb_message *msg;
3510 const struct dsdb_attribute *rdn_attr;
3511 const char *rdn_name;
3512 const struct ldb_val *rdn_val;
3513 const char *attrs[5] = { NULL, };
3514 time_t t = time(NULL);
3516 bool is_urgent = false, rodc = false;
3518 struct replmd_replicated_request *ac =
3519 talloc_get_type(req->context, struct replmd_replicated_request);
3520 struct replmd_private *replmd_private =
3521 talloc_get_type(ldb_module_get_private(ac->module),
3522 struct replmd_private);
3524 ldb = ldb_module_get_ctx(ac->module);
3526 if (ares->error != LDB_SUCCESS) {
3527 return ldb_module_done(ac->req, ares->controls,
3528 ares->response, ares->error);
3531 if (ares->type != LDB_REPLY_DONE) {
3532 ldb_set_errstring(ldb,
3533 "invalid ldb_reply_type in callback");
3535 return ldb_module_done(ac->req, NULL, NULL,
3536 LDB_ERR_OPERATIONS_ERROR);
3540 * - replace the old object with the newly constructed one
3543 msg = ldb_msg_new(ac);
3546 return LDB_ERR_OPERATIONS_ERROR;
3549 msg->dn = ac->req->op.rename.newdn;
3551 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3553 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3554 if (rdn_name == NULL) {
3556 return ldb_module_done(ac->req, NULL, NULL,
3560 /* normalize the rdn attribute name */
3561 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3562 if (rdn_attr == NULL) {
3564 return ldb_module_done(ac->req, NULL, NULL,
3567 rdn_name = rdn_attr->lDAPDisplayName;
3569 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3570 if (rdn_val == NULL) {
3572 return ldb_module_done(ac->req, NULL, NULL,
3576 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3578 return ldb_module_done(ac->req, NULL, NULL,
3581 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3583 return ldb_module_done(ac->req, NULL, NULL,
3586 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3588 return ldb_module_done(ac->req, NULL, NULL,
3591 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3593 return ldb_module_done(ac->req, NULL, NULL,
3598 * here we let replmd_update_rpmd() only search for
3599 * the existing "replPropertyMetaData" and rdn_name attributes.
3601 * We do not want the existing "name" attribute as
3602 * the "name" attribute needs to get the version
3603 * updated on rename even if the rdn value hasn't changed.
3605 * This is the diff of the meta data, for a moved user
3606 * on a w2k8r2 server:
3609 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3610 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3611 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3612 * version : 0x00000001 (1)
3613 * reserved : 0x00000000 (0)
3614 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3615 * local_usn : 0x00000000000037a5 (14245)
3616 * array: struct replPropertyMetaData1
3617 * attid : DRSUAPI_ATTID_name (0x90001)
3618 * - version : 0x00000001 (1)
3619 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3620 * + version : 0x00000002 (2)
3621 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3622 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3623 * - originating_usn : 0x00000000000037a5 (14245)
3624 * - local_usn : 0x00000000000037a5 (14245)
3625 * + originating_usn : 0x0000000000003834 (14388)
3626 * + local_usn : 0x0000000000003834 (14388)
3627 * array: struct replPropertyMetaData1
3628 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3629 * version : 0x00000004 (4)
3631 attrs[0] = "replPropertyMetaData";
3632 attrs[1] = "objectClass";
3633 attrs[2] = "instanceType";
3634 attrs[3] = rdn_name;
3637 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3638 msg, &ac->seq_num, t,
3639 is_schema_nc, &is_urgent, &rodc);
3640 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3641 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3643 return ldb_module_done(req, NULL, NULL, ret);
3646 if (ret != LDB_SUCCESS) {
3648 return ldb_module_done(ac->req, NULL, NULL, ret);
3651 if (ac->seq_num == 0) {
3653 return ldb_module_done(ac->req, NULL, NULL,
3655 "internal error seq_num == 0"));
3657 ac->is_urgent = is_urgent;
3659 ret = ldb_build_mod_req(&down_req, ldb, ac,
3662 ac, replmd_op_callback,
3664 LDB_REQ_SET_LOCATION(down_req);
3665 if (ret != LDB_SUCCESS) {
3670 /* current partition control is needed by "replmd_op_callback" */
3671 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3672 ret = ldb_request_add_control(down_req,
3673 DSDB_CONTROL_CURRENT_PARTITION_OID,
3675 if (ret != LDB_SUCCESS) {
3681 talloc_steal(down_req, msg);
3683 ret = add_time_element(msg, "whenChanged", t);
3684 if (ret != LDB_SUCCESS) {
3690 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3691 if (ret != LDB_SUCCESS) {
3697 /* go on with the call chain - do the modify after the rename */
3698 return ldb_next_request(ac->module, down_req);
3702 * remove links from objects that point at this object when an object
3703 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3704 * RemoveObj which states that link removal due to the object being
3705 * deleted is NOT an originating update - they just go away!
3708 static int replmd_delete_remove_link(struct ldb_module *module,
3709 const struct dsdb_schema *schema,
3710 struct replmd_private *replmd_private,
3713 struct ldb_message_element *el,
3714 const struct dsdb_attribute *sa,
3715 struct ldb_request *parent)
3718 TALLOC_CTX *tmp_ctx = talloc_new(module);
3719 struct ldb_context *ldb = ldb_module_get_ctx(module);
3721 for (i=0; i<el->num_values; i++) {
3722 struct dsdb_dn *dsdb_dn;
3724 struct ldb_message *msg;
3725 const struct dsdb_attribute *target_attr;
3726 struct ldb_message_element *el2;
3728 struct ldb_val dn_val;
3729 uint32_t dsdb_flags = 0;
3730 const char *attrs[] = { NULL, NULL };
3731 struct ldb_result *link_res;
3732 struct ldb_message *link_msg;
3733 struct ldb_message_element *link_el;
3734 struct parsed_dn *link_dns;
3735 struct parsed_dn *p = NULL, *unused = NULL;
3737 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3741 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3743 talloc_free(tmp_ctx);
3744 return LDB_ERR_OPERATIONS_ERROR;
3747 /* remove the link */
3748 msg = ldb_msg_new(tmp_ctx);
3750 ldb_module_oom(module);
3751 talloc_free(tmp_ctx);
3752 return LDB_ERR_OPERATIONS_ERROR;
3756 msg->dn = dsdb_dn->dn;
3758 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3759 if (target_attr == NULL) {
3762 attrs[0] = target_attr->lDAPDisplayName;
3764 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3765 LDB_FLAG_MOD_DELETE, &el2);
3766 if (ret != LDB_SUCCESS) {
3767 ldb_module_oom(module);
3768 talloc_free(tmp_ctx);
3769 return LDB_ERR_OPERATIONS_ERROR;
3772 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3774 DSDB_FLAG_NEXT_MODULE |
3775 DSDB_SEARCH_SHOW_EXTENDED_DN,
3778 if (ret != LDB_SUCCESS) {
3779 talloc_free(tmp_ctx);
3783 link_msg = link_res->msgs[0];
3784 link_el = ldb_msg_find_element(link_msg,
3785 target_attr->lDAPDisplayName);
3786 if (link_el == NULL) {
3787 talloc_free(tmp_ctx);
3788 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3792 * This call 'upgrades' the links in link_dns, but we
3793 * do not commit the result back into the database, so
3794 * this is safe to call in FL2000 or on databases that
3795 * have been run at that level in the past.
3797 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3799 target_attr->syntax->ldap_oid, parent);
3800 if (ret != LDB_SUCCESS) {
3801 talloc_free(tmp_ctx);
3805 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3809 target_attr->syntax->ldap_oid, false);
3810 if (ret != LDB_SUCCESS) {
3811 talloc_free(tmp_ctx);
3816 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3817 "Failed to find forward link on %s "
3818 "as %s to remove backlink %s on %s",
3819 ldb_dn_get_linearized(msg->dn),
3820 target_attr->lDAPDisplayName,
3821 sa->lDAPDisplayName,
3822 ldb_dn_get_linearized(dn));
3823 talloc_free(tmp_ctx);
3824 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3828 /* This needs to get the Binary DN, by first searching */
3829 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3832 dn_val = data_blob_string_const(dn_str);
3833 el2->values = &dn_val;
3834 el2->num_values = 1;
3837 * Ensure that we tell the modification to vanish any linked
3838 * attributes (not simply mark them as isDeleted = TRUE)
3840 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3842 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3843 if (ret != LDB_SUCCESS) {
3844 talloc_free(tmp_ctx);
3848 talloc_free(tmp_ctx);
3854 handle update of replication meta data for deletion of objects
3856 This also handles the mapping of delete to a rename operation
3857 to allow deletes to be replicated.
3859 It also handles the incoming deleted objects, to ensure they are
3860 fully deleted here. In that case re_delete is true, and we do not
3861 use this as a signal to change the deleted state, just reinforce it.
3864 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3866 int ret = LDB_ERR_OTHER;
3867 bool retb, disallow_move_on_delete;
3868 struct ldb_dn *old_dn, *new_dn;
3869 const char *rdn_name;
3870 const struct ldb_val *rdn_value, *new_rdn_value;
3872 struct ldb_context *ldb = ldb_module_get_ctx(module);
3873 const struct dsdb_schema *schema;
3874 struct ldb_message *msg, *old_msg;
3875 struct ldb_message_element *el;
3876 TALLOC_CTX *tmp_ctx;
3877 struct ldb_result *res, *parent_res;
3878 static const char * const preserved_attrs[] = {
3879 /* yes, this really is a hard coded list. See MS-ADTS
3880 section 3.1.1.5.5.1.1 */
3883 "dNReferenceUpdate",
3894 "msDS-LastKnownRDN",
3900 "distinguishedName",
3904 "proxiedObjectName",
3906 "nTSecurityDescriptor",
3907 "replPropertyMetaData",
3909 "securityIdentifier",
3917 "userAccountControl",
3924 static const char * const all_attrs[] = {
3925 DSDB_SECRET_ATTRIBUTES,
3929 unsigned int i, el_count = 0;
3930 uint32_t dsdb_flags = 0;
3931 struct replmd_private *replmd_private;
3932 enum deletion_state deletion_state, next_deletion_state;
3934 if (ldb_dn_is_special(req->op.del.dn)) {
3935 return ldb_next_request(module, req);
3939 * We have to allow dbcheck to remove an object that
3940 * is beyond repair, and to do so totally. This could
3941 * mean we we can get a partial object from the other
3942 * DC, causing havoc, so dbcheck suggests
3943 * re-replication first. dbcheck sets both DBCHECK
3944 * and RELAX in this situation.
3946 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3947 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3948 /* really, really remove it */
3949 return ldb_next_request(module, req);
3952 tmp_ctx = talloc_new(ldb);
3955 return LDB_ERR_OPERATIONS_ERROR;
3958 schema = dsdb_get_schema(ldb, tmp_ctx);
3960 talloc_free(tmp_ctx);
3961 return LDB_ERR_OPERATIONS_ERROR;
3964 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3966 /* we need the complete msg off disk, so we can work out which
3967 attributes need to be removed */
3968 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3969 DSDB_FLAG_NEXT_MODULE |
3970 DSDB_SEARCH_SHOW_RECYCLED |
3971 DSDB_SEARCH_REVEAL_INTERNALS |
3972 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3973 if (ret != LDB_SUCCESS) {
3974 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3975 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3976 re_delete ? "re-delete" : "delete",
3977 ldb_dn_get_linearized(old_dn),
3978 ldb_errstring(ldb_module_get_ctx(module)));
3979 talloc_free(tmp_ctx);
3982 old_msg = res->msgs[0];
3984 replmd_deletion_state(module, old_msg,
3986 &next_deletion_state);
3988 /* This supports us noticing an incoming isDeleted and acting on it */
3990 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3991 next_deletion_state = deletion_state;
3994 if (next_deletion_state == OBJECT_REMOVED) {
3996 * We have to prevent objects being deleted, even if
3997 * the administrator really wants them gone, as
3998 * without the tombstone, we can get a partial object
3999 * from the other DC, causing havoc.
4001 * The only other valid case is when the 180 day
4002 * timeout has expired, when relax is specified.
4004 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4005 /* it is already deleted - really remove it this time */
4006 talloc_free(tmp_ctx);
4007 return ldb_next_request(module, req);
4010 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4011 "This check is to prevent corruption of the replicated state.",
4012 ldb_dn_get_linearized(old_msg->dn));
4013 return LDB_ERR_UNWILLING_TO_PERFORM;
4016 rdn_name = ldb_dn_get_rdn_name(old_dn);
4017 rdn_value = ldb_dn_get_rdn_val(old_dn);
4018 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4019 talloc_free(tmp_ctx);
4020 return ldb_operr(ldb);
4023 msg = ldb_msg_new(tmp_ctx);
4025 ldb_module_oom(module);
4026 talloc_free(tmp_ctx);
4027 return LDB_ERR_OPERATIONS_ERROR;
4032 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4033 disallow_move_on_delete =
4034 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4035 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4037 /* work out where we will be renaming this object to */
4038 if (!disallow_move_on_delete) {
4039 struct ldb_dn *deleted_objects_dn;
4040 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4041 &deleted_objects_dn);
4044 * We should not move objects if we can't find the
4045 * deleted objects DN. Not moving (or otherwise
4046 * harming) the Deleted Objects DN itself is handled
4049 if (re_delete && (ret != LDB_SUCCESS)) {
4050 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4051 if (new_dn == NULL) {
4052 ldb_module_oom(module);
4053 talloc_free(tmp_ctx);
4054 return LDB_ERR_OPERATIONS_ERROR;
4056 } else if (ret != LDB_SUCCESS) {
4057 /* this is probably an attempted delete on a partition
4058 * that doesn't allow delete operations, such as the
4059 * schema partition */
4060 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4061 ldb_dn_get_linearized(old_dn));
4062 talloc_free(tmp_ctx);
4063 return LDB_ERR_UNWILLING_TO_PERFORM;
4065 new_dn = deleted_objects_dn;
4068 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4069 if (new_dn == NULL) {
4070 ldb_module_oom(module);
4071 talloc_free(tmp_ctx);
4072 return LDB_ERR_OPERATIONS_ERROR;
4076 /* get the objects GUID from the search we just did */
4077 guid = samdb_result_guid(old_msg, "objectGUID");
4079 if (deletion_state == OBJECT_NOT_DELETED) {
4080 /* Add a formatted child */
4081 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
4083 ldb_dn_escape_value(tmp_ctx, *rdn_value),
4084 GUID_string(tmp_ctx, &guid));
4086 ldb_asprintf_errstring(ldb, __location__
4087 ": Unable to add a formatted child to dn: %s",
4088 ldb_dn_get_linearized(new_dn));
4089 talloc_free(tmp_ctx);
4090 return LDB_ERR_OPERATIONS_ERROR;
4093 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4094 if (ret != LDB_SUCCESS) {
4095 ldb_asprintf_errstring(ldb, __location__
4096 ": Failed to add isDeleted string to the msg");
4097 talloc_free(tmp_ctx);
4100 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4103 * No matter what has happened with other renames etc, try again to
4104 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4107 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4108 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4110 ldb_asprintf_errstring(ldb, __location__
4111 ": Unable to add a prepare rdn of %s",
4112 ldb_dn_get_linearized(rdn));
4113 talloc_free(tmp_ctx);
4114 return LDB_ERR_OPERATIONS_ERROR;
4116 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4118 retb = ldb_dn_add_child(new_dn, rdn);
4120 ldb_asprintf_errstring(ldb, __location__
4121 ": Unable to add rdn %s to base dn: %s",
4122 ldb_dn_get_linearized(rdn),
4123 ldb_dn_get_linearized(new_dn));
4124 talloc_free(tmp_ctx);
4125 return LDB_ERR_OPERATIONS_ERROR;
4130 now we need to modify the object in the following ways:
4132 - add isDeleted=TRUE
4133 - update rDN and name, with new rDN
4134 - remove linked attributes
4135 - remove objectCategory and sAMAccountType
4136 - remove attribs not on the preserved list
4137 - preserved if in above list, or is rDN
4138 - remove all linked attribs from this object
4139 - remove all links from other objects to this object
4140 - add lastKnownParent
4141 - update replPropertyMetaData?
4143 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4146 if (deletion_state == OBJECT_NOT_DELETED) {
4147 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4148 char *parent_dn_str = NULL;
4150 /* we need the storage form of the parent GUID */
4151 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4153 DSDB_FLAG_NEXT_MODULE |
4154 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4155 DSDB_SEARCH_REVEAL_INTERNALS|
4156 DSDB_SEARCH_SHOW_RECYCLED, req);
4157 if (ret != LDB_SUCCESS) {
4158 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4159 "repmd_delete: Failed to %s %s, "
4160 "because we failed to find it's parent (%s): %s",
4161 re_delete ? "re-delete" : "delete",
4162 ldb_dn_get_linearized(old_dn),
4163 ldb_dn_get_linearized(parent_dn),
4164 ldb_errstring(ldb_module_get_ctx(module)));
4165 talloc_free(tmp_ctx);
4170 * Now we can use the DB version,
4171 * it will have the extended DN info in it
4173 parent_dn = parent_res->msgs[0]->dn;
4174 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4177 if (parent_dn_str == NULL) {
4178 talloc_free(tmp_ctx);
4179 return ldb_module_oom(module);
4182 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4184 if (ret != LDB_SUCCESS) {
4185 ldb_asprintf_errstring(ldb, __location__
4186 ": Failed to add lastKnownParent "
4187 "string when deleting %s",
4188 ldb_dn_get_linearized(old_dn));
4189 talloc_free(tmp_ctx);
4192 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4194 if (next_deletion_state == OBJECT_DELETED) {
4195 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4196 if (ret != LDB_SUCCESS) {
4197 ldb_asprintf_errstring(ldb, __location__
4198 ": Failed to add msDS-LastKnownRDN "
4199 "string when deleting %s",
4200 ldb_dn_get_linearized(old_dn));
4201 talloc_free(tmp_ctx);
4204 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4208 switch (next_deletion_state) {
4210 case OBJECT_RECYCLED:
4211 case OBJECT_TOMBSTONE:
4214 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4215 * describes what must be removed from a tombstone
4218 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4219 * describes what must be removed from a recycled
4225 * we also mark it as recycled, meaning this object can't be
4226 * recovered (we are stripping its attributes).
4227 * This is done only if we have this schema object of course ...
4228 * This behavior is identical to the one of Windows 2008R2 which
4229 * always set the isRecycled attribute, even if the recycle-bin is
4230 * not activated and what ever the forest level is.
4232 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4233 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4234 if (ret != LDB_SUCCESS) {
4235 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4236 ldb_module_oom(module);
4237 talloc_free(tmp_ctx);
4240 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4243 replmd_private = talloc_get_type(ldb_module_get_private(module),
4244 struct replmd_private);
4245 /* work out which of the old attributes we will be removing */
4246 for (i=0; i<old_msg->num_elements; i++) {
4247 const struct dsdb_attribute *sa;
4248 el = &old_msg->elements[i];
4249 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4251 talloc_free(tmp_ctx);
4252 return LDB_ERR_OPERATIONS_ERROR;
4254 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4255 /* don't remove the rDN */
4258 if (sa->linkID & 1) {
4260 we have a backlink in this object
4261 that needs to be removed. We're not
4262 allowed to remove it directly
4263 however, so we instead setup a
4264 modify to delete the corresponding
4267 ret = replmd_delete_remove_link(module, schema,
4271 if (ret != LDB_SUCCESS) {
4272 const char *old_dn_str
4273 = ldb_dn_get_linearized(old_dn);
4274 ldb_asprintf_errstring(ldb,
4276 ": Failed to remove backlink of "
4277 "%s when deleting %s: %s",
4280 ldb_errstring(ldb));
4281 talloc_free(tmp_ctx);
4282 return LDB_ERR_OPERATIONS_ERROR;
4284 /* now we continue, which means we
4285 won't remove this backlink
4289 } else if (sa->linkID == 0) {
4290 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4293 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4298 * Ensure that we tell the modification to vanish any linked
4299 * attributes (not simply mark them as isDeleted = TRUE)
4301 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4303 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4304 if (ret != LDB_SUCCESS) {
4305 talloc_free(tmp_ctx);
4306 ldb_module_oom(module);
4313 case OBJECT_DELETED:
4315 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4316 * describes what must be removed from a deleted
4320 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4321 if (ret != LDB_SUCCESS) {
4322 talloc_free(tmp_ctx);
4323 ldb_module_oom(module);
4327 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4328 if (ret != LDB_SUCCESS) {
4329 talloc_free(tmp_ctx);
4330 ldb_module_oom(module);
4340 if (deletion_state == OBJECT_NOT_DELETED) {
4341 const struct dsdb_attribute *sa;
4343 /* work out what the new rdn value is, for updating the
4344 rDN and name fields */
4345 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4346 if (new_rdn_value == NULL) {
4347 talloc_free(tmp_ctx);
4348 return ldb_operr(ldb);
4351 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4353 talloc_free(tmp_ctx);
4354 return LDB_ERR_OPERATIONS_ERROR;
4357 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4359 if (ret != LDB_SUCCESS) {
4360 talloc_free(tmp_ctx);
4363 el->flags = LDB_FLAG_MOD_REPLACE;
4365 el = ldb_msg_find_element(old_msg, "name");
4367 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4368 if (ret != LDB_SUCCESS) {
4369 talloc_free(tmp_ctx);
4372 el->flags = LDB_FLAG_MOD_REPLACE;
4377 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4382 * No matter what has happned with other renames, try again to
4383 * get this to be under the deleted DN.
4385 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4386 /* now rename onto the new DN */
4387 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4388 if (ret != LDB_SUCCESS){
4389 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4390 ldb_dn_get_linearized(old_dn),
4391 ldb_dn_get_linearized(new_dn),
4392 ldb_errstring(ldb)));
4393 talloc_free(tmp_ctx);
4399 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4400 if (ret != LDB_SUCCESS) {
4401 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4402 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4403 talloc_free(tmp_ctx);
4407 talloc_free(tmp_ctx);
4409 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4412 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4414 return replmd_delete_internals(module, req, false);
4418 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4423 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4425 int ret = LDB_ERR_OTHER;
4426 /* TODO: do some error mapping */
4428 /* Let the caller know the full WERROR */
4429 ar->objs->error = status;
4435 static struct replPropertyMetaData1 *
4436 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4437 enum drsuapi_DsAttributeId attid)
4440 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4442 for (i = 0; i < rpmd_ctr->count; i++) {
4443 if (rpmd_ctr->array[i].attid == attid) {
4444 return &rpmd_ctr->array[i];
4452 return true if an update is newer than an existing entry
4453 see section 5.11 of MS-ADTS
4455 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4456 const struct GUID *update_invocation_id,
4457 uint32_t current_version,
4458 uint32_t update_version,
4459 NTTIME current_change_time,
4460 NTTIME update_change_time)
4462 if (update_version != current_version) {
4463 return update_version > current_version;
4465 if (update_change_time != current_change_time) {
4466 return update_change_time > current_change_time;
4468 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4471 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4472 struct replPropertyMetaData1 *new_m)
4474 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4475 &new_m->originating_invocation_id,
4478 cur_m->originating_change_time,
4479 new_m->originating_change_time);
4482 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4483 struct replPropertyMetaData1 *cur_m,
4484 struct replPropertyMetaData1 *new_m)
4489 * If the new replPropertyMetaData entry for this attribute is
4490 * not provided (this happens in the case where we look for
4491 * ATTID_name, but the name was not changed), then the local
4492 * state is clearly still current, as the remote
4493 * server didn't send it due to being older the high watermark
4496 if (new_m == NULL) {
4500 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4502 * if we compare equal then do an
4503 * update. This is used when a client
4504 * asks for a FULL_SYNC, and can be
4505 * used to recover a corrupt
4508 * This call is a bit tricky, what we
4509 * are doing it turning the 'is_newer'
4510 * call into a 'not is older' by
4511 * swapping cur_m and new_m, and negating the
4514 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4517 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4527 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
4529 const struct ldb_val *rdn_val;
4530 const char *rdn_name;
4531 struct ldb_dn *new_dn;
4533 rdn_val = ldb_dn_get_rdn_val(dn);
4534 rdn_name = ldb_dn_get_rdn_name(dn);
4535 if (!rdn_val || !rdn_name) {
4539 new_dn = ldb_dn_copy(mem_ctx, dn);
4544 if (!ldb_dn_remove_child_components(new_dn, 1)) {
4548 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
4550 ldb_dn_escape_value(new_dn, *rdn_val),
4551 GUID_string(new_dn, guid))) {
4560 perform a modify operation which sets the rDN and name attributes to
4561 their current values. This has the effect of changing these
4562 attributes to have been last updated by the current DC. This is
4563 needed to ensure that renames performed as part of conflict
4564 resolution are propogated to other DCs
4566 static int replmd_name_modify(struct replmd_replicated_request *ar,
4567 struct ldb_request *req, struct ldb_dn *dn)
4569 struct ldb_message *msg;
4570 const char *rdn_name;
4571 const struct ldb_val *rdn_val;
4572 const struct dsdb_attribute *rdn_attr;
4575 msg = ldb_msg_new(req);
4581 rdn_name = ldb_dn_get_rdn_name(dn);
4582 if (rdn_name == NULL) {
4586 /* normalize the rdn attribute name */
4587 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4588 if (rdn_attr == NULL) {
4591 rdn_name = rdn_attr->lDAPDisplayName;
4593 rdn_val = ldb_dn_get_rdn_val(dn);
4594 if (rdn_val == NULL) {
4598 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4601 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4604 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4607 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4612 * We have to mark this as a replicated update otherwise
4613 * schema_data may reject a rename in the schema partition
4616 ret = dsdb_module_modify(ar->module, msg,
4617 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4619 if (ret != LDB_SUCCESS) {
4620 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4621 ldb_dn_get_linearized(dn),
4622 ldb_errstring(ldb_module_get_ctx(ar->module))));
4632 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4633 ldb_dn_get_linearized(dn)));
4634 return LDB_ERR_OPERATIONS_ERROR;
4639 callback for conflict DN handling where we have renamed the incoming
4640 record. After renaming it, we need to ensure the change of name and
4641 rDN for the incoming record is seen as an originating update by this DC.
4643 This also handles updating lastKnownParent for entries sent to lostAndFound
4645 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4647 struct replmd_replicated_request *ar =
4648 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4649 struct ldb_dn *conflict_dn = NULL;
4652 if (ares->error != LDB_SUCCESS) {
4653 /* call the normal callback for everything except success */
4654 return replmd_op_callback(req, ares);
4657 switch (req->operation) {
4659 conflict_dn = req->op.add.message->dn;
4662 conflict_dn = req->op.mod.message->dn;
4665 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4668 /* perform a modify of the rDN and name of the record */
4669 ret = replmd_name_modify(ar, req, conflict_dn);
4670 if (ret != LDB_SUCCESS) {
4672 return replmd_op_callback(req, ares);
4675 if (ar->objs->objects[ar->index_current].last_known_parent) {
4676 struct ldb_message *msg = ldb_msg_new(req);
4678 ldb_module_oom(ar->module);
4679 return LDB_ERR_OPERATIONS_ERROR;
4682 msg->dn = req->op.add.message->dn;
4684 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4685 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4686 if (ret != LDB_SUCCESS) {
4687 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4688 ldb_module_oom(ar->module);
4691 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4693 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4694 if (ret != LDB_SUCCESS) {
4695 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4696 ldb_dn_get_linearized(msg->dn),
4697 ldb_errstring(ldb_module_get_ctx(ar->module))));
4703 return replmd_op_callback(req, ares);
4707 callback for replmd_replicated_apply_add()
4708 This copes with the creation of conflict records in the case where
4709 the DN exists, but with a different objectGUID
4711 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))
4713 struct ldb_dn *conflict_dn;
4714 struct replmd_replicated_request *ar =
4715 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4716 struct ldb_result *res;
4717 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4719 const struct ldb_val *omd_value;
4720 struct replPropertyMetaDataBlob omd, *rmd;
4721 enum ndr_err_code ndr_err;
4722 bool rename_incoming_record, rodc;
4723 struct replPropertyMetaData1 *rmd_name, *omd_name;
4724 struct ldb_message *msg;
4725 struct ldb_request *down_req = NULL;
4727 /* call the normal callback for success */
4728 if (ares->error == LDB_SUCCESS) {
4729 return callback(req, ares);
4733 * we have a conflict, and need to decide if we will keep the
4734 * new record or the old record
4737 msg = ar->objs->objects[ar->index_current].msg;
4738 conflict_dn = msg->dn;
4740 /* For failures other than conflicts, fail the whole operation here */
4741 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4742 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4743 ldb_dn_get_linearized(conflict_dn),
4744 ldb_errstring(ldb_module_get_ctx(ar->module)));
4746 return ldb_module_done(ar->req, NULL, NULL,
4747 LDB_ERR_OPERATIONS_ERROR);
4750 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4751 if (ret != LDB_SUCCESS) {
4752 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)));
4753 return ldb_module_done(ar->req, NULL, NULL,
4754 LDB_ERR_OPERATIONS_ERROR);
4760 * We are on an RODC, or were a GC for this
4761 * partition, so we have to fail this until
4762 * someone who owns the partition sorts it
4765 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4766 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4767 " - We must fail the operation until a master for this partition resolves the conflict",
4768 ldb_dn_get_linearized(conflict_dn));
4773 * first we need the replPropertyMetaData attribute from the
4774 * local, conflicting record
4776 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4778 DSDB_FLAG_NEXT_MODULE |
4779 DSDB_SEARCH_SHOW_DELETED |
4780 DSDB_SEARCH_SHOW_RECYCLED, req);
4781 if (ret != LDB_SUCCESS) {
4782 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4783 ldb_dn_get_linearized(conflict_dn)));
4787 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4788 if (omd_value == NULL) {
4789 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4790 ldb_dn_get_linearized(conflict_dn)));
4794 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4795 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4796 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4797 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4798 ldb_dn_get_linearized(conflict_dn)));
4802 rmd = ar->objs->objects[ar->index_current].meta_data;
4805 * we decide which is newer based on the RPMD on the name
4806 * attribute. See [MS-DRSR] ResolveNameConflict.
4808 * We expect omd_name to be present, as this is from a local
4809 * search, but while rmd_name should have been given to us by
4810 * the remote server, if it is missing we just prefer the
4812 * replmd_replPropertyMetaData1_new_should_be_taken()
4814 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4815 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4817 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4818 ldb_dn_get_linearized(conflict_dn)));
4823 * Should we preserve the current record, and so rename the
4824 * incoming record to be a conflict?
4826 rename_incoming_record
4827 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4828 omd_name, rmd_name);
4830 if (rename_incoming_record) {
4832 struct ldb_dn *new_dn;
4834 guid = samdb_result_guid(msg, "objectGUID");
4835 if (GUID_all_zero(&guid)) {
4836 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4837 ldb_dn_get_linearized(conflict_dn)));
4840 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4841 if (new_dn == NULL) {
4842 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4843 ldb_dn_get_linearized(conflict_dn)));
4847 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4848 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4850 /* re-submit the request, but with the new DN */
4851 callback = replmd_op_name_modify_callback;
4854 /* we are renaming the existing record */
4856 struct ldb_dn *new_dn;
4858 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4859 if (GUID_all_zero(&guid)) {
4860 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4861 ldb_dn_get_linearized(conflict_dn)));
4865 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4866 if (new_dn == NULL) {
4867 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4868 ldb_dn_get_linearized(conflict_dn)));
4872 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4873 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4875 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4876 DSDB_FLAG_OWN_MODULE, req);
4877 if (ret != LDB_SUCCESS) {
4878 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4879 ldb_dn_get_linearized(conflict_dn),
4880 ldb_dn_get_linearized(new_dn),
4881 ldb_errstring(ldb_module_get_ctx(ar->module))));
4886 * now we need to ensure that the rename is seen as an
4887 * originating update. We do that with a modify.
4889 ret = replmd_name_modify(ar, req, new_dn);
4890 if (ret != LDB_SUCCESS) {
4894 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4895 ldb_dn_get_linearized(req->op.add.message->dn)));
4898 ret = ldb_build_add_req(&down_req,
4899 ldb_module_get_ctx(ar->module),
4906 if (ret != LDB_SUCCESS) {
4909 LDB_REQ_SET_LOCATION(down_req);
4911 /* current partition control needed by "repmd_op_callback" */
4912 ret = ldb_request_add_control(down_req,
4913 DSDB_CONTROL_CURRENT_PARTITION_OID,
4915 if (ret != LDB_SUCCESS) {
4916 return replmd_replicated_request_error(ar, ret);
4919 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4920 /* this tells the partition module to make it a
4921 partial replica if creating an NC */
4922 ret = ldb_request_add_control(down_req,
4923 DSDB_CONTROL_PARTIAL_REPLICA,
4925 if (ret != LDB_SUCCESS) {
4926 return replmd_replicated_request_error(ar, ret);
4931 * Finally we re-run the add, otherwise the new record won't
4932 * exist, as we are here because of that exact failure!
4934 return ldb_next_request(ar->module, down_req);
4937 /* on failure make the caller get the error. This means
4938 * replication will stop with an error, but there is not much
4941 return ldb_module_done(ar->req, NULL, NULL,
4946 callback for replmd_replicated_apply_add()
4947 This copes with the creation of conflict records in the case where
4948 the DN exists, but with a different objectGUID
4950 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4952 struct replmd_replicated_request *ar =
4953 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4955 if (ar->objs->objects[ar->index_current].last_known_parent) {
4956 /* This is like a conflict DN, where we put the object in LostAndFound
4957 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4958 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4961 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4965 this is called when a new object comes in over DRS
4967 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4969 struct ldb_context *ldb;
4970 struct ldb_request *change_req;
4971 enum ndr_err_code ndr_err;
4972 struct ldb_message *msg;
4973 struct replPropertyMetaDataBlob *md;
4974 struct ldb_val md_value;
4977 bool remote_isDeleted = false;
4980 time_t t = time(NULL);
4981 const struct ldb_val *rdn_val;
4982 struct replmd_private *replmd_private =
4983 talloc_get_type(ldb_module_get_private(ar->module),
4984 struct replmd_private);
4985 unix_to_nt_time(&now, t);
4987 ldb = ldb_module_get_ctx(ar->module);
4988 msg = ar->objs->objects[ar->index_current].msg;
4989 md = ar->objs->objects[ar->index_current].meta_data;
4990 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4992 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4993 if (ret != LDB_SUCCESS) {
4994 return replmd_replicated_request_error(ar, ret);
4997 ret = dsdb_msg_add_guid(msg,
4998 &ar->objs->objects[ar->index_current].object_guid,
5000 if (ret != LDB_SUCCESS) {
5001 return replmd_replicated_request_error(ar, ret);
5004 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5005 if (ret != LDB_SUCCESS) {
5006 return replmd_replicated_request_error(ar, ret);
5009 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5010 if (ret != LDB_SUCCESS) {
5011 return replmd_replicated_request_error(ar, ret);
5014 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5015 if (ret != LDB_SUCCESS) {
5016 return replmd_replicated_request_error(ar, ret);
5019 /* remove any message elements that have zero values */
5020 for (i=0; i<msg->num_elements; i++) {
5021 struct ldb_message_element *el = &msg->elements[i];
5023 if (el->num_values == 0) {
5024 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5025 ldb_asprintf_errstring(ldb, __location__
5026 ": empty objectClass sent on %s, aborting replication\n",
5027 ldb_dn_get_linearized(msg->dn));
5028 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5031 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5033 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5034 msg->num_elements--;
5041 struct GUID_txt_buf guid_txt;
5043 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5046 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5047 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5050 } else if (DEBUGLVL(4)) {
5051 struct GUID_txt_buf guid_txt;
5052 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5053 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5054 ldb_dn_get_linearized(msg->dn)));
5056 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5057 "isDeleted", false);
5060 * the meta data array is already sorted by the caller, except
5061 * for the RDN, which needs to be added.
5065 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5066 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5067 md, ar, now, is_schema_nc,
5069 if (ret != LDB_SUCCESS) {
5070 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5071 return replmd_replicated_request_error(ar, ret);
5074 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5075 if (ret != LDB_SUCCESS) {
5076 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5077 return replmd_replicated_request_error(ar, ret);
5080 for (i=0; i < md->ctr.ctr1.count; i++) {
5081 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5083 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5084 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5085 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5086 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5087 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5089 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5090 if (ret != LDB_SUCCESS) {
5091 return replmd_replicated_request_error(ar, ret);
5094 replmd_ldb_message_sort(msg, ar->schema);
5096 if (!remote_isDeleted) {
5097 ret = dsdb_module_schedule_sd_propagation(ar->module,
5098 ar->objs->partition_dn,
5100 if (ret != LDB_SUCCESS) {
5101 return replmd_replicated_request_error(ar, ret);
5105 ar->isDeleted = remote_isDeleted;
5107 ret = ldb_build_add_req(&change_req,
5113 replmd_op_add_callback,
5115 LDB_REQ_SET_LOCATION(change_req);
5116 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5118 /* current partition control needed by "repmd_op_callback" */
5119 ret = ldb_request_add_control(change_req,
5120 DSDB_CONTROL_CURRENT_PARTITION_OID,
5122 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5124 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5125 /* this tells the partition module to make it a
5126 partial replica if creating an NC */
5127 ret = ldb_request_add_control(change_req,
5128 DSDB_CONTROL_PARTIAL_REPLICA,
5130 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5133 return ldb_next_request(ar->module, change_req);
5136 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5137 struct ldb_reply *ares)
5139 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5140 struct replmd_replicated_request);
5144 return ldb_module_done(ar->req, NULL, NULL,
5145 LDB_ERR_OPERATIONS_ERROR);
5149 * The error NO_SUCH_OBJECT is not expected, unless the search
5150 * base is the partition DN, and that case doesn't happen here
5151 * because then we wouldn't get a parent_guid_value in any
5154 if (ares->error != LDB_SUCCESS) {
5155 return ldb_module_done(ar->req, ares->controls,
5156 ares->response, ares->error);
5159 switch (ares->type) {
5160 case LDB_REPLY_ENTRY:
5162 struct ldb_message *parent_msg = ares->message;
5163 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5164 struct ldb_dn *parent_dn;
5167 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5168 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5169 /* Per MS-DRSR 4.1.10.6.10
5170 * FindBestParentObject we need to move this
5171 * new object under a deleted object to
5173 struct ldb_dn *nc_root;
5175 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5176 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5177 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5178 "No suitable NC root found for %s. "
5179 "We need to move this object because parent object %s "
5180 "is deleted, but this object is not.",
5181 ldb_dn_get_linearized(msg->dn),
5182 ldb_dn_get_linearized(parent_msg->dn));
5183 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5184 } else if (ret != LDB_SUCCESS) {
5185 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5186 "Unable to find NC root for %s: %s. "
5187 "We need to move this object because parent object %s "
5188 "is deleted, but this object is not.",
5189 ldb_dn_get_linearized(msg->dn),
5190 ldb_errstring(ldb_module_get_ctx(ar->module)),
5191 ldb_dn_get_linearized(parent_msg->dn));
5192 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5195 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5197 DS_GUID_LOSTANDFOUND_CONTAINER,
5199 if (ret != LDB_SUCCESS) {
5200 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5201 "Unable to find LostAndFound Container for %s "
5202 "in partition %s: %s. "
5203 "We need to move this object because parent object %s "
5204 "is deleted, but this object is not.",
5205 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5206 ldb_errstring(ldb_module_get_ctx(ar->module)),
5207 ldb_dn_get_linearized(parent_msg->dn));
5208 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5210 ar->objs->objects[ar->index_current].last_known_parent
5211 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5215 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5218 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5220 comp_num = ldb_dn_get_comp_num(msg->dn);
5222 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5224 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5227 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5229 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5233 case LDB_REPLY_REFERRAL:
5234 /* we ignore referrals */
5237 case LDB_REPLY_DONE:
5239 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5240 struct GUID_txt_buf str_buf;
5241 if (ar->search_msg != NULL) {
5242 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5243 "No parent with GUID %s found for object locally known as %s",
5244 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5245 ldb_dn_get_linearized(ar->search_msg->dn));
5247 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5248 "No parent with GUID %s found for object remotely known as %s",
5249 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5250 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5254 * This error code is really important, as it
5255 * is the flag back to the callers to retry
5256 * this with DRSUAPI_DRS_GET_ANC, and so get
5257 * the parent objects before the child
5260 return ldb_module_done(ar->req, NULL, NULL,
5261 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5264 if (ar->search_msg != NULL) {
5265 ret = replmd_replicated_apply_merge(ar);
5267 ret = replmd_replicated_apply_add(ar);
5269 if (ret != LDB_SUCCESS) {
5270 return ldb_module_done(ar->req, NULL, NULL, ret);
5279 * Look for the parent object, so we put the new object in the right
5280 * place This is akin to NameObject in MS-DRSR - this routine and the
5281 * callbacks find the right parent name, and correct name for this
5285 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5287 struct ldb_context *ldb;
5291 struct ldb_request *search_req;
5292 static const char *attrs[] = {"isDeleted", NULL};
5293 struct GUID_txt_buf guid_str_buf;
5295 ldb = ldb_module_get_ctx(ar->module);
5297 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5298 if (ar->search_msg != NULL) {
5299 return replmd_replicated_apply_merge(ar);
5301 return replmd_replicated_apply_add(ar);
5305 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5308 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5309 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5311 ret = ldb_build_search_req(&search_req,
5314 ar->objs->partition_dn,
5320 replmd_replicated_apply_search_for_parent_callback,
5322 LDB_REQ_SET_LOCATION(search_req);
5324 ret = dsdb_request_add_controls(search_req,
5325 DSDB_SEARCH_SHOW_RECYCLED|
5326 DSDB_SEARCH_SHOW_DELETED|
5327 DSDB_SEARCH_SHOW_EXTENDED_DN);
5328 if (ret != LDB_SUCCESS) {
5332 return ldb_next_request(ar->module, search_req);
5336 handle renames that come in over DRS replication
5338 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5339 struct ldb_message *msg,
5340 struct ldb_request *parent,
5344 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5345 struct ldb_result *res;
5346 struct ldb_dn *conflict_dn;
5347 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5348 const struct ldb_val *omd_value;
5349 struct replPropertyMetaDataBlob omd, *rmd;
5350 enum ndr_err_code ndr_err;
5351 bool rename_incoming_record, rodc;
5352 struct replPropertyMetaData1 *rmd_name, *omd_name;
5353 struct ldb_dn *new_dn;
5356 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5357 ldb_dn_get_linearized(ar->search_msg->dn),
5358 ldb_dn_get_linearized(msg->dn)));
5361 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5362 DSDB_FLAG_NEXT_MODULE, ar->req);
5363 if (ret == LDB_SUCCESS) {
5364 talloc_free(tmp_ctx);
5369 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5370 talloc_free(tmp_ctx);
5371 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5372 ldb_dn_get_linearized(ar->search_msg->dn),
5373 ldb_dn_get_linearized(msg->dn),
5374 ldb_errstring(ldb_module_get_ctx(ar->module)));
5378 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5379 if (ret != LDB_SUCCESS) {
5380 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5381 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5382 ldb_errstring(ldb_module_get_ctx(ar->module)));
5383 return LDB_ERR_OPERATIONS_ERROR;
5386 * we have a conflict, and need to decide if we will keep the
5387 * new record or the old record
5390 conflict_dn = msg->dn;
5394 * We are on an RODC, or were a GC for this
5395 * partition, so we have to fail this until
5396 * someone who owns the partition sorts it
5399 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5400 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5401 " - We must fail the operation until a master for this partition resolves the conflict",
5402 ldb_dn_get_linearized(conflict_dn));
5407 * first we need the replPropertyMetaData attribute from the
5410 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5412 DSDB_FLAG_NEXT_MODULE |
5413 DSDB_SEARCH_SHOW_DELETED |
5414 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5415 if (ret != LDB_SUCCESS) {
5416 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5417 ldb_dn_get_linearized(conflict_dn)));
5421 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5422 if (omd_value == NULL) {
5423 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5424 ldb_dn_get_linearized(conflict_dn)));
5428 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5429 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5430 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5431 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5432 ldb_dn_get_linearized(conflict_dn)));
5436 rmd = ar->objs->objects[ar->index_current].meta_data;
5439 * we decide which is newer based on the RPMD on the name
5440 * attribute. See [MS-DRSR] ResolveNameConflict.
5442 * We expect omd_name to be present, as this is from a local
5443 * search, but while rmd_name should have been given to us by
5444 * the remote server, if it is missing we just prefer the
5446 * replmd_replPropertyMetaData1_new_should_be_taken()
5448 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5449 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5451 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5452 ldb_dn_get_linearized(conflict_dn)));
5457 * Should we preserve the current record, and so rename the
5458 * incoming record to be a conflict?
5460 rename_incoming_record =
5461 !replmd_replPropertyMetaData1_new_should_be_taken(
5462 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5463 omd_name, rmd_name);
5465 if (rename_incoming_record) {
5467 new_dn = replmd_conflict_dn(msg, msg->dn,
5468 &ar->objs->objects[ar->index_current].object_guid);
5469 if (new_dn == NULL) {
5470 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5471 "Failed to form conflict DN for %s\n",
5472 ldb_dn_get_linearized(msg->dn));
5474 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5477 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5478 DSDB_FLAG_NEXT_MODULE, ar->req);
5479 if (ret != LDB_SUCCESS) {
5480 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5481 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5482 ldb_dn_get_linearized(conflict_dn),
5483 ldb_dn_get_linearized(ar->search_msg->dn),
5484 ldb_dn_get_linearized(new_dn),
5485 ldb_errstring(ldb_module_get_ctx(ar->module)));
5486 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5494 /* we are renaming the existing record */
5496 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5497 if (GUID_all_zero(&guid)) {
5498 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5499 ldb_dn_get_linearized(conflict_dn)));
5503 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
5504 if (new_dn == NULL) {
5505 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5506 ldb_dn_get_linearized(conflict_dn)));
5510 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5511 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5513 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5514 DSDB_FLAG_OWN_MODULE, ar->req);
5515 if (ret != LDB_SUCCESS) {
5516 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5517 ldb_dn_get_linearized(conflict_dn),
5518 ldb_dn_get_linearized(new_dn),
5519 ldb_errstring(ldb_module_get_ctx(ar->module))));
5524 * now we need to ensure that the rename is seen as an
5525 * originating update. We do that with a modify.
5527 ret = replmd_name_modify(ar, ar->req, new_dn);
5528 if (ret != LDB_SUCCESS) {
5532 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5533 ldb_dn_get_linearized(ar->search_msg->dn),
5534 ldb_dn_get_linearized(msg->dn)));
5537 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5538 DSDB_FLAG_NEXT_MODULE, ar->req);
5539 if (ret != LDB_SUCCESS) {
5540 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5541 ldb_dn_get_linearized(ar->search_msg->dn),
5542 ldb_dn_get_linearized(msg->dn),
5543 ldb_errstring(ldb_module_get_ctx(ar->module))));
5549 * On failure make the caller get the error
5550 * This means replication will stop with an error,
5551 * but there is not much else we can do. In the
5552 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5556 talloc_free(tmp_ctx);
5561 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5563 struct ldb_context *ldb;
5564 struct ldb_request *change_req;
5565 enum ndr_err_code ndr_err;
5566 struct ldb_message *msg;
5567 struct replPropertyMetaDataBlob *rmd;
5568 struct replPropertyMetaDataBlob omd;
5569 const struct ldb_val *omd_value;
5570 struct replPropertyMetaDataBlob nmd;
5571 struct ldb_val nmd_value;
5572 struct GUID remote_parent_guid;
5575 unsigned int removed_attrs = 0;
5577 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5578 bool isDeleted = false;
5579 bool local_isDeleted = false;
5580 bool remote_isDeleted = false;
5581 bool take_remote_isDeleted = false;
5582 bool sd_updated = false;
5583 bool renamed = false;
5584 bool is_schema_nc = false;
5586 const struct ldb_val *old_rdn, *new_rdn;
5587 struct replmd_private *replmd_private =
5588 talloc_get_type(ldb_module_get_private(ar->module),
5589 struct replmd_private);
5591 time_t t = time(NULL);
5592 unix_to_nt_time(&now, t);
5594 ldb = ldb_module_get_ctx(ar->module);
5595 msg = ar->objs->objects[ar->index_current].msg;
5597 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5599 rmd = ar->objs->objects[ar->index_current].meta_data;
5603 /* find existing meta data */
5604 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5606 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5607 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5608 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5609 nt_status = ndr_map_error2ntstatus(ndr_err);
5610 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5613 if (omd.version != 1) {
5614 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5619 struct GUID_txt_buf guid_txt;
5621 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5622 LDB_CHANGETYPE_MODIFY, msg);
5623 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5626 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5628 ndr_print_struct_string(s,
5629 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5630 "existing replPropertyMetaData",
5632 ndr_print_struct_string(s,
5633 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5634 "incoming replPropertyMetaData",
5637 } else if (DEBUGLVL(4)) {
5638 struct GUID_txt_buf guid_txt;
5640 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5641 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5643 ldb_dn_get_linearized(msg->dn)));
5646 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5647 "isDeleted", false);
5648 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5649 "isDeleted", false);
5652 * Fill in the remote_parent_guid with the GUID or an all-zero
5655 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5656 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5658 remote_parent_guid = GUID_zero();
5662 * To ensure we follow a complex rename chain around, we have
5663 * to confirm that the DN is the same (mostly to confirm the
5664 * RDN) and the parentGUID is the same.
5666 * This ensures we keep things under the correct parent, which
5667 * replmd_replicated_handle_rename() will do.
5670 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5671 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5675 * handle renames, even just by case that come in over
5676 * DRS. Changes in the parent DN don't hit us here,
5677 * because the search for a parent will clean up those
5680 * We also have already filtered out the case where
5681 * the peer has an older name to what we have (see
5682 * replmd_replicated_apply_search_callback())
5684 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5687 if (ret != LDB_SUCCESS) {
5688 ldb_debug(ldb, LDB_DEBUG_FATAL,
5689 "replmd_replicated_request rename %s => %s failed - %s\n",
5690 ldb_dn_get_linearized(ar->search_msg->dn),
5691 ldb_dn_get_linearized(msg->dn),
5692 ldb_errstring(ldb));
5693 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5696 if (renamed == true) {
5698 * Set the callback to one that will fix up the name
5699 * metadata on the new conflict DN
5701 callback = replmd_op_name_modify_callback;
5706 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5707 nmd.ctr.ctr1.array = talloc_array(ar,
5708 struct replPropertyMetaData1,
5709 nmd.ctr.ctr1.count);
5710 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5712 /* first copy the old meta data */
5713 for (i=0; i < omd.ctr.ctr1.count; i++) {
5714 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5719 /* now merge in the new meta data */
5720 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5723 for (j=0; j < ni; j++) {
5726 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5730 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5731 ar->objs->dsdb_repl_flags,
5732 &nmd.ctr.ctr1.array[j],
5733 &rmd->ctr.ctr1.array[i]);
5735 /* replace the entry */
5736 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5737 if (ar->seq_num == 0) {
5738 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5739 if (ret != LDB_SUCCESS) {
5740 return replmd_replicated_request_error(ar, ret);
5743 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5744 switch (nmd.ctr.ctr1.array[j].attid) {
5745 case DRSUAPI_ATTID_ntSecurityDescriptor:
5748 case DRSUAPI_ATTID_isDeleted:
5749 take_remote_isDeleted = true;
5758 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5759 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5760 msg->elements[i-removed_attrs].name,
5761 ldb_dn_get_linearized(msg->dn),
5762 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5765 /* we don't want to apply this change so remove the attribute */
5766 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5773 if (found) continue;
5775 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5776 if (ar->seq_num == 0) {
5777 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5778 if (ret != LDB_SUCCESS) {
5779 return replmd_replicated_request_error(ar, ret);
5782 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5783 switch (nmd.ctr.ctr1.array[ni].attid) {
5784 case DRSUAPI_ATTID_ntSecurityDescriptor:
5787 case DRSUAPI_ATTID_isDeleted:
5788 take_remote_isDeleted = true;
5797 * finally correct the size of the meta_data array
5799 nmd.ctr.ctr1.count = ni;
5801 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5802 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5805 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5806 &nmd, ar, now, is_schema_nc,
5808 if (ret != LDB_SUCCESS) {
5809 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5810 return replmd_replicated_request_error(ar, ret);
5814 * sort the new meta data array
5816 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5817 if (ret != LDB_SUCCESS) {
5818 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5823 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5826 * This also controls SD propagation below
5828 if (take_remote_isDeleted) {
5829 isDeleted = remote_isDeleted;
5831 isDeleted = local_isDeleted;
5834 ar->isDeleted = isDeleted;
5837 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5839 if (msg->num_elements == 0) {
5840 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5843 return replmd_replicated_apply_isDeleted(ar);
5846 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5847 ar->index_current, msg->num_elements);
5853 if (sd_updated && !isDeleted) {
5854 ret = dsdb_module_schedule_sd_propagation(ar->module,
5855 ar->objs->partition_dn,
5857 if (ret != LDB_SUCCESS) {
5858 return ldb_operr(ldb);
5862 /* create the meta data value */
5863 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5864 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5865 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5866 nt_status = ndr_map_error2ntstatus(ndr_err);
5867 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5871 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5872 * and replPopertyMetaData attributes
5874 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5875 if (ret != LDB_SUCCESS) {
5876 return replmd_replicated_request_error(ar, ret);
5878 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5879 if (ret != LDB_SUCCESS) {
5880 return replmd_replicated_request_error(ar, ret);
5882 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5883 if (ret != LDB_SUCCESS) {
5884 return replmd_replicated_request_error(ar, ret);
5887 replmd_ldb_message_sort(msg, ar->schema);
5889 /* we want to replace the old values */
5890 for (i=0; i < msg->num_elements; i++) {
5891 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5892 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5893 if (msg->elements[i].num_values == 0) {
5894 ldb_asprintf_errstring(ldb, __location__
5895 ": objectClass removed on %s, aborting replication\n",
5896 ldb_dn_get_linearized(msg->dn));
5897 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5903 struct GUID_txt_buf guid_txt;
5905 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5906 LDB_CHANGETYPE_MODIFY,
5908 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
5909 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5913 } else if (DEBUGLVL(4)) {
5914 struct GUID_txt_buf guid_txt;
5916 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
5917 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5919 ldb_dn_get_linearized(msg->dn)));
5922 ret = ldb_build_mod_req(&change_req,
5930 LDB_REQ_SET_LOCATION(change_req);
5931 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5933 /* current partition control needed by "repmd_op_callback" */
5934 ret = ldb_request_add_control(change_req,
5935 DSDB_CONTROL_CURRENT_PARTITION_OID,
5937 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5939 return ldb_next_request(ar->module, change_req);
5942 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5943 struct ldb_reply *ares)
5945 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5946 struct replmd_replicated_request);
5950 return ldb_module_done(ar->req, NULL, NULL,
5951 LDB_ERR_OPERATIONS_ERROR);
5953 if (ares->error != LDB_SUCCESS &&
5954 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5955 return ldb_module_done(ar->req, ares->controls,
5956 ares->response, ares->error);
5959 switch (ares->type) {
5960 case LDB_REPLY_ENTRY:
5961 ar->search_msg = talloc_steal(ar, ares->message);
5964 case LDB_REPLY_REFERRAL:
5965 /* we ignore referrals */
5968 case LDB_REPLY_DONE:
5970 struct replPropertyMetaData1 *md_remote;
5971 struct replPropertyMetaData1 *md_local;
5973 struct replPropertyMetaDataBlob omd;
5974 const struct ldb_val *omd_value;
5975 struct replPropertyMetaDataBlob *rmd;
5976 struct ldb_message *msg;
5978 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5979 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5982 * This is the ADD case, find the appropriate parent,
5983 * as this object doesn't exist locally:
5985 if (ar->search_msg == NULL) {
5986 ret = replmd_replicated_apply_search_for_parent(ar);
5987 if (ret != LDB_SUCCESS) {
5988 return ldb_module_done(ar->req, NULL, NULL, ret);
5995 * Otherwise, in the MERGE case, work out if we are
5996 * attempting a rename, and if so find the parent the
5997 * newly renamed object wants to belong under (which
5998 * may not be the parent in it's attached string DN
6000 rmd = ar->objs->objects[ar->index_current].meta_data;
6004 /* find existing meta data */
6005 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6007 enum ndr_err_code ndr_err;
6008 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6009 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6010 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6011 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6012 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6015 if (omd.version != 1) {
6016 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6020 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6022 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6023 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6024 && GUID_all_zero(&ar->local_parent_guid)) {
6025 DEBUG(0, ("Refusing to replicate new version of %s "
6026 "as local object has an all-zero parentGUID attribute, "
6027 "despite not being an NC root\n",
6028 ldb_dn_get_linearized(ar->search_msg->dn)));
6029 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6033 * now we need to check for double renames. We could have a
6034 * local rename pending which our replication partner hasn't
6035 * received yet. We choose which one wins by looking at the
6036 * attribute stamps on the two objects, the newer one wins.
6038 * This also simply applies the correct algorithms for
6039 * determining if a change was made to name at all, or
6040 * if the object has just been renamed under the same
6043 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6044 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6046 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6047 ldb_dn_get_linearized(ar->search_msg->dn)));
6048 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6052 * if there is no name attribute given then we have to assume the
6053 * object we've received has the older name
6055 if (replmd_replPropertyMetaData1_new_should_be_taken(
6056 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6057 md_local, md_remote)) {
6058 struct GUID_txt_buf p_guid_local;
6059 struct GUID_txt_buf p_guid_remote;
6060 msg = ar->objs->objects[ar->index_current].msg;
6062 /* Merge on the existing object, with rename */
6064 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6065 "as incoming object changing to %s under %s\n",
6066 ldb_dn_get_linearized(ar->search_msg->dn),
6067 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6068 ldb_dn_get_linearized(msg->dn),
6069 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6071 ret = replmd_replicated_apply_search_for_parent(ar);
6073 struct GUID_txt_buf p_guid_local;
6074 struct GUID_txt_buf p_guid_remote;
6075 msg = ar->objs->objects[ar->index_current].msg;
6078 * Merge on the existing object, force no
6079 * rename (code below just to explain why in
6083 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6084 ldb_dn_get_linearized(msg->dn)) == 0) {
6085 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6086 GUID_equal(&ar->local_parent_guid,
6087 ar->objs->objects[ar->index_current].parent_guid)
6089 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6090 "despite incoming object changing parent to %s\n",
6091 ldb_dn_get_linearized(ar->search_msg->dn),
6092 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6093 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6097 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6098 " and rejecting older rename to %s under %s\n",
6099 ldb_dn_get_linearized(ar->search_msg->dn),
6100 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6101 ldb_dn_get_linearized(msg->dn),
6102 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6106 * This assignment ensures that the strcmp()
6107 * and GUID_equal() calls in
6108 * replmd_replicated_apply_merge() avoids the
6111 ar->objs->objects[ar->index_current].parent_guid =
6112 &ar->local_parent_guid;
6114 msg->dn = ar->search_msg->dn;
6115 ret = replmd_replicated_apply_merge(ar);
6117 if (ret != LDB_SUCCESS) {
6118 return ldb_module_done(ar->req, NULL, NULL, ret);
6128 * Stores the linked attributes received in the replication chunk - these get
6129 * applied at the end of the transaction. We also check that each linked
6130 * attribute is valid, i.e. source and target objects are known.
6132 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6134 int ret = LDB_SUCCESS;
6136 struct ldb_module *module = ar->module;
6137 struct replmd_private *replmd_private =
6138 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6139 struct ldb_context *ldb;
6141 ldb = ldb_module_get_ctx(module);
6143 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6145 /* save away the linked attributes for the end of the transaction */
6146 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6147 struct la_entry *la_entry;
6149 if (replmd_private->la_ctx == NULL) {
6150 replmd_private->la_ctx = talloc_new(replmd_private);
6152 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6153 if (la_entry == NULL) {
6155 return LDB_ERR_OPERATIONS_ERROR;
6157 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6158 if (la_entry->la == NULL) {
6159 talloc_free(la_entry);
6161 return LDB_ERR_OPERATIONS_ERROR;
6163 *la_entry->la = ar->objs->linked_attributes[i];
6164 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6166 /* we need to steal the non-scalars so they stay
6167 around until the end of the transaction */
6168 talloc_steal(la_entry->la, la_entry->la->identifier);
6169 talloc_steal(la_entry->la, la_entry->la->value.blob);
6171 ret = replmd_verify_linked_attribute(ar, la_entry);
6173 if (ret != LDB_SUCCESS) {
6177 DLIST_ADD(replmd_private->la_list, la_entry);
6183 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6185 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6187 struct ldb_context *ldb;
6191 struct ldb_request *search_req;
6192 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6193 "parentGUID", "instanceType",
6194 "replPropertyMetaData", "nTSecurityDescriptor",
6195 "isDeleted", NULL };
6196 struct GUID_txt_buf guid_str_buf;
6198 if (ar->index_current >= ar->objs->num_objects) {
6201 * Now that we've applied all the objects, check the new linked
6202 * attributes and store them (we apply them in .prepare_commit)
6204 ret = replmd_store_linked_attributes(ar);
6206 if (ret != LDB_SUCCESS) {
6210 /* done applying objects, move on to the next stage */
6211 return replmd_replicated_uptodate_vector(ar);
6214 ldb = ldb_module_get_ctx(ar->module);
6215 ar->search_msg = NULL;
6216 ar->isDeleted = false;
6218 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6221 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6222 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6224 ret = ldb_build_search_req(&search_req,
6227 ar->objs->partition_dn,
6233 replmd_replicated_apply_search_callback,
6235 LDB_REQ_SET_LOCATION(search_req);
6237 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6239 if (ret != LDB_SUCCESS) {
6243 return ldb_next_request(ar->module, search_req);
6247 * This is essentially a wrapper for replmd_replicated_apply_next()
6249 * This is needed to ensure that both codepaths call this handler.
6251 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6253 struct ldb_dn *deleted_objects_dn;
6254 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6255 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6256 &deleted_objects_dn);
6257 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6259 * Do a delete here again, so that if there is
6260 * anything local that conflicts with this
6261 * object being deleted, it is removed. This
6262 * includes links. See MS-DRSR 4.1.10.6.9
6265 * If the object is already deleted, and there
6266 * is no more work required, it doesn't do
6270 /* This has been updated to point to the DN we eventually did the modify on */
6272 struct ldb_request *del_req;
6273 struct ldb_result *res;
6275 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6277 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6281 res = talloc_zero(tmp_ctx, struct ldb_result);
6283 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6284 talloc_free(tmp_ctx);
6288 /* Build a delete request, which hopefully will artually turn into nothing */
6289 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6293 ldb_modify_default_callback,
6295 LDB_REQ_SET_LOCATION(del_req);
6296 if (ret != LDB_SUCCESS) {
6297 talloc_free(tmp_ctx);
6302 * This is the guts of the call, call back
6303 * into our delete code, but setting the
6304 * re_delete flag so we delete anything that
6305 * shouldn't be there on a deleted or recycled
6308 ret = replmd_delete_internals(ar->module, del_req, true);
6309 if (ret == LDB_SUCCESS) {
6310 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6313 talloc_free(tmp_ctx);
6314 if (ret != LDB_SUCCESS) {
6319 ar->index_current++;
6320 return replmd_replicated_apply_next(ar);
6323 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6324 struct ldb_reply *ares)
6326 struct ldb_context *ldb;
6327 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6328 struct replmd_replicated_request);
6329 ldb = ldb_module_get_ctx(ar->module);
6332 return ldb_module_done(ar->req, NULL, NULL,
6333 LDB_ERR_OPERATIONS_ERROR);
6335 if (ares->error != LDB_SUCCESS) {
6336 return ldb_module_done(ar->req, ares->controls,
6337 ares->response, ares->error);
6340 if (ares->type != LDB_REPLY_DONE) {
6341 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6342 return ldb_module_done(ar->req, NULL, NULL,
6343 LDB_ERR_OPERATIONS_ERROR);
6348 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6351 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6353 struct ldb_context *ldb;
6354 struct ldb_request *change_req;
6355 enum ndr_err_code ndr_err;
6356 struct ldb_message *msg;
6357 struct replUpToDateVectorBlob ouv;
6358 const struct ldb_val *ouv_value;
6359 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6360 struct replUpToDateVectorBlob nuv;
6361 struct ldb_val nuv_value;
6362 struct ldb_message_element *nuv_el = NULL;
6363 struct ldb_message_element *orf_el = NULL;
6364 struct repsFromToBlob nrf;
6365 struct ldb_val *nrf_value = NULL;
6366 struct ldb_message_element *nrf_el = NULL;
6370 time_t t = time(NULL);
6373 uint32_t instanceType;
6375 ldb = ldb_module_get_ctx(ar->module);
6376 ruv = ar->objs->uptodateness_vector;
6382 unix_to_nt_time(&now, t);
6384 if (ar->search_msg == NULL) {
6385 /* this happens for a REPL_OBJ call where we are
6386 creating the target object by replicating it. The
6387 subdomain join code does this for the partition DN
6389 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6390 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6393 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6394 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6395 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6396 ldb_dn_get_linearized(ar->search_msg->dn)));
6397 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6401 * first create the new replUpToDateVector
6403 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6405 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6406 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6407 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6408 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6409 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6412 if (ouv.version != 2) {
6413 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6418 * the new uptodateness vector will at least
6419 * contain 1 entry, one for the source_dsa
6421 * plus optional values from our old vector and the one from the source_dsa
6423 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6424 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6425 nuv.ctr.ctr2.cursors = talloc_array(ar,
6426 struct drsuapi_DsReplicaCursor2,
6427 nuv.ctr.ctr2.count);
6428 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6430 /* first copy the old vector */
6431 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6432 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6436 /* merge in the source_dsa vector is available */
6437 for (i=0; (ruv && i < ruv->count); i++) {
6440 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6441 &ar->our_invocation_id)) {
6445 for (j=0; j < ni; j++) {
6446 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6447 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6453 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6454 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6459 if (found) continue;
6461 /* if it's not there yet, add it */
6462 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6467 * finally correct the size of the cursors array
6469 nuv.ctr.ctr2.count = ni;
6474 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6477 * create the change ldb_message
6479 msg = ldb_msg_new(ar);
6480 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6481 msg->dn = ar->search_msg->dn;
6483 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6484 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6485 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6486 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6487 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6489 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6490 if (ret != LDB_SUCCESS) {
6491 return replmd_replicated_request_error(ar, ret);
6493 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6496 * now create the new repsFrom value from the given repsFromTo1 structure
6500 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6501 nrf.ctr.ctr1.last_attempt = now;
6502 nrf.ctr.ctr1.last_success = now;
6503 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6506 * first see if we already have a repsFrom value for the current source dsa
6507 * if so we'll later replace this value
6509 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6511 for (i=0; i < orf_el->num_values; i++) {
6512 struct repsFromToBlob *trf;
6514 trf = talloc(ar, struct repsFromToBlob);
6515 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6517 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6518 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6519 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6520 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6521 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6524 if (trf->version != 1) {
6525 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6529 * we compare the source dsa objectGUID not the invocation_id
6530 * because we want only one repsFrom value per source dsa
6531 * and when the invocation_id of the source dsa has changed we don't need
6532 * the old repsFrom with the old invocation_id
6534 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6535 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6541 nrf_value = &orf_el->values[i];
6546 * copy over all old values to the new ldb_message
6548 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6549 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6554 * if we haven't found an old repsFrom value for the current source dsa
6555 * we'll add a new value
6558 struct ldb_val zero_value;
6559 ZERO_STRUCT(zero_value);
6560 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6561 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6563 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6566 /* we now fill the value which is already attached to ldb_message */
6567 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6569 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6570 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6571 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6572 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6576 * the ldb_message_element for the attribute, has all the old values and the new one
6577 * so we'll replace the whole attribute with all values
6579 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6581 if (CHECK_DEBUGLVL(4)) {
6582 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6583 LDB_CHANGETYPE_MODIFY,
6585 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6589 /* prepare the ldb_modify() request */
6590 ret = ldb_build_mod_req(&change_req,
6596 replmd_replicated_uptodate_modify_callback,
6598 LDB_REQ_SET_LOCATION(change_req);
6599 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6601 return ldb_next_request(ar->module, change_req);
6604 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6605 struct ldb_reply *ares)
6607 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6608 struct replmd_replicated_request);
6612 return ldb_module_done(ar->req, NULL, NULL,
6613 LDB_ERR_OPERATIONS_ERROR);
6615 if (ares->error != LDB_SUCCESS &&
6616 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6617 return ldb_module_done(ar->req, ares->controls,
6618 ares->response, ares->error);
6621 switch (ares->type) {
6622 case LDB_REPLY_ENTRY:
6623 ar->search_msg = talloc_steal(ar, ares->message);
6626 case LDB_REPLY_REFERRAL:
6627 /* we ignore referrals */
6630 case LDB_REPLY_DONE:
6631 ret = replmd_replicated_uptodate_modify(ar);
6632 if (ret != LDB_SUCCESS) {
6633 return ldb_module_done(ar->req, NULL, NULL, ret);
6642 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6644 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6645 struct replmd_private *replmd_private =
6646 talloc_get_type_abort(ldb_module_get_private(ar->module),
6647 struct replmd_private);
6649 static const char *attrs[] = {
6650 "replUpToDateVector",
6655 struct ldb_request *search_req;
6657 ar->search_msg = NULL;
6660 * Let the caller know that we did an originating updates
6662 ar->objs->originating_updates = replmd_private->originating_updates;
6664 ret = ldb_build_search_req(&search_req,
6667 ar->objs->partition_dn,
6673 replmd_replicated_uptodate_search_callback,
6675 LDB_REQ_SET_LOCATION(search_req);
6676 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6678 return ldb_next_request(ar->module, search_req);
6683 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6685 struct ldb_context *ldb;
6686 struct dsdb_extended_replicated_objects *objs;
6687 struct replmd_replicated_request *ar;
6688 struct ldb_control **ctrls;
6691 ldb = ldb_module_get_ctx(module);
6693 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6695 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6697 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6698 return LDB_ERR_PROTOCOL_ERROR;
6701 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6702 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6703 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6704 return LDB_ERR_PROTOCOL_ERROR;
6707 ar = replmd_ctx_init(module, req);
6709 return LDB_ERR_OPERATIONS_ERROR;
6711 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6712 ar->apply_mode = true;
6714 ar->schema = dsdb_get_schema(ldb, ar);
6716 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6718 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6719 return LDB_ERR_CONSTRAINT_VIOLATION;
6722 ctrls = req->controls;
6724 if (req->controls) {
6725 req->controls = talloc_memdup(ar, req->controls,
6726 talloc_get_size(req->controls));
6727 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6730 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6731 if (ret != LDB_SUCCESS) {
6735 /* If this change contained linked attributes in the body
6736 * (rather than in the links section) we need to update
6737 * backlinks in linked_attributes */
6738 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6739 if (ret != LDB_SUCCESS) {
6743 ar->controls = req->controls;
6744 req->controls = ctrls;
6746 return replmd_replicated_apply_next(ar);
6750 * Checks how to handle an missing target - either we need to fail the
6751 * replication and retry with GET_TGT, ignore the link and continue, or try to
6752 * add a partial link to an unknown target.
6754 static int replmd_allow_missing_target(struct ldb_module *module,
6755 TALLOC_CTX *mem_ctx,
6756 struct ldb_dn *target_dn,
6757 struct ldb_dn *source_dn,
6760 uint32_t dsdb_repl_flags,
6762 const char * missing_str)
6764 struct ldb_context *ldb = ldb_module_get_ctx(module);
6768 * we may not be able to resolve link targets properly when
6769 * dealing with subsets of objects, e.g. the source is a
6770 * critical object and the target isn't
6773 * When we implement Trusted Domains we need to consider
6774 * whether they get treated as an incomplete replica here or not
6776 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
6779 * Ignore the link. We don't increase the highwater-mark in
6780 * the object subset cases, so subsequent replications should
6781 * resolve any missing links
6783 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
6784 ldb_dn_get_linearized(target_dn),
6785 ldb_dn_get_linearized(source_dn)));
6786 *ignore_link = true;
6790 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
6793 * target should already be up-to-date so there's no point in
6794 * retrying. This could be due to bad timing, or if a target
6795 * on a one-way link was deleted. We ignore the link rather
6796 * than failing the replication cycle completely
6798 *ignore_link = true;
6799 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
6800 ldb_dn_get_linearized(target_dn), missing_str,
6801 ldb_dn_get_linearized(source_dn));
6805 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
6809 if (is_in_same_nc) {
6810 /* fail the replication and retry with GET_TGT */
6811 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
6813 ldb_dn_get_linearized(target_dn),
6814 GUID_string(mem_ctx, guid),
6815 ldb_dn_get_linearized(source_dn));
6816 return LDB_ERR_NO_SUCH_OBJECT;
6820 * The target of the cross-partition link is missing. Continue
6821 * and try to at least add the forward-link. This isn't great,
6822 * but a partial link can be fixed by dbcheck, so it's better
6823 * than dropping the link completely.
6825 *ignore_link = false;
6827 if (is_obj_commit) {
6830 * Only log this when we're actually committing the objects.
6831 * This avoids spurious logs, i.e. if we're just verifying the
6832 * received link during a join.
6834 DBG_WARNING("%s cross-partition target %s linked from %s\n",
6835 missing_str, ldb_dn_get_linearized(target_dn),
6836 ldb_dn_get_linearized(source_dn));
6843 * Checks that the target object for a linked attribute exists.
6844 * @param guid returns the target object's GUID (is returned)if it exists)
6845 * @param ignore_link set to true if the linked attribute should be ignored
6846 * (i.e. the target doesn't exist, but that it's OK to skip the link)
6848 static int replmd_check_target_exists(struct ldb_module *module,
6849 struct dsdb_dn *dsdb_dn,
6850 struct la_entry *la_entry,
6851 struct ldb_dn *source_dn,
6856 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6857 struct ldb_context *ldb = ldb_module_get_ctx(module);
6858 struct ldb_result *target_res;
6859 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6860 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
6863 enum deletion_state target_deletion_state = OBJECT_REMOVED;
6864 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
6866 *ignore_link = false;
6867 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
6869 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6872 * This strange behaviour (allowing a NULL/missing
6873 * GUID) originally comes from:
6875 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6876 * Author: Andrew Tridgell <tridge@samba.org>
6877 * Date: Mon Dec 21 21:21:55 2009 +1100
6879 * s4-drs: cope better with NULL GUIDS from DRS
6881 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6882 * need to match by DN if possible when seeing if we should update an
6885 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6887 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6889 DSDB_FLAG_NEXT_MODULE |
6890 DSDB_SEARCH_SHOW_RECYCLED |
6891 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6892 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6894 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6895 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
6897 ldb_dn_get_linearized(dsdb_dn->dn),
6898 ldb_dn_get_linearized(source_dn));
6899 talloc_free(tmp_ctx);
6900 return LDB_ERR_OPERATIONS_ERROR;
6902 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6903 NULL, LDB_SCOPE_SUBTREE,
6905 DSDB_FLAG_NEXT_MODULE |
6906 DSDB_SEARCH_SHOW_RECYCLED |
6907 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6908 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6911 GUID_string(tmp_ctx, guid));
6914 if (ret != LDB_SUCCESS) {
6915 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
6916 GUID_string(tmp_ctx, guid),
6917 ldb_errstring(ldb));
6918 talloc_free(tmp_ctx);
6922 if (target_res->count == 0) {
6925 * target object is unknown. Check whether to ignore the link,
6926 * fail the replication, or add a partial link
6928 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
6929 source_dn, is_obj_commit, guid,
6930 la_entry->dsdb_repl_flags,
6931 ignore_link, "Unknown");
6933 } else if (target_res->count != 1) {
6934 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
6935 GUID_string(tmp_ctx, guid));
6936 ret = LDB_ERR_OPERATIONS_ERROR;
6938 struct ldb_message *target_msg = target_res->msgs[0];
6940 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6942 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
6943 replmd_deletion_state(module, target_msg,
6944 &target_deletion_state, NULL);
6947 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
6948 * ProcessLinkValue(). Link updates should not be sent for
6949 * recycled and tombstone objects (deleting the links should
6950 * happen when we delete the object). This probably means our
6951 * copy of the target object isn't up to date.
6953 if (target_deletion_state >= OBJECT_RECYCLED) {
6956 * target object is deleted. Check whether to ignore the
6957 * link, fail the replication, or add a partial link
6959 ret = replmd_allow_missing_target(module, tmp_ctx,
6960 dsdb_dn->dn, source_dn,
6961 is_obj_commit, guid,
6962 la_entry->dsdb_repl_flags,
6963 ignore_link, "Deleted");
6967 talloc_free(tmp_ctx);
6972 * Extracts the key details about the source/target object for a
6973 * linked-attribute entry.
6974 * This returns the following details:
6975 * @param ret_attr the schema details for the linked attribute
6976 * @param source_msg the search result for the source object
6977 * @param target_dsdb_dn the unpacked DN info for the target object
6979 static int replmd_extract_la_entry_details(struct ldb_module *module,
6980 struct la_entry *la_entry,
6981 TALLOC_CTX *mem_ctx,
6982 const struct dsdb_attribute **ret_attr,
6983 struct ldb_message **source_msg,
6984 struct dsdb_dn **target_dsdb_dn)
6986 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6987 struct ldb_context *ldb = ldb_module_get_ctx(module);
6988 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
6990 const struct dsdb_attribute *attr;
6992 struct ldb_result *res;
6993 const char *attrs[4];
6996 linked_attributes[0]:
6997 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6999 identifier: struct drsuapi_DsReplicaObjectIdentifier
7000 __ndr_size : 0x0000003a (58)
7001 __ndr_size_sid : 0x00000000 (0)
7002 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7004 __ndr_size_dn : 0x00000000 (0)
7006 attid : DRSUAPI_ATTID_member (0x1F)
7007 value: struct drsuapi_DsAttributeValue
7008 __ndr_size : 0x0000007e (126)
7010 blob : DATA_BLOB length=126
7011 flags : 0x00000001 (1)
7012 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7013 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7014 meta_data: struct drsuapi_DsReplicaMetaData
7015 version : 0x00000015 (21)
7016 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7017 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7018 originating_usn : 0x000000000001e19c (123292)
7020 (for cases where the link is to a normal DN)
7021 &target: struct drsuapi_DsReplicaObjectIdentifier3
7022 __ndr_size : 0x0000007e (126)
7023 __ndr_size_sid : 0x0000001c (28)
7024 guid : 7639e594-db75-4086-b0d4-67890ae46031
7025 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7026 __ndr_size_dn : 0x00000022 (34)
7027 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7030 /* find the attribute being modified */
7031 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7033 struct GUID_txt_buf guid_str;
7034 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7036 GUID_buf_string(&la->identifier->guid,
7038 return LDB_ERR_OPERATIONS_ERROR;
7041 attrs[0] = attr->lDAPDisplayName;
7042 attrs[1] = "isDeleted";
7043 attrs[2] = "isRecycled";
7047 * get the existing message from the db for the object with
7048 * this GUID, returning attribute being modified. We will then
7049 * use this msg as the basis for a modify call
7051 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7052 DSDB_FLAG_NEXT_MODULE |
7053 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7054 DSDB_SEARCH_SHOW_RECYCLED |
7055 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7056 DSDB_SEARCH_REVEAL_INTERNALS,
7058 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7059 if (ret != LDB_SUCCESS) {
7062 if (res->count != 1) {
7063 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7064 GUID_string(mem_ctx, &la->identifier->guid));
7065 return LDB_ERR_NO_SUCH_OBJECT;
7068 *source_msg = res->msgs[0];
7070 /* the value blob for the attribute holds the target object DN */
7071 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7072 if (!W_ERROR_IS_OK(status)) {
7073 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7074 attr->lDAPDisplayName,
7075 ldb_dn_get_linearized(res->msgs[0]->dn),
7076 win_errstr(status));
7077 return LDB_ERR_OPERATIONS_ERROR;
7086 * Verifies the source and target objects are known for a linked attribute
7088 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7089 struct la_entry *la)
7091 int ret = LDB_SUCCESS;
7092 TALLOC_CTX *tmp_ctx = talloc_new(la);
7093 struct ldb_module *module = ar->module;
7094 struct ldb_message *src_msg;
7095 const struct dsdb_attribute *attr;
7096 struct dsdb_dn *tgt_dsdb_dn;
7097 struct GUID guid = GUID_zero();
7100 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7101 &src_msg, &tgt_dsdb_dn);
7104 * When we fail to find the source object, the error code we pass
7105 * back here is really important. It flags back to the callers to
7106 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7107 * never happen if we're replicating from a Samba DC, but it is
7108 * needed to talk to a Windows DC
7110 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7111 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7114 if (ret != LDB_SUCCESS) {
7115 talloc_free(tmp_ctx);
7120 * We can skip the target object checks if we're only syncing critical
7121 * objects, or we know the target is up-to-date. If either case, we
7122 * still continue even if the target doesn't exist
7124 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7125 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7127 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7128 src_msg->dn, false, &guid,
7133 * When we fail to find the target object, the error code we pass
7134 * back here is really important. It flags back to the callers to
7135 * retry this request with DRSUAPI_DRS_GET_TGT
7137 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7138 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7141 talloc_free(tmp_ctx);
7146 * Finds the current active Parsed-DN value for a single-valued linked
7147 * attribute, if one exists.
7148 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7149 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7152 static int replmd_get_active_singleval_link(struct ldb_module *module,
7153 TALLOC_CTX *mem_ctx,
7154 struct parsed_dn pdn_list[],
7156 const struct dsdb_attribute *attr,
7157 struct parsed_dn **ret_pdn)
7163 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7165 /* nothing to do for multi-valued linked attributes */
7169 for (i = 0; i < count; i++) {
7170 int ret = LDB_SUCCESS;
7171 struct parsed_dn *pdn = &pdn_list[i];
7173 /* skip any inactive links */
7174 if (dsdb_dn_is_deleted_val(pdn->v)) {
7178 /* we've found an active value for this attribute */
7181 if (pdn->dsdb_dn == NULL) {
7182 struct ldb_context *ldb = ldb_module_get_ctx(module);
7184 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7185 attr->syntax->ldap_oid);
7191 /* no active link found */
7196 * @returns true if the replication linked attribute info is newer than we
7197 * already have in our DB
7198 * @param pdn the existing linked attribute info in our DB
7199 * @param la the new linked attribute info received during replication
7201 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7202 struct drsuapi_DsReplicaLinkedAttribute *la)
7204 /* see if this update is newer than what we have already */
7205 struct GUID invocation_id = GUID_zero();
7206 uint32_t version = 0;
7207 NTTIME change_time = 0;
7211 /* no existing info so update is newer */
7215 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7216 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7217 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7219 return replmd_update_is_newer(&invocation_id,
7220 &la->meta_data.originating_invocation_id,
7222 la->meta_data.version,
7224 la->meta_data.originating_change_time);
7228 * Marks an existing linked attribute value as deleted in the DB
7229 * @param pdn the parsed-DN of the target-value to delete
7231 static int replmd_delete_link_value(struct ldb_module *module,
7232 struct replmd_private *replmd_private,
7233 TALLOC_CTX *mem_ctx,
7234 struct ldb_dn *src_obj_dn,
7235 const struct dsdb_schema *schema,
7236 const struct dsdb_attribute *attr,
7239 struct GUID *target_guid,
7240 struct dsdb_dn *target_dsdb_dn,
7241 struct ldb_val *output_val)
7243 struct ldb_context *ldb = ldb_module_get_ctx(module);
7246 const struct GUID *invocation_id = NULL;
7250 unix_to_nt_time(&now, t);
7252 invocation_id = samdb_ntds_invocation_id(ldb);
7253 if (invocation_id == NULL) {
7254 return LDB_ERR_OPERATIONS_ERROR;
7257 /* if the existing link is active, remove its backlink */
7260 ret = replmd_add_backlink(module, replmd_private, schema,
7261 src_obj_dn, target_guid, false,
7263 if (ret != LDB_SUCCESS) {
7268 /* mark the existing value as deleted */
7269 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7270 target_dsdb_dn, invocation_id, seq_num,
7271 seq_num, now, true);
7276 process one linked attribute structure
7278 static int replmd_process_linked_attribute(struct ldb_module *module,
7279 struct replmd_private *replmd_private,
7280 struct la_entry *la_entry,
7281 struct ldb_request *parent)
7283 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7284 struct ldb_context *ldb = ldb_module_get_ctx(module);
7285 struct ldb_message *msg;
7286 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7287 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7289 const struct dsdb_attribute *attr;
7290 struct dsdb_dn *dsdb_dn;
7291 uint64_t seq_num = 0;
7292 struct ldb_message_element *old_el;
7293 time_t t = time(NULL);
7294 struct parsed_dn *pdn_list, *pdn, *next;
7295 struct parsed_dn *conflict_pdn = NULL;
7296 struct GUID guid = GUID_zero();
7297 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7299 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7302 * get the attribute being modified, the search result for the source object,
7303 * and the target object's DN details
7305 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7308 if (ret != LDB_SUCCESS) {
7309 talloc_free(tmp_ctx);
7314 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7315 * ProcessLinkValue, because link updates are not applied to
7316 * recycled and tombstone objects. We don't have to delete
7317 * any existing link, that should have happened when the
7318 * object deletion was replicated or initiated.
7320 replmd_deletion_state(module, msg, &deletion_state, NULL);
7322 if (deletion_state >= OBJECT_RECYCLED) {
7323 talloc_free(tmp_ctx);
7327 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7328 if (old_el == NULL) {
7329 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7330 if (ret != LDB_SUCCESS) {
7331 ldb_module_oom(module);
7332 talloc_free(tmp_ctx);
7333 return LDB_ERR_OPERATIONS_ERROR;
7336 old_el->flags = LDB_FLAG_MOD_REPLACE;
7339 /* parse the existing links */
7340 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7341 attr->syntax->ldap_oid, parent);
7343 if (ret != LDB_SUCCESS) {
7344 talloc_free(tmp_ctx);
7348 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7349 true, &guid, &ignore_link);
7351 if (ret != LDB_SUCCESS) {
7352 talloc_free(tmp_ctx);
7357 * there are some cases where the target object doesn't exist, but it's
7358 * OK to ignore the linked attribute
7361 talloc_free(tmp_ctx);
7365 /* see if this link already exists */
7366 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7369 dsdb_dn->extra_part, 0,
7371 attr->syntax->ldap_oid,
7373 if (ret != LDB_SUCCESS) {
7374 talloc_free(tmp_ctx);
7378 if (pdn == NULL && active) {
7381 * check if there's a conflict for single-valued links, i.e.
7382 * an active linked attribute already exists, but it has a
7383 * different target value
7385 ret = replmd_get_active_singleval_link(module, tmp_ctx, pdn_list,
7386 old_el->num_values, attr,
7388 if (ret != LDB_SUCCESS) {
7389 talloc_free(tmp_ctx);
7394 if (!replmd_link_update_is_newer(pdn, la)) {
7395 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7396 old_el->name, ldb_dn_get_linearized(msg->dn),
7397 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7398 talloc_free(tmp_ctx);
7402 /* get a seq_num for this change */
7403 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7404 if (ret != LDB_SUCCESS) {
7405 talloc_free(tmp_ctx);
7409 /* resolve any single-valued link conflicts */
7410 if (conflict_pdn != NULL) {
7412 DBG_WARNING("Link conflict for %s attribute on %s\n",
7413 attr->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
7415 if (replmd_link_update_is_newer(conflict_pdn, la)) {
7416 DBG_WARNING("Using received value %s, over existing target %s\n",
7417 ldb_dn_get_linearized(dsdb_dn->dn),
7418 ldb_dn_get_linearized(conflict_pdn->dsdb_dn->dn));
7420 ret = replmd_delete_link_value(module, replmd_private,
7421 old_el, msg->dn, schema,
7422 attr, seq_num, true,
7423 &conflict_pdn->guid,
7424 conflict_pdn->dsdb_dn,
7427 if (ret != LDB_SUCCESS) {
7428 talloc_free(tmp_ctx);
7432 DBG_WARNING("Using existing target %s, over received value %s\n",
7433 ldb_dn_get_linearized(conflict_pdn->dsdb_dn->dn),
7434 ldb_dn_get_linearized(dsdb_dn->dn));
7436 /* don't add the link as active */
7442 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7444 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7445 /* remove the existing backlink */
7446 ret = replmd_add_backlink(module, replmd_private,
7449 &pdn->guid, false, attr,
7451 if (ret != LDB_SUCCESS) {
7452 talloc_free(tmp_ctx);
7457 ret = replmd_set_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
7458 &la->meta_data.originating_invocation_id,
7459 la->meta_data.originating_usn, seq_num,
7460 la->meta_data.originating_change_time,
7461 la->meta_data.version,
7463 if (ret != LDB_SUCCESS) {
7464 talloc_free(tmp_ctx);
7471 * We know where the new one needs to be, from the *next
7472 * pointer into pdn_list.
7475 offset = old_el->num_values;
7477 if (next->dsdb_dn == NULL) {
7478 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7479 attr->syntax->ldap_oid);
7480 if (ret != LDB_SUCCESS) {
7484 offset = next - pdn_list;
7485 if (offset > old_el->num_values) {
7486 talloc_free(tmp_ctx);
7487 return LDB_ERR_OPERATIONS_ERROR;
7491 old_el->values = talloc_realloc(msg->elements, old_el->values,
7492 struct ldb_val, old_el->num_values+1);
7493 if (!old_el->values) {
7494 ldb_module_oom(module);
7495 return LDB_ERR_OPERATIONS_ERROR;
7498 if (offset != old_el->num_values) {
7499 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7500 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7503 old_el->num_values++;
7505 ret = replmd_build_la_val(tmp_ctx, &old_el->values[offset], dsdb_dn,
7506 &la->meta_data.originating_invocation_id,
7507 la->meta_data.originating_usn, seq_num,
7508 la->meta_data.originating_change_time,
7509 la->meta_data.version,
7511 if (ret != LDB_SUCCESS) {
7512 talloc_free(tmp_ctx);
7517 /* if the new link is active, then add the new backlink */
7519 ret = replmd_add_backlink(module, replmd_private,
7524 if (ret != LDB_SUCCESS) {
7525 talloc_free(tmp_ctx);
7530 /* we only change whenChanged and uSNChanged if the seq_num
7532 ret = add_time_element(msg, "whenChanged", t);
7533 if (ret != LDB_SUCCESS) {
7534 talloc_free(tmp_ctx);
7539 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7540 if (ret != LDB_SUCCESS) {
7541 talloc_free(tmp_ctx);
7546 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7547 if (old_el == NULL) {
7548 talloc_free(tmp_ctx);
7549 return ldb_operr(ldb);
7552 ret = dsdb_check_single_valued_link(attr, old_el);
7553 if (ret != LDB_SUCCESS) {
7554 talloc_free(tmp_ctx);
7558 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7560 ret = linked_attr_modify(module, msg, parent);
7561 if (ret != LDB_SUCCESS) {
7562 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7564 ldb_ldif_message_redacted_string(ldb,
7566 LDB_CHANGETYPE_MODIFY,
7568 talloc_free(tmp_ctx);
7572 talloc_free(tmp_ctx);
7577 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7579 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7580 return replmd_extended_replicated_objects(module, req);
7583 return ldb_next_request(module, req);
7588 we hook into the transaction operations to allow us to
7589 perform the linked attribute updates at the end of the whole
7590 transaction. This allows a forward linked attribute to be created
7591 before the object is created. During a vampire, w2k8 sends us linked
7592 attributes before the objects they are part of.
7594 static int replmd_start_transaction(struct ldb_module *module)
7596 /* create our private structure for this transaction */
7597 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7598 struct replmd_private);
7599 replmd_txn_cleanup(replmd_private);
7601 /* free any leftover mod_usn records from cancelled
7603 while (replmd_private->ncs) {
7604 struct nc_entry *e = replmd_private->ncs;
7605 DLIST_REMOVE(replmd_private->ncs, e);
7609 replmd_private->originating_updates = false;
7611 return ldb_next_start_trans(module);
7615 on prepare commit we loop over our queued la_context structures and
7618 static int replmd_prepare_commit(struct ldb_module *module)
7620 struct replmd_private *replmd_private =
7621 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7622 struct la_entry *la, *prev;
7626 * Walk the list of linked attributes from DRS replication.
7628 * We walk backwards, to do the first entry first, as we
7629 * added the entries with DLIST_ADD() which puts them at the
7632 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7633 prev = DLIST_PREV(la);
7634 DLIST_REMOVE(replmd_private->la_list, la);
7635 ret = replmd_process_linked_attribute(module, replmd_private,
7637 if (ret != LDB_SUCCESS) {
7638 replmd_txn_cleanup(replmd_private);
7643 replmd_txn_cleanup(replmd_private);
7645 /* possibly change @REPLCHANGED */
7646 ret = replmd_notify_store(module, NULL);
7647 if (ret != LDB_SUCCESS) {
7651 return ldb_next_prepare_commit(module);
7654 static int replmd_del_transaction(struct ldb_module *module)
7656 struct replmd_private *replmd_private =
7657 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7658 replmd_txn_cleanup(replmd_private);
7660 return ldb_next_del_trans(module);
7664 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7665 .name = "repl_meta_data",
7666 .init_context = replmd_init,
7668 .modify = replmd_modify,
7669 .rename = replmd_rename,
7670 .del = replmd_delete,
7671 .extended = replmd_extended,
7672 .start_transaction = replmd_start_transaction,
7673 .prepare_commit = replmd_prepare_commit,
7674 .del_transaction = replmd_del_transaction,
7677 int ldb_repl_meta_data_module_init(const char *version)
7679 LDB_MODULE_CHECK_VERSION(version);
7680 return ldb_register_module(&ldb_repl_meta_data_module_ops);