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,
2217 uint32_t version, bool deleted);
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, 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 update an extended DN, including all meta data fields
2295 see replmd_build_la_val for value names
2297 static int replmd_update_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;
2309 uint32_t old_version;
2312 const char *dnstring;
2314 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2316 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2318 return LDB_ERR_OPERATIONS_ERROR;
2320 tval = data_blob_string_const(tstring);
2322 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2324 return LDB_ERR_OPERATIONS_ERROR;
2326 usnv = data_blob_string_const(usn_string);
2328 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2330 return LDB_ERR_OPERATIONS_ERROR;
2332 local_usnv = data_blob_string_const(usn_string);
2334 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2335 if (!NT_STATUS_IS_OK(status)) {
2336 return LDB_ERR_OPERATIONS_ERROR;
2339 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2340 if (!flags_string) {
2341 return LDB_ERR_OPERATIONS_ERROR;
2343 flagsv = data_blob_string_const(flags_string);
2345 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2346 if (ret != LDB_SUCCESS) return ret;
2348 /* get the ADDTIME from the original */
2349 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2350 if (old_addtime == NULL) {
2351 old_addtime = &tval;
2353 if (dsdb_dn != old_dsdb_dn ||
2354 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2355 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2356 if (ret != LDB_SUCCESS) return ret;
2359 /* use our invocation id */
2360 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2361 if (ret != LDB_SUCCESS) return ret;
2363 /* changetime is the current time */
2364 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2365 if (ret != LDB_SUCCESS) return ret;
2367 /* update the USN */
2368 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2369 if (ret != LDB_SUCCESS) return ret;
2371 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2372 if (ret != LDB_SUCCESS) return ret;
2374 /* increase the version by 1 */
2375 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2376 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2377 version = old_version+1;
2379 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2380 vers = data_blob_string_const(vstring);
2381 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2382 if (ret != LDB_SUCCESS) return ret;
2384 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2385 if (dnstring == NULL) {
2386 return LDB_ERR_OPERATIONS_ERROR;
2388 *v = data_blob_string_const(dnstring);
2394 handle adding a linked attribute
2396 static int replmd_modify_la_add(struct ldb_module *module,
2397 struct replmd_private *replmd_private,
2398 const struct dsdb_schema *schema,
2399 struct ldb_message *msg,
2400 struct ldb_message_element *el,
2401 struct ldb_message_element *old_el,
2402 const struct dsdb_attribute *schema_attr,
2405 struct ldb_dn *msg_dn,
2406 struct ldb_request *parent)
2409 struct parsed_dn *dns, *old_dns;
2410 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2412 struct ldb_val *new_values = NULL;
2413 unsigned old_num_values = old_el ? old_el->num_values : 0;
2414 unsigned num_values = 0;
2415 unsigned max_num_values;
2416 const struct GUID *invocation_id;
2417 struct ldb_context *ldb = ldb_module_get_ctx(module);
2419 unix_to_nt_time(&now, t);
2421 invocation_id = samdb_ntds_invocation_id(ldb);
2422 if (!invocation_id) {
2423 talloc_free(tmp_ctx);
2424 return LDB_ERR_OPERATIONS_ERROR;
2427 /* get the DNs to be added, fully parsed.
2429 * We need full parsing because they came off the wire and we don't
2430 * trust them, besides which we need their details to know where to put
2433 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2434 schema_attr->syntax->ldap_oid, parent);
2435 if (ret != LDB_SUCCESS) {
2436 talloc_free(tmp_ctx);
2440 /* get the existing DNs, lazily parsed */
2441 ret = get_parsed_dns_trusted(module, replmd_private,
2442 tmp_ctx, old_el, &old_dns,
2443 schema_attr->syntax->ldap_oid, parent);
2445 if (ret != LDB_SUCCESS) {
2446 talloc_free(tmp_ctx);
2450 max_num_values = old_num_values + el->num_values;
2451 if (max_num_values < old_num_values) {
2452 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2453 "old values: %u, new values: %u, sum: %u",
2454 old_num_values, el->num_values, max_num_values));
2455 talloc_free(tmp_ctx);
2456 return LDB_ERR_OPERATIONS_ERROR;
2459 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2461 if (new_values == NULL) {
2462 ldb_module_oom(module);
2463 talloc_free(tmp_ctx);
2464 return LDB_ERR_OPERATIONS_ERROR;
2468 * For each new value, find where it would go in the list. If there is
2469 * a matching GUID there, we update the existing value; otherwise we
2473 for (i = 0; i < el->num_values; i++) {
2474 struct parsed_dn *exact;
2475 struct parsed_dn *next;
2477 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2480 dns[i].dsdb_dn->extra_part, 0,
2482 schema_attr->syntax->ldap_oid,
2484 if (err != LDB_SUCCESS) {
2485 talloc_free(tmp_ctx);
2489 if (exact != NULL) {
2491 * We are trying to add one that exists, which is only
2492 * allowed if it was previously deleted.
2494 * When we do undelete a link we change it in place.
2495 * It will be copied across into the right spot in due
2499 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2501 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2502 struct GUID_txt_buf guid_str;
2503 ldb_asprintf_errstring(ldb,
2504 "Attribute %s already "
2505 "exists for target GUID %s",
2507 GUID_buf_string(&exact->guid,
2509 talloc_free(tmp_ctx);
2510 /* error codes for 'member' need to be
2512 if (ldb_attr_cmp(el->name, "member") == 0) {
2513 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2515 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2519 ret = replmd_update_la_val(new_values, exact->v,
2522 invocation_id, seq_num,
2523 seq_num, now, 0, false);
2524 if (ret != LDB_SUCCESS) {
2525 talloc_free(tmp_ctx);
2529 ret = replmd_add_backlink(module, replmd_private,
2536 if (ret != LDB_SUCCESS) {
2537 talloc_free(tmp_ctx);
2543 * Here we don't have an exact match.
2545 * If next is NULL, this one goes beyond the end of the
2546 * existing list, so we need to add all of those ones first.
2548 * If next is not NULL, we need to add all the ones before
2552 offset = old_num_values;
2554 /* next should have been parsed, but let's make sure */
2555 if (next->dsdb_dn == NULL) {
2556 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2557 schema_attr->syntax->ldap_oid);
2558 if (ret != LDB_SUCCESS) {
2562 offset = MIN(next - old_dns, old_num_values);
2565 /* put all the old ones before next on the list */
2566 for (; j < offset; j++) {
2567 new_values[num_values] = *old_dns[j].v;
2571 ret = replmd_add_backlink(module, replmd_private,
2576 /* Make the new linked attribute ldb_val. */
2577 ret = replmd_build_la_val(new_values, &new_values[num_values],
2578 dns[i].dsdb_dn, invocation_id,
2581 if (ret != LDB_SUCCESS) {
2582 talloc_free(tmp_ctx);
2586 if (ret != LDB_SUCCESS) {
2587 talloc_free(tmp_ctx);
2591 /* copy the rest of the old ones (if any) */
2592 for (; j < old_num_values; j++) {
2593 new_values[num_values] = *old_dns[j].v;
2597 talloc_steal(msg->elements, new_values);
2598 if (old_el != NULL) {
2599 talloc_steal(msg->elements, old_el->values);
2601 el->values = new_values;
2602 el->num_values = num_values;
2604 talloc_free(tmp_ctx);
2606 /* we now tell the backend to replace all existing values
2607 with the one we have constructed */
2608 el->flags = LDB_FLAG_MOD_REPLACE;
2615 handle deleting all active linked attributes
2617 static int replmd_modify_la_delete(struct ldb_module *module,
2618 struct replmd_private *replmd_private,
2619 const struct dsdb_schema *schema,
2620 struct ldb_message *msg,
2621 struct ldb_message_element *el,
2622 struct ldb_message_element *old_el,
2623 const struct dsdb_attribute *schema_attr,
2626 struct ldb_dn *msg_dn,
2627 struct ldb_request *parent)
2630 struct parsed_dn *dns, *old_dns;
2631 TALLOC_CTX *tmp_ctx = NULL;
2633 struct ldb_context *ldb = ldb_module_get_ctx(module);
2634 struct ldb_control *vanish_links_ctrl = NULL;
2635 bool vanish_links = false;
2636 unsigned int num_to_delete = el->num_values;
2638 const struct GUID *invocation_id;
2641 unix_to_nt_time(&now, t);
2643 invocation_id = samdb_ntds_invocation_id(ldb);
2644 if (!invocation_id) {
2645 return LDB_ERR_OPERATIONS_ERROR;
2648 if (old_el == NULL || old_el->num_values == 0) {
2649 /* there is nothing to delete... */
2650 if (num_to_delete == 0) {
2651 /* and we're deleting nothing, so that's OK */
2654 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2657 tmp_ctx = talloc_new(msg);
2658 if (tmp_ctx == NULL) {
2659 return LDB_ERR_OPERATIONS_ERROR;
2662 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2663 schema_attr->syntax->ldap_oid, parent);
2664 if (ret != LDB_SUCCESS) {
2665 talloc_free(tmp_ctx);
2669 ret = get_parsed_dns_trusted(module, replmd_private,
2670 tmp_ctx, old_el, &old_dns,
2671 schema_attr->syntax->ldap_oid, parent);
2673 if (ret != LDB_SUCCESS) {
2674 talloc_free(tmp_ctx);
2679 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2680 if (vanish_links_ctrl) {
2681 vanish_links = true;
2682 vanish_links_ctrl->critical = false;
2686 /* we empty out el->values here to avoid damage if we return early. */
2691 * If vanish links is set, we are actually removing members of
2692 * old_el->values; otherwise we are just marking them deleted.
2694 * There is a special case when no values are given: we remove them
2695 * all. When we have the vanish_links control we just have to remove
2696 * the backlinks and change our element to replace the existing values
2697 * with the empty list.
2700 if (num_to_delete == 0) {
2701 for (i = 0; i < old_el->num_values; i++) {
2702 struct parsed_dn *p = &old_dns[i];
2703 if (p->dsdb_dn == NULL) {
2704 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2705 schema_attr->syntax->ldap_oid);
2706 if (ret != LDB_SUCCESS) {
2710 ret = replmd_add_backlink(module, replmd_private,
2711 schema, msg_dn, &p->guid,
2714 if (ret != LDB_SUCCESS) {
2715 talloc_free(tmp_ctx);
2722 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2723 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2727 ret = replmd_update_la_val(old_el->values, p->v,
2728 p->dsdb_dn, p->dsdb_dn,
2729 invocation_id, seq_num,
2730 seq_num, now, 0, true);
2731 if (ret != LDB_SUCCESS) {
2732 talloc_free(tmp_ctx);
2738 el->flags = LDB_FLAG_MOD_REPLACE;
2739 talloc_free(tmp_ctx);
2745 for (i = 0; i < num_to_delete; i++) {
2746 struct parsed_dn *p = &dns[i];
2747 struct parsed_dn *exact = NULL;
2748 struct parsed_dn *next = NULL;
2749 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2752 p->dsdb_dn->extra_part, 0,
2754 schema_attr->syntax->ldap_oid,
2756 if (ret != LDB_SUCCESS) {
2757 talloc_free(tmp_ctx);
2760 if (exact == NULL) {
2761 struct GUID_txt_buf buf;
2762 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2763 "exist for target GUID %s",
2765 GUID_buf_string(&p->guid, &buf));
2766 if (ldb_attr_cmp(el->name, "member") == 0) {
2767 talloc_free(tmp_ctx);
2768 return LDB_ERR_UNWILLING_TO_PERFORM;
2770 talloc_free(tmp_ctx);
2771 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2776 if (CHECK_DEBUGLVL(5)) {
2777 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2778 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2779 struct GUID_txt_buf buf;
2780 const char *guid_str = \
2781 GUID_buf_string(&p->guid, &buf);
2782 DEBUG(5, ("Deleting deleted linked "
2783 "attribute %s to %s, because "
2784 "vanish_links control is set\n",
2785 el->name, guid_str));
2789 /* remove the backlink */
2790 ret = replmd_add_backlink(module,
2797 if (ret != LDB_SUCCESS) {
2798 talloc_free(tmp_ctx);
2802 /* We flag the deletion and tidy it up later. */
2807 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2809 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2810 struct GUID_txt_buf buf;
2811 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2812 ldb_asprintf_errstring(ldb, "Attribute %s already "
2813 "deleted for target GUID %s",
2814 el->name, guid_str);
2815 if (ldb_attr_cmp(el->name, "member") == 0) {
2816 talloc_free(tmp_ctx);
2817 return LDB_ERR_UNWILLING_TO_PERFORM;
2819 talloc_free(tmp_ctx);
2820 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2824 ret = replmd_update_la_val(old_el->values, exact->v,
2825 exact->dsdb_dn, exact->dsdb_dn,
2826 invocation_id, seq_num, seq_num,
2828 if (ret != LDB_SUCCESS) {
2829 talloc_free(tmp_ctx);
2832 ret = replmd_add_backlink(module, replmd_private,
2837 if (ret != LDB_SUCCESS) {
2838 talloc_free(tmp_ctx);
2845 for (i = 0; i < old_el->num_values; i++) {
2846 if (old_dns[i].v != NULL) {
2847 old_el->values[j] = *old_dns[i].v;
2851 old_el->num_values = j;
2854 el->values = talloc_steal(msg->elements, old_el->values);
2855 el->num_values = old_el->num_values;
2857 talloc_free(tmp_ctx);
2859 /* we now tell the backend to replace all existing values
2860 with the one we have constructed */
2861 el->flags = LDB_FLAG_MOD_REPLACE;
2867 handle replacing a linked attribute
2869 static int replmd_modify_la_replace(struct ldb_module *module,
2870 struct replmd_private *replmd_private,
2871 const struct dsdb_schema *schema,
2872 struct ldb_message *msg,
2873 struct ldb_message_element *el,
2874 struct ldb_message_element *old_el,
2875 const struct dsdb_attribute *schema_attr,
2878 struct ldb_dn *msg_dn,
2879 struct ldb_request *parent)
2881 unsigned int i, old_i, new_i;
2882 struct parsed_dn *dns, *old_dns;
2883 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2885 const struct GUID *invocation_id;
2886 struct ldb_context *ldb = ldb_module_get_ctx(module);
2887 struct ldb_val *new_values = NULL;
2888 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2889 unsigned int old_num_values;
2890 unsigned int repl_num_values;
2891 unsigned int max_num_values;
2894 unix_to_nt_time(&now, t);
2896 invocation_id = samdb_ntds_invocation_id(ldb);
2897 if (!invocation_id) {
2898 return LDB_ERR_OPERATIONS_ERROR;
2902 * The replace operation is unlike the replace and delete cases in that
2903 * we need to look at every existing link to see whether it is being
2904 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2906 * As we are trying to combine two sorted lists, the algorithm we use
2907 * is akin to the merge phase of a merge sort. We interleave the two
2908 * lists, doing different things depending on which side the current
2911 * There are three main cases, with some sub-cases.
2913 * - a DN is in the old list but not the new one. It needs to be
2914 * marked as deleted (but left in the list).
2915 * - maybe it is already deleted, and we have less to do.
2917 * - a DN is in both lists. The old data gets replaced by the new,
2918 * and the list doesn't grow. The old link may have been marked as
2919 * deleted, in which case we undelete it.
2921 * - a DN is in the new list only. We add it in the right place.
2924 old_num_values = old_el ? old_el->num_values : 0;
2925 repl_num_values = el->num_values;
2926 max_num_values = old_num_values + repl_num_values;
2928 if (max_num_values == 0) {
2929 /* There is nothing to do! */
2933 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2934 if (ret != LDB_SUCCESS) {
2935 talloc_free(tmp_ctx);
2939 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2941 if (ret != LDB_SUCCESS) {
2942 talloc_free(tmp_ctx);
2946 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2948 if (ret != LDB_SUCCESS) {
2949 talloc_free(tmp_ctx);
2953 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2954 if (new_values == NULL) {
2955 ldb_module_oom(module);
2956 talloc_free(tmp_ctx);
2957 return LDB_ERR_OPERATIONS_ERROR;
2962 for (i = 0; i < max_num_values; i++) {
2964 struct parsed_dn *old_p, *new_p;
2965 if (old_i < old_num_values && new_i < repl_num_values) {
2966 old_p = &old_dns[old_i];
2967 new_p = &dns[new_i];
2968 cmp = parsed_dn_compare(old_p, new_p);
2969 } else if (old_i < old_num_values) {
2970 /* the new list is empty, read the old list */
2971 old_p = &old_dns[old_i];
2974 } else if (new_i < repl_num_values) {
2975 /* the old list is empty, read new list */
2977 new_p = &dns[new_i];
2985 * An old ones that come before the next replacement
2986 * (if any). We mark it as deleted and add it to the
2989 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2990 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
2991 ret = replmd_update_la_val(new_values, old_p->v,
2997 if (ret != LDB_SUCCESS) {
2998 talloc_free(tmp_ctx);
3002 ret = replmd_add_backlink(module, replmd_private,
3005 &old_p->guid, false,
3008 if (ret != LDB_SUCCESS) {
3009 talloc_free(tmp_ctx);
3013 new_values[i] = *old_p->v;
3015 } else if (cmp == 0) {
3017 * We are overwriting one. If it was previously
3018 * deleted, we need to add a backlink.
3020 * Note that if any RMD_FLAGs in an extended new DN
3025 ret = replmd_update_la_val(new_values, old_p->v,
3031 if (ret != LDB_SUCCESS) {
3032 talloc_free(tmp_ctx);
3036 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3037 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3038 ret = replmd_add_backlink(module, replmd_private,
3044 if (ret != LDB_SUCCESS) {
3045 talloc_free(tmp_ctx);
3050 new_values[i] = *old_p->v;
3055 * Replacements that don't match an existing one. We
3056 * just add them to the final list.
3058 ret = replmd_build_la_val(new_values,
3064 if (ret != LDB_SUCCESS) {
3065 talloc_free(tmp_ctx);
3068 ret = replmd_add_backlink(module, replmd_private,
3074 if (ret != LDB_SUCCESS) {
3075 talloc_free(tmp_ctx);
3078 new_values[i] = *new_p->v;
3082 if (old_el != NULL) {
3083 talloc_steal(msg->elements, old_el->values);
3085 el->values = talloc_steal(msg->elements, new_values);
3087 talloc_free(tmp_ctx);
3089 el->flags = LDB_FLAG_MOD_REPLACE;
3096 handle linked attributes in modify requests
3098 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3099 struct replmd_private *replmd_private,
3100 struct ldb_message *msg,
3101 uint64_t seq_num, time_t t,
3102 struct ldb_request *parent)
3104 struct ldb_result *res;
3107 struct ldb_context *ldb = ldb_module_get_ctx(module);
3108 struct ldb_message *old_msg;
3110 const struct dsdb_schema *schema;
3112 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3114 * Nothing special is required for modifying or vanishing links
3115 * in fl2000 since they are just strings in a multi-valued
3118 struct ldb_control *ctrl = ldb_request_get_control(parent,
3119 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3121 ctrl->critical = false;
3129 * We should restrict this to the intersection of the list of
3130 * linked attributes in the schema and the list of attributes
3133 * This will help performance a little, as otherwise we have
3134 * to allocate the entire object value-by-value.
3136 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3137 DSDB_FLAG_NEXT_MODULE |
3138 DSDB_SEARCH_SHOW_RECYCLED |
3139 DSDB_SEARCH_REVEAL_INTERNALS |
3140 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3142 if (ret != LDB_SUCCESS) {
3145 schema = dsdb_get_schema(ldb, res);
3147 return LDB_ERR_OPERATIONS_ERROR;
3150 old_msg = res->msgs[0];
3152 for (i=0; i<msg->num_elements; i++) {
3153 struct ldb_message_element *el = &msg->elements[i];
3154 struct ldb_message_element *old_el, *new_el;
3155 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3156 const struct dsdb_attribute *schema_attr
3157 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3159 ldb_asprintf_errstring(ldb,
3160 "%s: attribute %s is not a valid attribute in schema",
3161 __FUNCTION__, el->name);
3162 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3164 if (schema_attr->linkID == 0) {
3167 if ((schema_attr->linkID & 1) == 1) {
3168 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3171 /* Odd is for the target. Illegal to modify */
3172 ldb_asprintf_errstring(ldb,
3173 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3174 return LDB_ERR_UNWILLING_TO_PERFORM;
3176 old_el = ldb_msg_find_element(old_msg, el->name);
3178 case LDB_FLAG_MOD_REPLACE:
3179 ret = replmd_modify_la_replace(module, replmd_private,
3180 schema, msg, el, old_el,
3181 schema_attr, seq_num, t,
3185 case LDB_FLAG_MOD_DELETE:
3186 ret = replmd_modify_la_delete(module, replmd_private,
3187 schema, msg, el, old_el,
3188 schema_attr, seq_num, t,
3192 case LDB_FLAG_MOD_ADD:
3193 ret = replmd_modify_la_add(module, replmd_private,
3194 schema, msg, el, old_el,
3195 schema_attr, seq_num, t,
3200 ldb_asprintf_errstring(ldb,
3201 "invalid flags 0x%x for %s linked attribute",
3202 el->flags, el->name);
3203 return LDB_ERR_UNWILLING_TO_PERFORM;
3205 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3206 ldb_asprintf_errstring(ldb,
3207 "Attribute %s is single valued but more than one value has been supplied",
3209 /* Return codes as found on Windows 2012r2 */
3210 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3211 return LDB_ERR_CONSTRAINT_VIOLATION;
3213 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3216 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3219 if (ret != LDB_SUCCESS) {
3223 ldb_msg_remove_attr(old_msg, el->name);
3225 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3226 new_el->num_values = el->num_values;
3227 new_el->values = talloc_steal(msg->elements, el->values);
3229 /* TODO: this relises a bit too heavily on the exact
3230 behaviour of ldb_msg_find_element and
3231 ldb_msg_remove_element */
3232 old_el = ldb_msg_find_element(msg, el->name);
3234 ldb_msg_remove_element(msg, old_el);
3244 static int send_rodc_referral(struct ldb_request *req,
3245 struct ldb_context *ldb,
3248 char *referral = NULL;
3249 struct loadparm_context *lp_ctx = NULL;
3250 struct ldb_dn *fsmo_role_dn = NULL;
3251 struct ldb_dn *role_owner_dn = NULL;
3252 const char *domain = NULL;
3255 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3256 struct loadparm_context);
3258 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3259 &fsmo_role_dn, &role_owner_dn);
3261 if (W_ERROR_IS_OK(werr)) {
3262 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3263 if (server_dn != NULL) {
3264 ldb_dn_remove_child_components(server_dn, 1);
3265 domain = samdb_dn_to_dnshostname(ldb, req,
3270 if (domain == NULL) {
3271 domain = lpcfg_dnsdomain(lp_ctx);
3274 referral = talloc_asprintf(req, "ldap://%s/%s",
3276 ldb_dn_get_linearized(dn));
3277 if (referral == NULL) {
3279 return LDB_ERR_OPERATIONS_ERROR;
3282 return ldb_module_send_referral(req, referral);
3286 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3288 struct ldb_context *ldb;
3289 struct replmd_replicated_request *ac;
3290 struct ldb_request *down_req;
3291 struct ldb_message *msg;
3292 time_t t = time(NULL);
3294 bool is_urgent = false, rodc = false;
3295 bool is_schema_nc = false;
3296 unsigned int functional_level;
3297 const struct ldb_message_element *guid_el = NULL;
3298 struct ldb_control *sd_propagation_control;
3299 struct replmd_private *replmd_private =
3300 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3302 /* do not manipulate our control entries */
3303 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3304 return ldb_next_request(module, req);
3307 sd_propagation_control = ldb_request_get_control(req,
3308 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3309 if (sd_propagation_control != NULL) {
3310 if (req->op.mod.message->num_elements != 1) {
3311 return ldb_module_operr(module);
3313 ret = strcmp(req->op.mod.message->elements[0].name,
3314 "nTSecurityDescriptor");
3316 return ldb_module_operr(module);
3319 return ldb_next_request(module, req);
3322 ldb = ldb_module_get_ctx(module);
3324 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3326 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3327 if (guid_el != NULL) {
3328 ldb_set_errstring(ldb,
3329 "replmd_modify: it's not allowed to change the objectGUID!");
3330 return LDB_ERR_CONSTRAINT_VIOLATION;
3333 ac = replmd_ctx_init(module, req);
3335 return ldb_module_oom(module);
3338 functional_level = dsdb_functional_level(ldb);
3340 /* we have to copy the message as the caller might have it as a const */
3341 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3345 return LDB_ERR_OPERATIONS_ERROR;
3348 ldb_msg_remove_attr(msg, "whenChanged");
3349 ldb_msg_remove_attr(msg, "uSNChanged");
3351 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3353 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3354 msg, &ac->seq_num, t, is_schema_nc,
3356 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3357 ret = send_rodc_referral(req, ldb, msg->dn);
3363 if (ret != LDB_SUCCESS) {
3368 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3369 msg, ac->seq_num, t, req);
3370 if (ret != LDB_SUCCESS) {
3376 * - replace the old object with the newly constructed one
3379 ac->is_urgent = is_urgent;
3381 ret = ldb_build_mod_req(&down_req, ldb, ac,
3384 ac, replmd_op_callback,
3386 LDB_REQ_SET_LOCATION(down_req);
3387 if (ret != LDB_SUCCESS) {
3392 /* current partition control is needed by "replmd_op_callback" */
3393 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3394 ret = ldb_request_add_control(down_req,
3395 DSDB_CONTROL_CURRENT_PARTITION_OID,
3397 if (ret != LDB_SUCCESS) {
3403 /* If we are in functional level 2000, then
3404 * replmd_modify_handle_linked_attribs will have done
3406 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3407 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3408 if (ret != LDB_SUCCESS) {
3414 talloc_steal(down_req, msg);
3416 /* we only change whenChanged and uSNChanged if the seq_num
3418 if (ac->seq_num != 0) {
3419 ret = add_time_element(msg, "whenChanged", t);
3420 if (ret != LDB_SUCCESS) {
3426 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3427 if (ret != LDB_SUCCESS) {
3434 /* go on with the call chain */
3435 return ldb_next_request(module, down_req);
3438 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3441 handle a rename request
3443 On a rename we need to do an extra ldb_modify which sets the
3444 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3446 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3448 struct ldb_context *ldb;
3449 struct replmd_replicated_request *ac;
3451 struct ldb_request *down_req;
3453 /* do not manipulate our control entries */
3454 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3455 return ldb_next_request(module, req);
3458 ldb = ldb_module_get_ctx(module);
3460 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3462 ac = replmd_ctx_init(module, req);
3464 return ldb_module_oom(module);
3467 ret = ldb_build_rename_req(&down_req, ldb, ac,
3468 ac->req->op.rename.olddn,
3469 ac->req->op.rename.newdn,
3471 ac, replmd_rename_callback,
3473 LDB_REQ_SET_LOCATION(down_req);
3474 if (ret != LDB_SUCCESS) {
3479 /* go on with the call chain */
3480 return ldb_next_request(module, down_req);
3483 /* After the rename is compleated, update the whenchanged etc */
3484 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3486 struct ldb_context *ldb;
3487 struct ldb_request *down_req;
3488 struct ldb_message *msg;
3489 const struct dsdb_attribute *rdn_attr;
3490 const char *rdn_name;
3491 const struct ldb_val *rdn_val;
3492 const char *attrs[5] = { NULL, };
3493 time_t t = time(NULL);
3495 bool is_urgent = false, rodc = false;
3497 struct replmd_replicated_request *ac =
3498 talloc_get_type(req->context, struct replmd_replicated_request);
3499 struct replmd_private *replmd_private =
3500 talloc_get_type(ldb_module_get_private(ac->module),
3501 struct replmd_private);
3503 ldb = ldb_module_get_ctx(ac->module);
3505 if (ares->error != LDB_SUCCESS) {
3506 return ldb_module_done(ac->req, ares->controls,
3507 ares->response, ares->error);
3510 if (ares->type != LDB_REPLY_DONE) {
3511 ldb_set_errstring(ldb,
3512 "invalid ldb_reply_type in callback");
3514 return ldb_module_done(ac->req, NULL, NULL,
3515 LDB_ERR_OPERATIONS_ERROR);
3519 * - replace the old object with the newly constructed one
3522 msg = ldb_msg_new(ac);
3525 return LDB_ERR_OPERATIONS_ERROR;
3528 msg->dn = ac->req->op.rename.newdn;
3530 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3532 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3533 if (rdn_name == NULL) {
3535 return ldb_module_done(ac->req, NULL, NULL,
3539 /* normalize the rdn attribute name */
3540 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3541 if (rdn_attr == NULL) {
3543 return ldb_module_done(ac->req, NULL, NULL,
3546 rdn_name = rdn_attr->lDAPDisplayName;
3548 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3549 if (rdn_val == NULL) {
3551 return ldb_module_done(ac->req, NULL, NULL,
3555 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3557 return ldb_module_done(ac->req, NULL, NULL,
3560 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3562 return ldb_module_done(ac->req, NULL, NULL,
3565 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3567 return ldb_module_done(ac->req, NULL, NULL,
3570 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3572 return ldb_module_done(ac->req, NULL, NULL,
3577 * here we let replmd_update_rpmd() only search for
3578 * the existing "replPropertyMetaData" and rdn_name attributes.
3580 * We do not want the existing "name" attribute as
3581 * the "name" attribute needs to get the version
3582 * updated on rename even if the rdn value hasn't changed.
3584 * This is the diff of the meta data, for a moved user
3585 * on a w2k8r2 server:
3588 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3589 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3590 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3591 * version : 0x00000001 (1)
3592 * reserved : 0x00000000 (0)
3593 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3594 * local_usn : 0x00000000000037a5 (14245)
3595 * array: struct replPropertyMetaData1
3596 * attid : DRSUAPI_ATTID_name (0x90001)
3597 * - version : 0x00000001 (1)
3598 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3599 * + version : 0x00000002 (2)
3600 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3601 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3602 * - originating_usn : 0x00000000000037a5 (14245)
3603 * - local_usn : 0x00000000000037a5 (14245)
3604 * + originating_usn : 0x0000000000003834 (14388)
3605 * + local_usn : 0x0000000000003834 (14388)
3606 * array: struct replPropertyMetaData1
3607 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3608 * version : 0x00000004 (4)
3610 attrs[0] = "replPropertyMetaData";
3611 attrs[1] = "objectClass";
3612 attrs[2] = "instanceType";
3613 attrs[3] = rdn_name;
3616 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3617 msg, &ac->seq_num, t,
3618 is_schema_nc, &is_urgent, &rodc);
3619 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3620 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3622 return ldb_module_done(req, NULL, NULL, ret);
3625 if (ret != LDB_SUCCESS) {
3627 return ldb_module_done(ac->req, NULL, NULL, ret);
3630 if (ac->seq_num == 0) {
3632 return ldb_module_done(ac->req, NULL, NULL,
3634 "internal error seq_num == 0"));
3636 ac->is_urgent = is_urgent;
3638 ret = ldb_build_mod_req(&down_req, ldb, ac,
3641 ac, replmd_op_callback,
3643 LDB_REQ_SET_LOCATION(down_req);
3644 if (ret != LDB_SUCCESS) {
3649 /* current partition control is needed by "replmd_op_callback" */
3650 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3651 ret = ldb_request_add_control(down_req,
3652 DSDB_CONTROL_CURRENT_PARTITION_OID,
3654 if (ret != LDB_SUCCESS) {
3660 talloc_steal(down_req, msg);
3662 ret = add_time_element(msg, "whenChanged", t);
3663 if (ret != LDB_SUCCESS) {
3669 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3670 if (ret != LDB_SUCCESS) {
3676 /* go on with the call chain - do the modify after the rename */
3677 return ldb_next_request(ac->module, down_req);
3681 * remove links from objects that point at this object when an object
3682 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3683 * RemoveObj which states that link removal due to the object being
3684 * deleted is NOT an originating update - they just go away!
3687 static int replmd_delete_remove_link(struct ldb_module *module,
3688 const struct dsdb_schema *schema,
3689 struct replmd_private *replmd_private,
3692 struct ldb_message_element *el,
3693 const struct dsdb_attribute *sa,
3694 struct ldb_request *parent)
3697 TALLOC_CTX *tmp_ctx = talloc_new(module);
3698 struct ldb_context *ldb = ldb_module_get_ctx(module);
3700 for (i=0; i<el->num_values; i++) {
3701 struct dsdb_dn *dsdb_dn;
3703 struct ldb_message *msg;
3704 const struct dsdb_attribute *target_attr;
3705 struct ldb_message_element *el2;
3707 struct ldb_val dn_val;
3708 uint32_t dsdb_flags = 0;
3709 const char *attrs[] = { NULL, NULL };
3710 struct ldb_result *link_res;
3711 struct ldb_message *link_msg;
3712 struct ldb_message_element *link_el;
3713 struct parsed_dn *link_dns;
3714 struct parsed_dn *p = NULL, *unused = NULL;
3716 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3720 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3722 talloc_free(tmp_ctx);
3723 return LDB_ERR_OPERATIONS_ERROR;
3726 /* remove the link */
3727 msg = ldb_msg_new(tmp_ctx);
3729 ldb_module_oom(module);
3730 talloc_free(tmp_ctx);
3731 return LDB_ERR_OPERATIONS_ERROR;
3735 msg->dn = dsdb_dn->dn;
3737 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3738 if (target_attr == NULL) {
3741 attrs[0] = target_attr->lDAPDisplayName;
3743 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3744 LDB_FLAG_MOD_DELETE, &el2);
3745 if (ret != LDB_SUCCESS) {
3746 ldb_module_oom(module);
3747 talloc_free(tmp_ctx);
3748 return LDB_ERR_OPERATIONS_ERROR;
3751 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3753 DSDB_FLAG_NEXT_MODULE |
3754 DSDB_SEARCH_SHOW_EXTENDED_DN,
3757 if (ret != LDB_SUCCESS) {
3758 talloc_free(tmp_ctx);
3762 link_msg = link_res->msgs[0];
3763 link_el = ldb_msg_find_element(link_msg,
3764 target_attr->lDAPDisplayName);
3765 if (link_el == NULL) {
3766 talloc_free(tmp_ctx);
3767 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3771 * This call 'upgrades' the links in link_dns, but we
3772 * do not commit the result back into the database, so
3773 * this is safe to call in FL2000 or on databases that
3774 * have been run at that level in the past.
3776 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3778 target_attr->syntax->ldap_oid, parent);
3779 if (ret != LDB_SUCCESS) {
3780 talloc_free(tmp_ctx);
3784 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3788 target_attr->syntax->ldap_oid, false);
3789 if (ret != LDB_SUCCESS) {
3790 talloc_free(tmp_ctx);
3795 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3796 "Failed to find forward link on %s "
3797 "as %s to remove backlink %s on %s",
3798 ldb_dn_get_linearized(msg->dn),
3799 target_attr->lDAPDisplayName,
3800 sa->lDAPDisplayName,
3801 ldb_dn_get_linearized(dn));
3802 talloc_free(tmp_ctx);
3803 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3807 /* This needs to get the Binary DN, by first searching */
3808 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3811 dn_val = data_blob_string_const(dn_str);
3812 el2->values = &dn_val;
3813 el2->num_values = 1;
3816 * Ensure that we tell the modification to vanish any linked
3817 * attributes (not simply mark them as isDeleted = TRUE)
3819 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3821 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3822 if (ret != LDB_SUCCESS) {
3823 talloc_free(tmp_ctx);
3827 talloc_free(tmp_ctx);
3833 handle update of replication meta data for deletion of objects
3835 This also handles the mapping of delete to a rename operation
3836 to allow deletes to be replicated.
3838 It also handles the incoming deleted objects, to ensure they are
3839 fully deleted here. In that case re_delete is true, and we do not
3840 use this as a signal to change the deleted state, just reinforce it.
3843 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3845 int ret = LDB_ERR_OTHER;
3846 bool retb, disallow_move_on_delete;
3847 struct ldb_dn *old_dn, *new_dn;
3848 const char *rdn_name;
3849 const struct ldb_val *rdn_value, *new_rdn_value;
3851 struct ldb_context *ldb = ldb_module_get_ctx(module);
3852 const struct dsdb_schema *schema;
3853 struct ldb_message *msg, *old_msg;
3854 struct ldb_message_element *el;
3855 TALLOC_CTX *tmp_ctx;
3856 struct ldb_result *res, *parent_res;
3857 static const char * const preserved_attrs[] = {
3858 /* yes, this really is a hard coded list. See MS-ADTS
3859 section 3.1.1.5.5.1.1 */
3862 "dNReferenceUpdate",
3873 "msDS-LastKnownRDN",
3879 "distinguishedName",
3883 "proxiedObjectName",
3885 "nTSecurityDescriptor",
3886 "replPropertyMetaData",
3888 "securityIdentifier",
3896 "userAccountControl",
3903 static const char * const all_attrs[] = {
3904 DSDB_SECRET_ATTRIBUTES,
3908 unsigned int i, el_count = 0;
3909 uint32_t dsdb_flags = 0;
3910 struct replmd_private *replmd_private;
3911 enum deletion_state deletion_state, next_deletion_state;
3913 if (ldb_dn_is_special(req->op.del.dn)) {
3914 return ldb_next_request(module, req);
3918 * We have to allow dbcheck to remove an object that
3919 * is beyond repair, and to do so totally. This could
3920 * mean we we can get a partial object from the other
3921 * DC, causing havoc, so dbcheck suggests
3922 * re-replication first. dbcheck sets both DBCHECK
3923 * and RELAX in this situation.
3925 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3926 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3927 /* really, really remove it */
3928 return ldb_next_request(module, req);
3931 tmp_ctx = talloc_new(ldb);
3934 return LDB_ERR_OPERATIONS_ERROR;
3937 schema = dsdb_get_schema(ldb, tmp_ctx);
3939 talloc_free(tmp_ctx);
3940 return LDB_ERR_OPERATIONS_ERROR;
3943 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3945 /* we need the complete msg off disk, so we can work out which
3946 attributes need to be removed */
3947 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3948 DSDB_FLAG_NEXT_MODULE |
3949 DSDB_SEARCH_SHOW_RECYCLED |
3950 DSDB_SEARCH_REVEAL_INTERNALS |
3951 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3952 if (ret != LDB_SUCCESS) {
3953 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3954 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3955 re_delete ? "re-delete" : "delete",
3956 ldb_dn_get_linearized(old_dn),
3957 ldb_errstring(ldb_module_get_ctx(module)));
3958 talloc_free(tmp_ctx);
3961 old_msg = res->msgs[0];
3963 replmd_deletion_state(module, old_msg,
3965 &next_deletion_state);
3967 /* This supports us noticing an incoming isDeleted and acting on it */
3969 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3970 next_deletion_state = deletion_state;
3973 if (next_deletion_state == OBJECT_REMOVED) {
3975 * We have to prevent objects being deleted, even if
3976 * the administrator really wants them gone, as
3977 * without the tombstone, we can get a partial object
3978 * from the other DC, causing havoc.
3980 * The only other valid case is when the 180 day
3981 * timeout has expired, when relax is specified.
3983 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3984 /* it is already deleted - really remove it this time */
3985 talloc_free(tmp_ctx);
3986 return ldb_next_request(module, req);
3989 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3990 "This check is to prevent corruption of the replicated state.",
3991 ldb_dn_get_linearized(old_msg->dn));
3992 return LDB_ERR_UNWILLING_TO_PERFORM;
3995 rdn_name = ldb_dn_get_rdn_name(old_dn);
3996 rdn_value = ldb_dn_get_rdn_val(old_dn);
3997 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3998 talloc_free(tmp_ctx);
3999 return ldb_operr(ldb);
4002 msg = ldb_msg_new(tmp_ctx);
4004 ldb_module_oom(module);
4005 talloc_free(tmp_ctx);
4006 return LDB_ERR_OPERATIONS_ERROR;
4011 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4012 disallow_move_on_delete =
4013 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4014 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4016 /* work out where we will be renaming this object to */
4017 if (!disallow_move_on_delete) {
4018 struct ldb_dn *deleted_objects_dn;
4019 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4020 &deleted_objects_dn);
4023 * We should not move objects if we can't find the
4024 * deleted objects DN. Not moving (or otherwise
4025 * harming) the Deleted Objects DN itself is handled
4028 if (re_delete && (ret != LDB_SUCCESS)) {
4029 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4030 if (new_dn == NULL) {
4031 ldb_module_oom(module);
4032 talloc_free(tmp_ctx);
4033 return LDB_ERR_OPERATIONS_ERROR;
4035 } else if (ret != LDB_SUCCESS) {
4036 /* this is probably an attempted delete on a partition
4037 * that doesn't allow delete operations, such as the
4038 * schema partition */
4039 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4040 ldb_dn_get_linearized(old_dn));
4041 talloc_free(tmp_ctx);
4042 return LDB_ERR_UNWILLING_TO_PERFORM;
4044 new_dn = deleted_objects_dn;
4047 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4048 if (new_dn == NULL) {
4049 ldb_module_oom(module);
4050 talloc_free(tmp_ctx);
4051 return LDB_ERR_OPERATIONS_ERROR;
4055 /* get the objects GUID from the search we just did */
4056 guid = samdb_result_guid(old_msg, "objectGUID");
4058 if (deletion_state == OBJECT_NOT_DELETED) {
4059 /* Add a formatted child */
4060 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
4062 ldb_dn_escape_value(tmp_ctx, *rdn_value),
4063 GUID_string(tmp_ctx, &guid));
4065 ldb_asprintf_errstring(ldb, __location__
4066 ": Unable to add a formatted child to dn: %s",
4067 ldb_dn_get_linearized(new_dn));
4068 talloc_free(tmp_ctx);
4069 return LDB_ERR_OPERATIONS_ERROR;
4072 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4073 if (ret != LDB_SUCCESS) {
4074 ldb_asprintf_errstring(ldb, __location__
4075 ": Failed to add isDeleted string to the msg");
4076 talloc_free(tmp_ctx);
4079 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4082 * No matter what has happened with other renames etc, try again to
4083 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4086 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4087 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4089 ldb_asprintf_errstring(ldb, __location__
4090 ": Unable to add a prepare rdn of %s",
4091 ldb_dn_get_linearized(rdn));
4092 talloc_free(tmp_ctx);
4093 return LDB_ERR_OPERATIONS_ERROR;
4095 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4097 retb = ldb_dn_add_child(new_dn, rdn);
4099 ldb_asprintf_errstring(ldb, __location__
4100 ": Unable to add rdn %s to base dn: %s",
4101 ldb_dn_get_linearized(rdn),
4102 ldb_dn_get_linearized(new_dn));
4103 talloc_free(tmp_ctx);
4104 return LDB_ERR_OPERATIONS_ERROR;
4109 now we need to modify the object in the following ways:
4111 - add isDeleted=TRUE
4112 - update rDN and name, with new rDN
4113 - remove linked attributes
4114 - remove objectCategory and sAMAccountType
4115 - remove attribs not on the preserved list
4116 - preserved if in above list, or is rDN
4117 - remove all linked attribs from this object
4118 - remove all links from other objects to this object
4119 - add lastKnownParent
4120 - update replPropertyMetaData?
4122 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4125 if (deletion_state == OBJECT_NOT_DELETED) {
4126 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4127 char *parent_dn_str = NULL;
4129 /* we need the storage form of the parent GUID */
4130 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4132 DSDB_FLAG_NEXT_MODULE |
4133 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4134 DSDB_SEARCH_REVEAL_INTERNALS|
4135 DSDB_SEARCH_SHOW_RECYCLED, req);
4136 if (ret != LDB_SUCCESS) {
4137 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4138 "repmd_delete: Failed to %s %s, "
4139 "because we failed to find it's parent (%s): %s",
4140 re_delete ? "re-delete" : "delete",
4141 ldb_dn_get_linearized(old_dn),
4142 ldb_dn_get_linearized(parent_dn),
4143 ldb_errstring(ldb_module_get_ctx(module)));
4144 talloc_free(tmp_ctx);
4149 * Now we can use the DB version,
4150 * it will have the extended DN info in it
4152 parent_dn = parent_res->msgs[0]->dn;
4153 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4156 if (parent_dn_str == NULL) {
4157 talloc_free(tmp_ctx);
4158 return ldb_module_oom(module);
4161 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4163 if (ret != LDB_SUCCESS) {
4164 ldb_asprintf_errstring(ldb, __location__
4165 ": Failed to add lastKnownParent "
4166 "string when deleting %s",
4167 ldb_dn_get_linearized(old_dn));
4168 talloc_free(tmp_ctx);
4171 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4173 if (next_deletion_state == OBJECT_DELETED) {
4174 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4175 if (ret != LDB_SUCCESS) {
4176 ldb_asprintf_errstring(ldb, __location__
4177 ": Failed to add msDS-LastKnownRDN "
4178 "string when deleting %s",
4179 ldb_dn_get_linearized(old_dn));
4180 talloc_free(tmp_ctx);
4183 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4187 switch (next_deletion_state) {
4189 case OBJECT_RECYCLED:
4190 case OBJECT_TOMBSTONE:
4193 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4194 * describes what must be removed from a tombstone
4197 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4198 * describes what must be removed from a recycled
4204 * we also mark it as recycled, meaning this object can't be
4205 * recovered (we are stripping its attributes).
4206 * This is done only if we have this schema object of course ...
4207 * This behavior is identical to the one of Windows 2008R2 which
4208 * always set the isRecycled attribute, even if the recycle-bin is
4209 * not activated and what ever the forest level is.
4211 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4212 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4213 if (ret != LDB_SUCCESS) {
4214 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4215 ldb_module_oom(module);
4216 talloc_free(tmp_ctx);
4219 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4222 replmd_private = talloc_get_type(ldb_module_get_private(module),
4223 struct replmd_private);
4224 /* work out which of the old attributes we will be removing */
4225 for (i=0; i<old_msg->num_elements; i++) {
4226 const struct dsdb_attribute *sa;
4227 el = &old_msg->elements[i];
4228 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4230 talloc_free(tmp_ctx);
4231 return LDB_ERR_OPERATIONS_ERROR;
4233 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4234 /* don't remove the rDN */
4237 if (sa->linkID & 1) {
4239 we have a backlink in this object
4240 that needs to be removed. We're not
4241 allowed to remove it directly
4242 however, so we instead setup a
4243 modify to delete the corresponding
4246 ret = replmd_delete_remove_link(module, schema,
4250 if (ret != LDB_SUCCESS) {
4251 const char *old_dn_str
4252 = ldb_dn_get_linearized(old_dn);
4253 ldb_asprintf_errstring(ldb,
4255 ": Failed to remove backlink of "
4256 "%s when deleting %s: %s",
4259 ldb_errstring(ldb));
4260 talloc_free(tmp_ctx);
4261 return LDB_ERR_OPERATIONS_ERROR;
4263 /* now we continue, which means we
4264 won't remove this backlink
4268 } else if (sa->linkID == 0) {
4269 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4272 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4277 * Ensure that we tell the modification to vanish any linked
4278 * attributes (not simply mark them as isDeleted = TRUE)
4280 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4282 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4283 if (ret != LDB_SUCCESS) {
4284 talloc_free(tmp_ctx);
4285 ldb_module_oom(module);
4292 case OBJECT_DELETED:
4294 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4295 * describes what must be removed from a deleted
4299 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4300 if (ret != LDB_SUCCESS) {
4301 talloc_free(tmp_ctx);
4302 ldb_module_oom(module);
4306 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4307 if (ret != LDB_SUCCESS) {
4308 talloc_free(tmp_ctx);
4309 ldb_module_oom(module);
4319 if (deletion_state == OBJECT_NOT_DELETED) {
4320 const struct dsdb_attribute *sa;
4322 /* work out what the new rdn value is, for updating the
4323 rDN and name fields */
4324 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4325 if (new_rdn_value == NULL) {
4326 talloc_free(tmp_ctx);
4327 return ldb_operr(ldb);
4330 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4332 talloc_free(tmp_ctx);
4333 return LDB_ERR_OPERATIONS_ERROR;
4336 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4338 if (ret != LDB_SUCCESS) {
4339 talloc_free(tmp_ctx);
4342 el->flags = LDB_FLAG_MOD_REPLACE;
4344 el = ldb_msg_find_element(old_msg, "name");
4346 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4347 if (ret != LDB_SUCCESS) {
4348 talloc_free(tmp_ctx);
4351 el->flags = LDB_FLAG_MOD_REPLACE;
4356 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4361 * No matter what has happned with other renames, try again to
4362 * get this to be under the deleted DN.
4364 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4365 /* now rename onto the new DN */
4366 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4367 if (ret != LDB_SUCCESS){
4368 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4369 ldb_dn_get_linearized(old_dn),
4370 ldb_dn_get_linearized(new_dn),
4371 ldb_errstring(ldb)));
4372 talloc_free(tmp_ctx);
4378 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4379 if (ret != LDB_SUCCESS) {
4380 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4381 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4382 talloc_free(tmp_ctx);
4386 talloc_free(tmp_ctx);
4388 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4391 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4393 return replmd_delete_internals(module, req, false);
4397 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4402 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4404 int ret = LDB_ERR_OTHER;
4405 /* TODO: do some error mapping */
4407 /* Let the caller know the full WERROR */
4408 ar->objs->error = status;
4414 static struct replPropertyMetaData1 *
4415 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4416 enum drsuapi_DsAttributeId attid)
4419 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4421 for (i = 0; i < rpmd_ctr->count; i++) {
4422 if (rpmd_ctr->array[i].attid == attid) {
4423 return &rpmd_ctr->array[i];
4431 return true if an update is newer than an existing entry
4432 see section 5.11 of MS-ADTS
4434 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4435 const struct GUID *update_invocation_id,
4436 uint32_t current_version,
4437 uint32_t update_version,
4438 NTTIME current_change_time,
4439 NTTIME update_change_time)
4441 if (update_version != current_version) {
4442 return update_version > current_version;
4444 if (update_change_time != current_change_time) {
4445 return update_change_time > current_change_time;
4447 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4450 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4451 struct replPropertyMetaData1 *new_m)
4453 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4454 &new_m->originating_invocation_id,
4457 cur_m->originating_change_time,
4458 new_m->originating_change_time);
4461 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4462 struct replPropertyMetaData1 *cur_m,
4463 struct replPropertyMetaData1 *new_m)
4468 * If the new replPropertyMetaData entry for this attribute is
4469 * not provided (this happens in the case where we look for
4470 * ATTID_name, but the name was not changed), then the local
4471 * state is clearly still current, as the remote
4472 * server didn't send it due to being older the high watermark
4475 if (new_m == NULL) {
4479 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4481 * if we compare equal then do an
4482 * update. This is used when a client
4483 * asks for a FULL_SYNC, and can be
4484 * used to recover a corrupt
4487 * This call is a bit tricky, what we
4488 * are doing it turning the 'is_newer'
4489 * call into a 'not is older' by
4490 * swapping cur_m and new_m, and negating the
4493 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4496 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4506 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
4508 const struct ldb_val *rdn_val;
4509 const char *rdn_name;
4510 struct ldb_dn *new_dn;
4512 rdn_val = ldb_dn_get_rdn_val(dn);
4513 rdn_name = ldb_dn_get_rdn_name(dn);
4514 if (!rdn_val || !rdn_name) {
4518 new_dn = ldb_dn_copy(mem_ctx, dn);
4523 if (!ldb_dn_remove_child_components(new_dn, 1)) {
4527 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
4529 ldb_dn_escape_value(new_dn, *rdn_val),
4530 GUID_string(new_dn, guid))) {
4539 perform a modify operation which sets the rDN and name attributes to
4540 their current values. This has the effect of changing these
4541 attributes to have been last updated by the current DC. This is
4542 needed to ensure that renames performed as part of conflict
4543 resolution are propogated to other DCs
4545 static int replmd_name_modify(struct replmd_replicated_request *ar,
4546 struct ldb_request *req, struct ldb_dn *dn)
4548 struct ldb_message *msg;
4549 const char *rdn_name;
4550 const struct ldb_val *rdn_val;
4551 const struct dsdb_attribute *rdn_attr;
4554 msg = ldb_msg_new(req);
4560 rdn_name = ldb_dn_get_rdn_name(dn);
4561 if (rdn_name == NULL) {
4565 /* normalize the rdn attribute name */
4566 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4567 if (rdn_attr == NULL) {
4570 rdn_name = rdn_attr->lDAPDisplayName;
4572 rdn_val = ldb_dn_get_rdn_val(dn);
4573 if (rdn_val == NULL) {
4577 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4580 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4583 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4586 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4591 * We have to mark this as a replicated update otherwise
4592 * schema_data may reject a rename in the schema partition
4595 ret = dsdb_module_modify(ar->module, msg,
4596 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4598 if (ret != LDB_SUCCESS) {
4599 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4600 ldb_dn_get_linearized(dn),
4601 ldb_errstring(ldb_module_get_ctx(ar->module))));
4611 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4612 ldb_dn_get_linearized(dn)));
4613 return LDB_ERR_OPERATIONS_ERROR;
4618 callback for conflict DN handling where we have renamed the incoming
4619 record. After renaming it, we need to ensure the change of name and
4620 rDN for the incoming record is seen as an originating update by this DC.
4622 This also handles updating lastKnownParent for entries sent to lostAndFound
4624 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4626 struct replmd_replicated_request *ar =
4627 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4628 struct ldb_dn *conflict_dn = NULL;
4631 if (ares->error != LDB_SUCCESS) {
4632 /* call the normal callback for everything except success */
4633 return replmd_op_callback(req, ares);
4636 switch (req->operation) {
4638 conflict_dn = req->op.add.message->dn;
4641 conflict_dn = req->op.mod.message->dn;
4644 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4647 /* perform a modify of the rDN and name of the record */
4648 ret = replmd_name_modify(ar, req, conflict_dn);
4649 if (ret != LDB_SUCCESS) {
4651 return replmd_op_callback(req, ares);
4654 if (ar->objs->objects[ar->index_current].last_known_parent) {
4655 struct ldb_message *msg = ldb_msg_new(req);
4657 ldb_module_oom(ar->module);
4658 return LDB_ERR_OPERATIONS_ERROR;
4661 msg->dn = req->op.add.message->dn;
4663 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4664 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4665 if (ret != LDB_SUCCESS) {
4666 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4667 ldb_module_oom(ar->module);
4670 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4672 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4673 if (ret != LDB_SUCCESS) {
4674 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4675 ldb_dn_get_linearized(msg->dn),
4676 ldb_errstring(ldb_module_get_ctx(ar->module))));
4682 return replmd_op_callback(req, ares);
4686 callback for replmd_replicated_apply_add()
4687 This copes with the creation of conflict records in the case where
4688 the DN exists, but with a different objectGUID
4690 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))
4692 struct ldb_dn *conflict_dn;
4693 struct replmd_replicated_request *ar =
4694 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4695 struct ldb_result *res;
4696 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4698 const struct ldb_val *omd_value;
4699 struct replPropertyMetaDataBlob omd, *rmd;
4700 enum ndr_err_code ndr_err;
4701 bool rename_incoming_record, rodc;
4702 struct replPropertyMetaData1 *rmd_name, *omd_name;
4703 struct ldb_message *msg;
4704 struct ldb_request *down_req = NULL;
4706 /* call the normal callback for success */
4707 if (ares->error == LDB_SUCCESS) {
4708 return callback(req, ares);
4712 * we have a conflict, and need to decide if we will keep the
4713 * new record or the old record
4716 msg = ar->objs->objects[ar->index_current].msg;
4717 conflict_dn = msg->dn;
4719 /* For failures other than conflicts, fail the whole operation here */
4720 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4721 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4722 ldb_dn_get_linearized(conflict_dn),
4723 ldb_errstring(ldb_module_get_ctx(ar->module)));
4725 return ldb_module_done(ar->req, NULL, NULL,
4726 LDB_ERR_OPERATIONS_ERROR);
4729 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4730 if (ret != LDB_SUCCESS) {
4731 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)));
4732 return ldb_module_done(ar->req, NULL, NULL,
4733 LDB_ERR_OPERATIONS_ERROR);
4739 * We are on an RODC, or were a GC for this
4740 * partition, so we have to fail this until
4741 * someone who owns the partition sorts it
4744 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4745 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4746 " - We must fail the operation until a master for this partition resolves the conflict",
4747 ldb_dn_get_linearized(conflict_dn));
4752 * first we need the replPropertyMetaData attribute from the
4753 * local, conflicting record
4755 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4757 DSDB_FLAG_NEXT_MODULE |
4758 DSDB_SEARCH_SHOW_DELETED |
4759 DSDB_SEARCH_SHOW_RECYCLED, req);
4760 if (ret != LDB_SUCCESS) {
4761 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4762 ldb_dn_get_linearized(conflict_dn)));
4766 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4767 if (omd_value == NULL) {
4768 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4769 ldb_dn_get_linearized(conflict_dn)));
4773 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4774 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4775 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4776 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4777 ldb_dn_get_linearized(conflict_dn)));
4781 rmd = ar->objs->objects[ar->index_current].meta_data;
4784 * we decide which is newer based on the RPMD on the name
4785 * attribute. See [MS-DRSR] ResolveNameConflict.
4787 * We expect omd_name to be present, as this is from a local
4788 * search, but while rmd_name should have been given to us by
4789 * the remote server, if it is missing we just prefer the
4791 * replmd_replPropertyMetaData1_new_should_be_taken()
4793 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4794 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4796 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4797 ldb_dn_get_linearized(conflict_dn)));
4802 * Should we preserve the current record, and so rename the
4803 * incoming record to be a conflict?
4805 rename_incoming_record
4806 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4807 omd_name, rmd_name);
4809 if (rename_incoming_record) {
4811 struct ldb_dn *new_dn;
4813 guid = samdb_result_guid(msg, "objectGUID");
4814 if (GUID_all_zero(&guid)) {
4815 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4816 ldb_dn_get_linearized(conflict_dn)));
4819 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4820 if (new_dn == NULL) {
4821 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4822 ldb_dn_get_linearized(conflict_dn)));
4826 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4827 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4829 /* re-submit the request, but with the new DN */
4830 callback = replmd_op_name_modify_callback;
4833 /* we are renaming the existing record */
4835 struct ldb_dn *new_dn;
4837 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4838 if (GUID_all_zero(&guid)) {
4839 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4840 ldb_dn_get_linearized(conflict_dn)));
4844 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4845 if (new_dn == NULL) {
4846 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4847 ldb_dn_get_linearized(conflict_dn)));
4851 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4852 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4854 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4855 DSDB_FLAG_OWN_MODULE, req);
4856 if (ret != LDB_SUCCESS) {
4857 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4858 ldb_dn_get_linearized(conflict_dn),
4859 ldb_dn_get_linearized(new_dn),
4860 ldb_errstring(ldb_module_get_ctx(ar->module))));
4865 * now we need to ensure that the rename is seen as an
4866 * originating update. We do that with a modify.
4868 ret = replmd_name_modify(ar, req, new_dn);
4869 if (ret != LDB_SUCCESS) {
4873 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4874 ldb_dn_get_linearized(req->op.add.message->dn)));
4877 ret = ldb_build_add_req(&down_req,
4878 ldb_module_get_ctx(ar->module),
4885 if (ret != LDB_SUCCESS) {
4888 LDB_REQ_SET_LOCATION(down_req);
4890 /* current partition control needed by "repmd_op_callback" */
4891 ret = ldb_request_add_control(down_req,
4892 DSDB_CONTROL_CURRENT_PARTITION_OID,
4894 if (ret != LDB_SUCCESS) {
4895 return replmd_replicated_request_error(ar, ret);
4898 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4899 /* this tells the partition module to make it a
4900 partial replica if creating an NC */
4901 ret = ldb_request_add_control(down_req,
4902 DSDB_CONTROL_PARTIAL_REPLICA,
4904 if (ret != LDB_SUCCESS) {
4905 return replmd_replicated_request_error(ar, ret);
4910 * Finally we re-run the add, otherwise the new record won't
4911 * exist, as we are here because of that exact failure!
4913 return ldb_next_request(ar->module, down_req);
4916 /* on failure make the caller get the error. This means
4917 * replication will stop with an error, but there is not much
4920 return ldb_module_done(ar->req, NULL, NULL,
4925 callback for replmd_replicated_apply_add()
4926 This copes with the creation of conflict records in the case where
4927 the DN exists, but with a different objectGUID
4929 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4931 struct replmd_replicated_request *ar =
4932 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4934 if (ar->objs->objects[ar->index_current].last_known_parent) {
4935 /* This is like a conflict DN, where we put the object in LostAndFound
4936 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4937 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4940 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4944 this is called when a new object comes in over DRS
4946 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4948 struct ldb_context *ldb;
4949 struct ldb_request *change_req;
4950 enum ndr_err_code ndr_err;
4951 struct ldb_message *msg;
4952 struct replPropertyMetaDataBlob *md;
4953 struct ldb_val md_value;
4956 bool remote_isDeleted = false;
4959 time_t t = time(NULL);
4960 const struct ldb_val *rdn_val;
4961 struct replmd_private *replmd_private =
4962 talloc_get_type(ldb_module_get_private(ar->module),
4963 struct replmd_private);
4964 unix_to_nt_time(&now, t);
4966 ldb = ldb_module_get_ctx(ar->module);
4967 msg = ar->objs->objects[ar->index_current].msg;
4968 md = ar->objs->objects[ar->index_current].meta_data;
4969 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4971 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4972 if (ret != LDB_SUCCESS) {
4973 return replmd_replicated_request_error(ar, ret);
4976 ret = dsdb_msg_add_guid(msg,
4977 &ar->objs->objects[ar->index_current].object_guid,
4979 if (ret != LDB_SUCCESS) {
4980 return replmd_replicated_request_error(ar, ret);
4983 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4984 if (ret != LDB_SUCCESS) {
4985 return replmd_replicated_request_error(ar, ret);
4988 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4989 if (ret != LDB_SUCCESS) {
4990 return replmd_replicated_request_error(ar, ret);
4993 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4994 if (ret != LDB_SUCCESS) {
4995 return replmd_replicated_request_error(ar, ret);
4998 /* remove any message elements that have zero values */
4999 for (i=0; i<msg->num_elements; i++) {
5000 struct ldb_message_element *el = &msg->elements[i];
5002 if (el->num_values == 0) {
5003 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5004 ldb_asprintf_errstring(ldb, __location__
5005 ": empty objectClass sent on %s, aborting replication\n",
5006 ldb_dn_get_linearized(msg->dn));
5007 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5010 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5012 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5013 msg->num_elements--;
5020 struct GUID_txt_buf guid_txt;
5022 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5025 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5026 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5029 } else if (DEBUGLVL(4)) {
5030 struct GUID_txt_buf guid_txt;
5031 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5032 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5033 ldb_dn_get_linearized(msg->dn)));
5035 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5036 "isDeleted", false);
5039 * the meta data array is already sorted by the caller, except
5040 * for the RDN, which needs to be added.
5044 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5045 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5046 md, ar, now, is_schema_nc,
5048 if (ret != LDB_SUCCESS) {
5049 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5050 return replmd_replicated_request_error(ar, ret);
5053 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5054 if (ret != LDB_SUCCESS) {
5055 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5056 return replmd_replicated_request_error(ar, ret);
5059 for (i=0; i < md->ctr.ctr1.count; i++) {
5060 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5062 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5063 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5064 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5065 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5066 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5068 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5069 if (ret != LDB_SUCCESS) {
5070 return replmd_replicated_request_error(ar, ret);
5073 replmd_ldb_message_sort(msg, ar->schema);
5075 if (!remote_isDeleted) {
5076 ret = dsdb_module_schedule_sd_propagation(ar->module,
5077 ar->objs->partition_dn,
5079 if (ret != LDB_SUCCESS) {
5080 return replmd_replicated_request_error(ar, ret);
5084 ar->isDeleted = remote_isDeleted;
5086 ret = ldb_build_add_req(&change_req,
5092 replmd_op_add_callback,
5094 LDB_REQ_SET_LOCATION(change_req);
5095 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5097 /* current partition control needed by "repmd_op_callback" */
5098 ret = ldb_request_add_control(change_req,
5099 DSDB_CONTROL_CURRENT_PARTITION_OID,
5101 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5103 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5104 /* this tells the partition module to make it a
5105 partial replica if creating an NC */
5106 ret = ldb_request_add_control(change_req,
5107 DSDB_CONTROL_PARTIAL_REPLICA,
5109 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5112 return ldb_next_request(ar->module, change_req);
5115 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5116 struct ldb_reply *ares)
5118 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5119 struct replmd_replicated_request);
5123 return ldb_module_done(ar->req, NULL, NULL,
5124 LDB_ERR_OPERATIONS_ERROR);
5128 * The error NO_SUCH_OBJECT is not expected, unless the search
5129 * base is the partition DN, and that case doesn't happen here
5130 * because then we wouldn't get a parent_guid_value in any
5133 if (ares->error != LDB_SUCCESS) {
5134 return ldb_module_done(ar->req, ares->controls,
5135 ares->response, ares->error);
5138 switch (ares->type) {
5139 case LDB_REPLY_ENTRY:
5141 struct ldb_message *parent_msg = ares->message;
5142 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5143 struct ldb_dn *parent_dn;
5146 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5147 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5148 /* Per MS-DRSR 4.1.10.6.10
5149 * FindBestParentObject we need to move this
5150 * new object under a deleted object to
5152 struct ldb_dn *nc_root;
5154 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5155 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5156 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5157 "No suitable NC root found for %s. "
5158 "We need to move this object because parent object %s "
5159 "is deleted, but this object is not.",
5160 ldb_dn_get_linearized(msg->dn),
5161 ldb_dn_get_linearized(parent_msg->dn));
5162 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5163 } else if (ret != LDB_SUCCESS) {
5164 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5165 "Unable to find NC root for %s: %s. "
5166 "We need to move this object because parent object %s "
5167 "is deleted, but this object is not.",
5168 ldb_dn_get_linearized(msg->dn),
5169 ldb_errstring(ldb_module_get_ctx(ar->module)),
5170 ldb_dn_get_linearized(parent_msg->dn));
5171 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5174 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5176 DS_GUID_LOSTANDFOUND_CONTAINER,
5178 if (ret != LDB_SUCCESS) {
5179 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5180 "Unable to find LostAndFound Container for %s "
5181 "in partition %s: %s. "
5182 "We need to move this object because parent object %s "
5183 "is deleted, but this object is not.",
5184 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5185 ldb_errstring(ldb_module_get_ctx(ar->module)),
5186 ldb_dn_get_linearized(parent_msg->dn));
5187 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5189 ar->objs->objects[ar->index_current].last_known_parent
5190 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5194 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5197 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5199 comp_num = ldb_dn_get_comp_num(msg->dn);
5201 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5203 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5206 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5208 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5212 case LDB_REPLY_REFERRAL:
5213 /* we ignore referrals */
5216 case LDB_REPLY_DONE:
5218 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5219 struct GUID_txt_buf str_buf;
5220 if (ar->search_msg != NULL) {
5221 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5222 "No parent with GUID %s found for object locally known as %s",
5223 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5224 ldb_dn_get_linearized(ar->search_msg->dn));
5226 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5227 "No parent with GUID %s found for object remotely known as %s",
5228 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5229 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5233 * This error code is really important, as it
5234 * is the flag back to the callers to retry
5235 * this with DRSUAPI_DRS_GET_ANC, and so get
5236 * the parent objects before the child
5239 return ldb_module_done(ar->req, NULL, NULL,
5240 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5243 if (ar->search_msg != NULL) {
5244 ret = replmd_replicated_apply_merge(ar);
5246 ret = replmd_replicated_apply_add(ar);
5248 if (ret != LDB_SUCCESS) {
5249 return ldb_module_done(ar->req, NULL, NULL, ret);
5258 * Look for the parent object, so we put the new object in the right
5259 * place This is akin to NameObject in MS-DRSR - this routine and the
5260 * callbacks find the right parent name, and correct name for this
5264 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5266 struct ldb_context *ldb;
5270 struct ldb_request *search_req;
5271 static const char *attrs[] = {"isDeleted", NULL};
5272 struct GUID_txt_buf guid_str_buf;
5274 ldb = ldb_module_get_ctx(ar->module);
5276 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5277 if (ar->search_msg != NULL) {
5278 return replmd_replicated_apply_merge(ar);
5280 return replmd_replicated_apply_add(ar);
5284 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5287 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5288 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5290 ret = ldb_build_search_req(&search_req,
5293 ar->objs->partition_dn,
5299 replmd_replicated_apply_search_for_parent_callback,
5301 LDB_REQ_SET_LOCATION(search_req);
5303 ret = dsdb_request_add_controls(search_req,
5304 DSDB_SEARCH_SHOW_RECYCLED|
5305 DSDB_SEARCH_SHOW_DELETED|
5306 DSDB_SEARCH_SHOW_EXTENDED_DN);
5307 if (ret != LDB_SUCCESS) {
5311 return ldb_next_request(ar->module, search_req);
5315 handle renames that come in over DRS replication
5317 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5318 struct ldb_message *msg,
5319 struct ldb_request *parent,
5323 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5324 struct ldb_result *res;
5325 struct ldb_dn *conflict_dn;
5326 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5327 const struct ldb_val *omd_value;
5328 struct replPropertyMetaDataBlob omd, *rmd;
5329 enum ndr_err_code ndr_err;
5330 bool rename_incoming_record, rodc;
5331 struct replPropertyMetaData1 *rmd_name, *omd_name;
5332 struct ldb_dn *new_dn;
5335 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5336 ldb_dn_get_linearized(ar->search_msg->dn),
5337 ldb_dn_get_linearized(msg->dn)));
5340 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5341 DSDB_FLAG_NEXT_MODULE, ar->req);
5342 if (ret == LDB_SUCCESS) {
5343 talloc_free(tmp_ctx);
5348 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5349 talloc_free(tmp_ctx);
5350 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5351 ldb_dn_get_linearized(ar->search_msg->dn),
5352 ldb_dn_get_linearized(msg->dn),
5353 ldb_errstring(ldb_module_get_ctx(ar->module)));
5357 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5358 if (ret != LDB_SUCCESS) {
5359 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5360 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5361 ldb_errstring(ldb_module_get_ctx(ar->module)));
5362 return LDB_ERR_OPERATIONS_ERROR;
5365 * we have a conflict, and need to decide if we will keep the
5366 * new record or the old record
5369 conflict_dn = msg->dn;
5373 * We are on an RODC, or were a GC for this
5374 * partition, so we have to fail this until
5375 * someone who owns the partition sorts it
5378 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5379 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5380 " - We must fail the operation until a master for this partition resolves the conflict",
5381 ldb_dn_get_linearized(conflict_dn));
5386 * first we need the replPropertyMetaData attribute from the
5389 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5391 DSDB_FLAG_NEXT_MODULE |
5392 DSDB_SEARCH_SHOW_DELETED |
5393 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5394 if (ret != LDB_SUCCESS) {
5395 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5396 ldb_dn_get_linearized(conflict_dn)));
5400 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5401 if (omd_value == NULL) {
5402 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5403 ldb_dn_get_linearized(conflict_dn)));
5407 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5408 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5409 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5410 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5411 ldb_dn_get_linearized(conflict_dn)));
5415 rmd = ar->objs->objects[ar->index_current].meta_data;
5418 * we decide which is newer based on the RPMD on the name
5419 * attribute. See [MS-DRSR] ResolveNameConflict.
5421 * We expect omd_name to be present, as this is from a local
5422 * search, but while rmd_name should have been given to us by
5423 * the remote server, if it is missing we just prefer the
5425 * replmd_replPropertyMetaData1_new_should_be_taken()
5427 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5428 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5430 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5431 ldb_dn_get_linearized(conflict_dn)));
5436 * Should we preserve the current record, and so rename the
5437 * incoming record to be a conflict?
5439 rename_incoming_record =
5440 !replmd_replPropertyMetaData1_new_should_be_taken(
5441 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5442 omd_name, rmd_name);
5444 if (rename_incoming_record) {
5446 new_dn = replmd_conflict_dn(msg, msg->dn,
5447 &ar->objs->objects[ar->index_current].object_guid);
5448 if (new_dn == NULL) {
5449 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5450 "Failed to form conflict DN for %s\n",
5451 ldb_dn_get_linearized(msg->dn));
5453 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5456 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5457 DSDB_FLAG_NEXT_MODULE, ar->req);
5458 if (ret != LDB_SUCCESS) {
5459 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5460 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5461 ldb_dn_get_linearized(conflict_dn),
5462 ldb_dn_get_linearized(ar->search_msg->dn),
5463 ldb_dn_get_linearized(new_dn),
5464 ldb_errstring(ldb_module_get_ctx(ar->module)));
5465 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5473 /* we are renaming the existing record */
5475 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5476 if (GUID_all_zero(&guid)) {
5477 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5478 ldb_dn_get_linearized(conflict_dn)));
5482 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
5483 if (new_dn == NULL) {
5484 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5485 ldb_dn_get_linearized(conflict_dn)));
5489 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5490 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5492 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5493 DSDB_FLAG_OWN_MODULE, ar->req);
5494 if (ret != LDB_SUCCESS) {
5495 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5496 ldb_dn_get_linearized(conflict_dn),
5497 ldb_dn_get_linearized(new_dn),
5498 ldb_errstring(ldb_module_get_ctx(ar->module))));
5503 * now we need to ensure that the rename is seen as an
5504 * originating update. We do that with a modify.
5506 ret = replmd_name_modify(ar, ar->req, new_dn);
5507 if (ret != LDB_SUCCESS) {
5511 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5512 ldb_dn_get_linearized(ar->search_msg->dn),
5513 ldb_dn_get_linearized(msg->dn)));
5516 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5517 DSDB_FLAG_NEXT_MODULE, ar->req);
5518 if (ret != LDB_SUCCESS) {
5519 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5520 ldb_dn_get_linearized(ar->search_msg->dn),
5521 ldb_dn_get_linearized(msg->dn),
5522 ldb_errstring(ldb_module_get_ctx(ar->module))));
5528 * On failure make the caller get the error
5529 * This means replication will stop with an error,
5530 * but there is not much else we can do. In the
5531 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5535 talloc_free(tmp_ctx);
5540 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5542 struct ldb_context *ldb;
5543 struct ldb_request *change_req;
5544 enum ndr_err_code ndr_err;
5545 struct ldb_message *msg;
5546 struct replPropertyMetaDataBlob *rmd;
5547 struct replPropertyMetaDataBlob omd;
5548 const struct ldb_val *omd_value;
5549 struct replPropertyMetaDataBlob nmd;
5550 struct ldb_val nmd_value;
5551 struct GUID remote_parent_guid;
5554 unsigned int removed_attrs = 0;
5556 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5557 bool isDeleted = false;
5558 bool local_isDeleted = false;
5559 bool remote_isDeleted = false;
5560 bool take_remote_isDeleted = false;
5561 bool sd_updated = false;
5562 bool renamed = false;
5563 bool is_schema_nc = false;
5565 const struct ldb_val *old_rdn, *new_rdn;
5566 struct replmd_private *replmd_private =
5567 talloc_get_type(ldb_module_get_private(ar->module),
5568 struct replmd_private);
5570 time_t t = time(NULL);
5571 unix_to_nt_time(&now, t);
5573 ldb = ldb_module_get_ctx(ar->module);
5574 msg = ar->objs->objects[ar->index_current].msg;
5576 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5578 rmd = ar->objs->objects[ar->index_current].meta_data;
5582 /* find existing meta data */
5583 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5585 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5586 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5587 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5588 nt_status = ndr_map_error2ntstatus(ndr_err);
5589 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5592 if (omd.version != 1) {
5593 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5598 struct GUID_txt_buf guid_txt;
5600 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5601 LDB_CHANGETYPE_MODIFY, msg);
5602 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5605 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5607 ndr_print_struct_string(s,
5608 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5609 "existing replPropertyMetaData",
5611 ndr_print_struct_string(s,
5612 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5613 "incoming replPropertyMetaData",
5616 } else if (DEBUGLVL(4)) {
5617 struct GUID_txt_buf guid_txt;
5619 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5620 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5622 ldb_dn_get_linearized(msg->dn)));
5625 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5626 "isDeleted", false);
5627 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5628 "isDeleted", false);
5631 * Fill in the remote_parent_guid with the GUID or an all-zero
5634 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5635 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5637 remote_parent_guid = GUID_zero();
5641 * To ensure we follow a complex rename chain around, we have
5642 * to confirm that the DN is the same (mostly to confirm the
5643 * RDN) and the parentGUID is the same.
5645 * This ensures we keep things under the correct parent, which
5646 * replmd_replicated_handle_rename() will do.
5649 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5650 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5654 * handle renames, even just by case that come in over
5655 * DRS. Changes in the parent DN don't hit us here,
5656 * because the search for a parent will clean up those
5659 * We also have already filtered out the case where
5660 * the peer has an older name to what we have (see
5661 * replmd_replicated_apply_search_callback())
5663 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5666 if (ret != LDB_SUCCESS) {
5667 ldb_debug(ldb, LDB_DEBUG_FATAL,
5668 "replmd_replicated_request rename %s => %s failed - %s\n",
5669 ldb_dn_get_linearized(ar->search_msg->dn),
5670 ldb_dn_get_linearized(msg->dn),
5671 ldb_errstring(ldb));
5672 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5675 if (renamed == true) {
5677 * Set the callback to one that will fix up the name
5678 * metadata on the new conflict DN
5680 callback = replmd_op_name_modify_callback;
5685 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5686 nmd.ctr.ctr1.array = talloc_array(ar,
5687 struct replPropertyMetaData1,
5688 nmd.ctr.ctr1.count);
5689 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5691 /* first copy the old meta data */
5692 for (i=0; i < omd.ctr.ctr1.count; i++) {
5693 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5698 /* now merge in the new meta data */
5699 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5702 for (j=0; j < ni; j++) {
5705 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5709 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5710 ar->objs->dsdb_repl_flags,
5711 &nmd.ctr.ctr1.array[j],
5712 &rmd->ctr.ctr1.array[i]);
5714 /* replace the entry */
5715 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5716 if (ar->seq_num == 0) {
5717 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5718 if (ret != LDB_SUCCESS) {
5719 return replmd_replicated_request_error(ar, ret);
5722 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5723 switch (nmd.ctr.ctr1.array[j].attid) {
5724 case DRSUAPI_ATTID_ntSecurityDescriptor:
5727 case DRSUAPI_ATTID_isDeleted:
5728 take_remote_isDeleted = true;
5737 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5738 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5739 msg->elements[i-removed_attrs].name,
5740 ldb_dn_get_linearized(msg->dn),
5741 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5744 /* we don't want to apply this change so remove the attribute */
5745 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5752 if (found) continue;
5754 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5755 if (ar->seq_num == 0) {
5756 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5757 if (ret != LDB_SUCCESS) {
5758 return replmd_replicated_request_error(ar, ret);
5761 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5762 switch (nmd.ctr.ctr1.array[ni].attid) {
5763 case DRSUAPI_ATTID_ntSecurityDescriptor:
5766 case DRSUAPI_ATTID_isDeleted:
5767 take_remote_isDeleted = true;
5776 * finally correct the size of the meta_data array
5778 nmd.ctr.ctr1.count = ni;
5780 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5781 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5784 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5785 &nmd, ar, now, is_schema_nc,
5787 if (ret != LDB_SUCCESS) {
5788 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5789 return replmd_replicated_request_error(ar, ret);
5793 * sort the new meta data array
5795 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5796 if (ret != LDB_SUCCESS) {
5797 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5802 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5805 * This also controls SD propagation below
5807 if (take_remote_isDeleted) {
5808 isDeleted = remote_isDeleted;
5810 isDeleted = local_isDeleted;
5813 ar->isDeleted = isDeleted;
5816 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5818 if (msg->num_elements == 0) {
5819 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5822 return replmd_replicated_apply_isDeleted(ar);
5825 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5826 ar->index_current, msg->num_elements);
5832 if (sd_updated && !isDeleted) {
5833 ret = dsdb_module_schedule_sd_propagation(ar->module,
5834 ar->objs->partition_dn,
5836 if (ret != LDB_SUCCESS) {
5837 return ldb_operr(ldb);
5841 /* create the meta data value */
5842 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5843 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5844 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5845 nt_status = ndr_map_error2ntstatus(ndr_err);
5846 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5850 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5851 * and replPopertyMetaData attributes
5853 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5854 if (ret != LDB_SUCCESS) {
5855 return replmd_replicated_request_error(ar, ret);
5857 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5858 if (ret != LDB_SUCCESS) {
5859 return replmd_replicated_request_error(ar, ret);
5861 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5862 if (ret != LDB_SUCCESS) {
5863 return replmd_replicated_request_error(ar, ret);
5866 replmd_ldb_message_sort(msg, ar->schema);
5868 /* we want to replace the old values */
5869 for (i=0; i < msg->num_elements; i++) {
5870 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5871 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5872 if (msg->elements[i].num_values == 0) {
5873 ldb_asprintf_errstring(ldb, __location__
5874 ": objectClass removed on %s, aborting replication\n",
5875 ldb_dn_get_linearized(msg->dn));
5876 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5882 struct GUID_txt_buf guid_txt;
5884 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5885 LDB_CHANGETYPE_MODIFY,
5887 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
5888 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5892 } else if (DEBUGLVL(4)) {
5893 struct GUID_txt_buf guid_txt;
5895 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
5896 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5898 ldb_dn_get_linearized(msg->dn)));
5901 ret = ldb_build_mod_req(&change_req,
5909 LDB_REQ_SET_LOCATION(change_req);
5910 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5912 /* current partition control needed by "repmd_op_callback" */
5913 ret = ldb_request_add_control(change_req,
5914 DSDB_CONTROL_CURRENT_PARTITION_OID,
5916 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5918 return ldb_next_request(ar->module, change_req);
5921 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5922 struct ldb_reply *ares)
5924 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5925 struct replmd_replicated_request);
5929 return ldb_module_done(ar->req, NULL, NULL,
5930 LDB_ERR_OPERATIONS_ERROR);
5932 if (ares->error != LDB_SUCCESS &&
5933 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5934 return ldb_module_done(ar->req, ares->controls,
5935 ares->response, ares->error);
5938 switch (ares->type) {
5939 case LDB_REPLY_ENTRY:
5940 ar->search_msg = talloc_steal(ar, ares->message);
5943 case LDB_REPLY_REFERRAL:
5944 /* we ignore referrals */
5947 case LDB_REPLY_DONE:
5949 struct replPropertyMetaData1 *md_remote;
5950 struct replPropertyMetaData1 *md_local;
5952 struct replPropertyMetaDataBlob omd;
5953 const struct ldb_val *omd_value;
5954 struct replPropertyMetaDataBlob *rmd;
5955 struct ldb_message *msg;
5957 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5958 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5961 * This is the ADD case, find the appropriate parent,
5962 * as this object doesn't exist locally:
5964 if (ar->search_msg == NULL) {
5965 ret = replmd_replicated_apply_search_for_parent(ar);
5966 if (ret != LDB_SUCCESS) {
5967 return ldb_module_done(ar->req, NULL, NULL, ret);
5974 * Otherwise, in the MERGE case, work out if we are
5975 * attempting a rename, and if so find the parent the
5976 * newly renamed object wants to belong under (which
5977 * may not be the parent in it's attached string DN
5979 rmd = ar->objs->objects[ar->index_current].meta_data;
5983 /* find existing meta data */
5984 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5986 enum ndr_err_code ndr_err;
5987 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5988 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5989 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5990 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5991 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5994 if (omd.version != 1) {
5995 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5999 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6001 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6002 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6003 && GUID_all_zero(&ar->local_parent_guid)) {
6004 DEBUG(0, ("Refusing to replicate new version of %s "
6005 "as local object has an all-zero parentGUID attribute, "
6006 "despite not being an NC root\n",
6007 ldb_dn_get_linearized(ar->search_msg->dn)));
6008 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6012 * now we need to check for double renames. We could have a
6013 * local rename pending which our replication partner hasn't
6014 * received yet. We choose which one wins by looking at the
6015 * attribute stamps on the two objects, the newer one wins.
6017 * This also simply applies the correct algorithms for
6018 * determining if a change was made to name at all, or
6019 * if the object has just been renamed under the same
6022 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6023 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6025 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6026 ldb_dn_get_linearized(ar->search_msg->dn)));
6027 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6031 * if there is no name attribute given then we have to assume the
6032 * object we've received has the older name
6034 if (replmd_replPropertyMetaData1_new_should_be_taken(
6035 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6036 md_local, md_remote)) {
6037 struct GUID_txt_buf p_guid_local;
6038 struct GUID_txt_buf p_guid_remote;
6039 msg = ar->objs->objects[ar->index_current].msg;
6041 /* Merge on the existing object, with rename */
6043 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6044 "as incoming object changing to %s under %s\n",
6045 ldb_dn_get_linearized(ar->search_msg->dn),
6046 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6047 ldb_dn_get_linearized(msg->dn),
6048 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6050 ret = replmd_replicated_apply_search_for_parent(ar);
6052 struct GUID_txt_buf p_guid_local;
6053 struct GUID_txt_buf p_guid_remote;
6054 msg = ar->objs->objects[ar->index_current].msg;
6057 * Merge on the existing object, force no
6058 * rename (code below just to explain why in
6062 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6063 ldb_dn_get_linearized(msg->dn)) == 0) {
6064 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6065 GUID_equal(&ar->local_parent_guid,
6066 ar->objs->objects[ar->index_current].parent_guid)
6068 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6069 "despite incoming object changing parent to %s\n",
6070 ldb_dn_get_linearized(ar->search_msg->dn),
6071 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6072 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6076 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6077 " and rejecting older rename to %s under %s\n",
6078 ldb_dn_get_linearized(ar->search_msg->dn),
6079 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6080 ldb_dn_get_linearized(msg->dn),
6081 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6085 * This assignment ensures that the strcmp()
6086 * and GUID_equal() calls in
6087 * replmd_replicated_apply_merge() avoids the
6090 ar->objs->objects[ar->index_current].parent_guid =
6091 &ar->local_parent_guid;
6093 msg->dn = ar->search_msg->dn;
6094 ret = replmd_replicated_apply_merge(ar);
6096 if (ret != LDB_SUCCESS) {
6097 return ldb_module_done(ar->req, NULL, NULL, ret);
6107 * Stores the linked attributes received in the replication chunk - these get
6108 * applied at the end of the transaction. We also check that each linked
6109 * attribute is valid, i.e. source and target objects are known.
6111 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6113 int ret = LDB_SUCCESS;
6115 struct ldb_module *module = ar->module;
6116 struct replmd_private *replmd_private =
6117 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6118 struct ldb_context *ldb;
6120 ldb = ldb_module_get_ctx(module);
6122 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6124 /* save away the linked attributes for the end of the transaction */
6125 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6126 struct la_entry *la_entry;
6128 if (replmd_private->la_ctx == NULL) {
6129 replmd_private->la_ctx = talloc_new(replmd_private);
6131 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6132 if (la_entry == NULL) {
6134 return LDB_ERR_OPERATIONS_ERROR;
6136 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6137 if (la_entry->la == NULL) {
6138 talloc_free(la_entry);
6140 return LDB_ERR_OPERATIONS_ERROR;
6142 *la_entry->la = ar->objs->linked_attributes[i];
6143 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6145 /* we need to steal the non-scalars so they stay
6146 around until the end of the transaction */
6147 talloc_steal(la_entry->la, la_entry->la->identifier);
6148 talloc_steal(la_entry->la, la_entry->la->value.blob);
6150 ret = replmd_verify_linked_attribute(ar, la_entry);
6152 if (ret != LDB_SUCCESS) {
6156 DLIST_ADD(replmd_private->la_list, la_entry);
6162 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6164 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6166 struct ldb_context *ldb;
6170 struct ldb_request *search_req;
6171 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6172 "parentGUID", "instanceType",
6173 "replPropertyMetaData", "nTSecurityDescriptor",
6174 "isDeleted", NULL };
6175 struct GUID_txt_buf guid_str_buf;
6177 if (ar->index_current >= ar->objs->num_objects) {
6180 * Now that we've applied all the objects, check the new linked
6181 * attributes and store them (we apply them in .prepare_commit)
6183 ret = replmd_store_linked_attributes(ar);
6185 if (ret != LDB_SUCCESS) {
6189 /* done applying objects, move on to the next stage */
6190 return replmd_replicated_uptodate_vector(ar);
6193 ldb = ldb_module_get_ctx(ar->module);
6194 ar->search_msg = NULL;
6195 ar->isDeleted = false;
6197 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6200 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6201 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6203 ret = ldb_build_search_req(&search_req,
6206 ar->objs->partition_dn,
6212 replmd_replicated_apply_search_callback,
6214 LDB_REQ_SET_LOCATION(search_req);
6216 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6218 if (ret != LDB_SUCCESS) {
6222 return ldb_next_request(ar->module, search_req);
6226 * This is essentially a wrapper for replmd_replicated_apply_next()
6228 * This is needed to ensure that both codepaths call this handler.
6230 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6232 struct ldb_dn *deleted_objects_dn;
6233 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6234 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6235 &deleted_objects_dn);
6236 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6238 * Do a delete here again, so that if there is
6239 * anything local that conflicts with this
6240 * object being deleted, it is removed. This
6241 * includes links. See MS-DRSR 4.1.10.6.9
6244 * If the object is already deleted, and there
6245 * is no more work required, it doesn't do
6249 /* This has been updated to point to the DN we eventually did the modify on */
6251 struct ldb_request *del_req;
6252 struct ldb_result *res;
6254 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6256 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6260 res = talloc_zero(tmp_ctx, struct ldb_result);
6262 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6263 talloc_free(tmp_ctx);
6267 /* Build a delete request, which hopefully will artually turn into nothing */
6268 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6272 ldb_modify_default_callback,
6274 LDB_REQ_SET_LOCATION(del_req);
6275 if (ret != LDB_SUCCESS) {
6276 talloc_free(tmp_ctx);
6281 * This is the guts of the call, call back
6282 * into our delete code, but setting the
6283 * re_delete flag so we delete anything that
6284 * shouldn't be there on a deleted or recycled
6287 ret = replmd_delete_internals(ar->module, del_req, true);
6288 if (ret == LDB_SUCCESS) {
6289 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6292 talloc_free(tmp_ctx);
6293 if (ret != LDB_SUCCESS) {
6298 ar->index_current++;
6299 return replmd_replicated_apply_next(ar);
6302 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6303 struct ldb_reply *ares)
6305 struct ldb_context *ldb;
6306 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6307 struct replmd_replicated_request);
6308 ldb = ldb_module_get_ctx(ar->module);
6311 return ldb_module_done(ar->req, NULL, NULL,
6312 LDB_ERR_OPERATIONS_ERROR);
6314 if (ares->error != LDB_SUCCESS) {
6315 return ldb_module_done(ar->req, ares->controls,
6316 ares->response, ares->error);
6319 if (ares->type != LDB_REPLY_DONE) {
6320 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6321 return ldb_module_done(ar->req, NULL, NULL,
6322 LDB_ERR_OPERATIONS_ERROR);
6327 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6330 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6332 struct ldb_context *ldb;
6333 struct ldb_request *change_req;
6334 enum ndr_err_code ndr_err;
6335 struct ldb_message *msg;
6336 struct replUpToDateVectorBlob ouv;
6337 const struct ldb_val *ouv_value;
6338 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6339 struct replUpToDateVectorBlob nuv;
6340 struct ldb_val nuv_value;
6341 struct ldb_message_element *nuv_el = NULL;
6342 struct ldb_message_element *orf_el = NULL;
6343 struct repsFromToBlob nrf;
6344 struct ldb_val *nrf_value = NULL;
6345 struct ldb_message_element *nrf_el = NULL;
6349 time_t t = time(NULL);
6352 uint32_t instanceType;
6354 ldb = ldb_module_get_ctx(ar->module);
6355 ruv = ar->objs->uptodateness_vector;
6361 unix_to_nt_time(&now, t);
6363 if (ar->search_msg == NULL) {
6364 /* this happens for a REPL_OBJ call where we are
6365 creating the target object by replicating it. The
6366 subdomain join code does this for the partition DN
6368 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6369 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6372 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6373 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6374 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6375 ldb_dn_get_linearized(ar->search_msg->dn)));
6376 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6380 * first create the new replUpToDateVector
6382 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6384 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6385 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6386 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6387 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6388 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6391 if (ouv.version != 2) {
6392 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6397 * the new uptodateness vector will at least
6398 * contain 1 entry, one for the source_dsa
6400 * plus optional values from our old vector and the one from the source_dsa
6402 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6403 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6404 nuv.ctr.ctr2.cursors = talloc_array(ar,
6405 struct drsuapi_DsReplicaCursor2,
6406 nuv.ctr.ctr2.count);
6407 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6409 /* first copy the old vector */
6410 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6411 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6415 /* merge in the source_dsa vector is available */
6416 for (i=0; (ruv && i < ruv->count); i++) {
6419 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6420 &ar->our_invocation_id)) {
6424 for (j=0; j < ni; j++) {
6425 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6426 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6432 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6433 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6438 if (found) continue;
6440 /* if it's not there yet, add it */
6441 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6446 * finally correct the size of the cursors array
6448 nuv.ctr.ctr2.count = ni;
6453 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6456 * create the change ldb_message
6458 msg = ldb_msg_new(ar);
6459 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6460 msg->dn = ar->search_msg->dn;
6462 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6463 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6464 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6465 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6466 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6468 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6469 if (ret != LDB_SUCCESS) {
6470 return replmd_replicated_request_error(ar, ret);
6472 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6475 * now create the new repsFrom value from the given repsFromTo1 structure
6479 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6480 nrf.ctr.ctr1.last_attempt = now;
6481 nrf.ctr.ctr1.last_success = now;
6482 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6485 * first see if we already have a repsFrom value for the current source dsa
6486 * if so we'll later replace this value
6488 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6490 for (i=0; i < orf_el->num_values; i++) {
6491 struct repsFromToBlob *trf;
6493 trf = talloc(ar, struct repsFromToBlob);
6494 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6496 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6497 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6498 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6499 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6500 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6503 if (trf->version != 1) {
6504 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6508 * we compare the source dsa objectGUID not the invocation_id
6509 * because we want only one repsFrom value per source dsa
6510 * and when the invocation_id of the source dsa has changed we don't need
6511 * the old repsFrom with the old invocation_id
6513 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6514 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6520 nrf_value = &orf_el->values[i];
6525 * copy over all old values to the new ldb_message
6527 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6528 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6533 * if we haven't found an old repsFrom value for the current source dsa
6534 * we'll add a new value
6537 struct ldb_val zero_value;
6538 ZERO_STRUCT(zero_value);
6539 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6540 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6542 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6545 /* we now fill the value which is already attached to ldb_message */
6546 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6548 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6549 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6550 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6551 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6555 * the ldb_message_element for the attribute, has all the old values and the new one
6556 * so we'll replace the whole attribute with all values
6558 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6560 if (CHECK_DEBUGLVL(4)) {
6561 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6562 LDB_CHANGETYPE_MODIFY,
6564 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6568 /* prepare the ldb_modify() request */
6569 ret = ldb_build_mod_req(&change_req,
6575 replmd_replicated_uptodate_modify_callback,
6577 LDB_REQ_SET_LOCATION(change_req);
6578 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6580 return ldb_next_request(ar->module, change_req);
6583 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6584 struct ldb_reply *ares)
6586 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6587 struct replmd_replicated_request);
6591 return ldb_module_done(ar->req, NULL, NULL,
6592 LDB_ERR_OPERATIONS_ERROR);
6594 if (ares->error != LDB_SUCCESS &&
6595 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6596 return ldb_module_done(ar->req, ares->controls,
6597 ares->response, ares->error);
6600 switch (ares->type) {
6601 case LDB_REPLY_ENTRY:
6602 ar->search_msg = talloc_steal(ar, ares->message);
6605 case LDB_REPLY_REFERRAL:
6606 /* we ignore referrals */
6609 case LDB_REPLY_DONE:
6610 ret = replmd_replicated_uptodate_modify(ar);
6611 if (ret != LDB_SUCCESS) {
6612 return ldb_module_done(ar->req, NULL, NULL, ret);
6621 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6623 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6624 struct replmd_private *replmd_private =
6625 talloc_get_type_abort(ldb_module_get_private(ar->module),
6626 struct replmd_private);
6628 static const char *attrs[] = {
6629 "replUpToDateVector",
6634 struct ldb_request *search_req;
6636 ar->search_msg = NULL;
6639 * Let the caller know that we did an originating updates
6641 ar->objs->originating_updates = replmd_private->originating_updates;
6643 ret = ldb_build_search_req(&search_req,
6646 ar->objs->partition_dn,
6652 replmd_replicated_uptodate_search_callback,
6654 LDB_REQ_SET_LOCATION(search_req);
6655 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6657 return ldb_next_request(ar->module, search_req);
6662 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6664 struct ldb_context *ldb;
6665 struct dsdb_extended_replicated_objects *objs;
6666 struct replmd_replicated_request *ar;
6667 struct ldb_control **ctrls;
6670 ldb = ldb_module_get_ctx(module);
6672 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6674 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6676 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6677 return LDB_ERR_PROTOCOL_ERROR;
6680 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6681 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6682 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6683 return LDB_ERR_PROTOCOL_ERROR;
6686 ar = replmd_ctx_init(module, req);
6688 return LDB_ERR_OPERATIONS_ERROR;
6690 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6691 ar->apply_mode = true;
6693 ar->schema = dsdb_get_schema(ldb, ar);
6695 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6697 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6698 return LDB_ERR_CONSTRAINT_VIOLATION;
6701 ctrls = req->controls;
6703 if (req->controls) {
6704 req->controls = talloc_memdup(ar, req->controls,
6705 talloc_get_size(req->controls));
6706 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6709 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6710 if (ret != LDB_SUCCESS) {
6714 /* If this change contained linked attributes in the body
6715 * (rather than in the links section) we need to update
6716 * backlinks in linked_attributes */
6717 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6718 if (ret != LDB_SUCCESS) {
6722 ar->controls = req->controls;
6723 req->controls = ctrls;
6725 return replmd_replicated_apply_next(ar);
6729 * Checks that the target object for a linked attribute exists.
6730 * @param guid returns the target object's GUID (is returned)if it exists)
6731 * @param ignore_link set to true if the linked attribute should be ignored
6732 * (i.e. the target doesn't exist, but that it's OK to skip the link)
6734 static int replmd_check_target_exists(struct ldb_module *module,
6735 struct dsdb_dn *dsdb_dn,
6736 struct la_entry *la_entry,
6737 struct ldb_dn *source_dn,
6741 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6742 struct ldb_context *ldb = ldb_module_get_ctx(module);
6743 struct ldb_result *target_res;
6744 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6745 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
6748 enum deletion_state target_deletion_state = OBJECT_REMOVED;
6749 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
6751 *ignore_link = false;
6752 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
6754 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6757 * This strange behaviour (allowing a NULL/missing
6758 * GUID) originally comes from:
6760 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6761 * Author: Andrew Tridgell <tridge@samba.org>
6762 * Date: Mon Dec 21 21:21:55 2009 +1100
6764 * s4-drs: cope better with NULL GUIDS from DRS
6766 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6767 * need to match by DN if possible when seeing if we should update an
6770 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6772 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6774 DSDB_FLAG_NEXT_MODULE |
6775 DSDB_SEARCH_SHOW_RECYCLED |
6776 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6777 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6779 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6780 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
6782 ldb_dn_get_linearized(dsdb_dn->dn),
6783 ldb_dn_get_linearized(source_dn));
6784 talloc_free(tmp_ctx);
6785 return LDB_ERR_OPERATIONS_ERROR;
6787 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6788 NULL, LDB_SCOPE_SUBTREE,
6790 DSDB_FLAG_NEXT_MODULE |
6791 DSDB_SEARCH_SHOW_RECYCLED |
6792 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6793 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6796 GUID_string(tmp_ctx, guid));
6799 if (ret != LDB_SUCCESS) {
6800 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
6801 GUID_string(tmp_ctx, guid),
6802 ldb_errstring(ldb));
6803 talloc_free(tmp_ctx);
6807 if (target_res->count == 0) {
6810 * we may not be able to resolve link targets properly when
6811 * dealing with subsets of objects, e.g. the source is a
6812 * critical object and the target isn't
6815 * When we implement Trusted Domains we need to consider
6816 * whether they get treated as an incomplete replica here or not
6818 if (la_entry->dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
6821 * We don't increase the highwater-mark in the object
6822 * subset cases, so subsequent replications should
6823 * resolve any missing links
6825 DEBUG(2,(__location__
6826 ": Failed to find target %s linked from %s\n",
6827 ldb_dn_get_linearized(dsdb_dn->dn),
6828 ldb_dn_get_linearized(source_dn)));
6829 *ignore_link = true;
6831 } else if (dsdb_objects_have_same_nc(ldb, tmp_ctx, source_dn,
6833 ldb_asprintf_errstring(ldb, "Unknown target %s GUID %s linked from %s\n",
6834 ldb_dn_get_linearized(dsdb_dn->dn),
6835 GUID_string(tmp_ctx, guid),
6836 ldb_dn_get_linearized(source_dn));
6837 ret = LDB_ERR_NO_SUCH_OBJECT;
6841 * The target of the cross-partition link is missing.
6842 * Continue and try to at least add the forward-link.
6843 * This isn't great, but if we can add a partial link
6844 * then it's better than nothing.
6846 DEBUG(2,("Failed to resolve cross-partition link between %s and %s\n",
6847 ldb_dn_get_linearized(source_dn),
6848 ldb_dn_get_linearized(dsdb_dn->dn)));
6850 } else if (target_res->count != 1) {
6851 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
6852 GUID_string(tmp_ctx, guid));
6853 ret = LDB_ERR_OPERATIONS_ERROR;
6855 struct ldb_message *target_msg = target_res->msgs[0];
6857 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6859 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
6860 replmd_deletion_state(module, target_msg,
6861 &target_deletion_state, NULL);
6864 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
6865 * ProcessLinkValue(). Link updates should not be sent for
6866 * recycled and tombstone objects (deleting the links should
6867 * happen when we delete the object). This probably means our
6868 * copy of the target object isn't up to date.
6870 if (target_deletion_state >= OBJECT_RECYCLED) {
6872 if (la_entry->dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
6875 * target should already be uptodate so there's no
6876 * point retrying - it's probably just bad timing
6878 *ignore_link = true;
6879 DEBUG(0, ("%s is deleted but up to date. "
6880 "Ignoring link from %s\n",
6881 ldb_dn_get_linearized(dsdb_dn->dn),
6882 ldb_dn_get_linearized(source_dn)));
6885 ldb_asprintf_errstring(ldb,
6886 "Deleted target %s GUID %s linked from %s",
6887 ldb_dn_get_linearized(dsdb_dn->dn),
6888 GUID_string(tmp_ctx, guid),
6889 ldb_dn_get_linearized(source_dn));
6890 ret = LDB_ERR_NO_SUCH_OBJECT;
6895 talloc_free(tmp_ctx);
6900 * Extracts the key details about the source/target object for a
6901 * linked-attribute entry.
6902 * This returns the following details:
6903 * @param ret_attr the schema details for the linked attribute
6904 * @param source_msg the search result for the source object
6905 * @param target_dsdb_dn the unpacked DN info for the target object
6907 static int replmd_extract_la_entry_details(struct ldb_module *module,
6908 struct la_entry *la_entry,
6909 TALLOC_CTX *mem_ctx,
6910 const struct dsdb_attribute **ret_attr,
6911 struct ldb_message **source_msg,
6912 struct dsdb_dn **target_dsdb_dn)
6914 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6915 struct ldb_context *ldb = ldb_module_get_ctx(module);
6916 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
6918 const struct dsdb_attribute *attr;
6920 struct ldb_result *res;
6921 const char *attrs[4];
6924 linked_attributes[0]:
6925 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6927 identifier: struct drsuapi_DsReplicaObjectIdentifier
6928 __ndr_size : 0x0000003a (58)
6929 __ndr_size_sid : 0x00000000 (0)
6930 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6932 __ndr_size_dn : 0x00000000 (0)
6934 attid : DRSUAPI_ATTID_member (0x1F)
6935 value: struct drsuapi_DsAttributeValue
6936 __ndr_size : 0x0000007e (126)
6938 blob : DATA_BLOB length=126
6939 flags : 0x00000001 (1)
6940 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6941 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6942 meta_data: struct drsuapi_DsReplicaMetaData
6943 version : 0x00000015 (21)
6944 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6945 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6946 originating_usn : 0x000000000001e19c (123292)
6948 (for cases where the link is to a normal DN)
6949 &target: struct drsuapi_DsReplicaObjectIdentifier3
6950 __ndr_size : 0x0000007e (126)
6951 __ndr_size_sid : 0x0000001c (28)
6952 guid : 7639e594-db75-4086-b0d4-67890ae46031
6953 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6954 __ndr_size_dn : 0x00000022 (34)
6955 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6958 /* find the attribute being modified */
6959 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6961 struct GUID_txt_buf guid_str;
6962 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6964 GUID_buf_string(&la->identifier->guid,
6966 return LDB_ERR_OPERATIONS_ERROR;
6969 attrs[0] = attr->lDAPDisplayName;
6970 attrs[1] = "isDeleted";
6971 attrs[2] = "isRecycled";
6975 * get the existing message from the db for the object with
6976 * this GUID, returning attribute being modified. We will then
6977 * use this msg as the basis for a modify call
6979 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6980 DSDB_FLAG_NEXT_MODULE |
6981 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6982 DSDB_SEARCH_SHOW_RECYCLED |
6983 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6984 DSDB_SEARCH_REVEAL_INTERNALS,
6986 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
6987 if (ret != LDB_SUCCESS) {
6990 if (res->count != 1) {
6991 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6992 GUID_string(mem_ctx, &la->identifier->guid));
6993 return LDB_ERR_NO_SUCH_OBJECT;
6996 *source_msg = res->msgs[0];
6998 /* the value blob for the attribute holds the target object DN */
6999 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7000 if (!W_ERROR_IS_OK(status)) {
7001 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7002 attr->lDAPDisplayName,
7003 ldb_dn_get_linearized(res->msgs[0]->dn),
7004 win_errstr(status));
7005 return LDB_ERR_OPERATIONS_ERROR;
7014 * Verifies the source and target objects are known for a linked attribute
7016 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7017 struct la_entry *la)
7019 int ret = LDB_SUCCESS;
7020 TALLOC_CTX *tmp_ctx = talloc_new(la);
7021 struct ldb_module *module = ar->module;
7022 struct ldb_message *src_msg;
7023 const struct dsdb_attribute *attr;
7024 struct dsdb_dn *tgt_dsdb_dn;
7025 struct GUID guid = GUID_zero();
7028 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7029 &src_msg, &tgt_dsdb_dn);
7032 * When we fail to find the source object, the error code we pass
7033 * back here is really important. It flags back to the callers to
7034 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7035 * never happen if we're replicating from a Samba DC, but it is
7036 * needed to talk to a Windows DC
7038 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7039 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7042 if (ret != LDB_SUCCESS) {
7043 talloc_free(tmp_ctx);
7047 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7048 src_msg->dn, &guid, &dummy);
7051 * When we fail to find the target object, the error code we pass
7052 * back here is really important. It flags back to the callers to
7053 * retry this request with DRSUAPI_DRS_GET_TGT
7055 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7056 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7059 talloc_free(tmp_ctx);
7064 process one linked attribute structure
7066 static int replmd_process_linked_attribute(struct ldb_module *module,
7067 struct replmd_private *replmd_private,
7068 struct la_entry *la_entry,
7069 struct ldb_request *parent)
7071 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7072 struct ldb_context *ldb = ldb_module_get_ctx(module);
7073 struct ldb_message *msg;
7074 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7075 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7077 const struct dsdb_attribute *attr;
7078 struct dsdb_dn *dsdb_dn;
7079 uint64_t seq_num = 0;
7080 struct ldb_message_element *old_el;
7081 time_t t = time(NULL);
7082 struct parsed_dn *pdn_list, *pdn, *next;
7083 struct GUID guid = GUID_zero();
7084 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7086 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7089 * get the attribute being modified, the search result for the source object,
7090 * and the target object's DN details
7092 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7095 if (ret != LDB_SUCCESS) {
7096 talloc_free(tmp_ctx);
7101 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7102 * ProcessLinkValue, because link updates are not applied to
7103 * recycled and tombstone objects. We don't have to delete
7104 * any existing link, that should have happened when the
7105 * object deletion was replicated or initiated.
7107 replmd_deletion_state(module, msg, &deletion_state, NULL);
7109 if (deletion_state >= OBJECT_RECYCLED) {
7110 talloc_free(tmp_ctx);
7114 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7115 if (old_el == NULL) {
7116 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7117 if (ret != LDB_SUCCESS) {
7118 ldb_module_oom(module);
7119 talloc_free(tmp_ctx);
7120 return LDB_ERR_OPERATIONS_ERROR;
7123 old_el->flags = LDB_FLAG_MOD_REPLACE;
7126 /* parse the existing links */
7127 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7128 attr->syntax->ldap_oid, parent);
7130 if (ret != LDB_SUCCESS) {
7131 talloc_free(tmp_ctx);
7135 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7136 &guid, &ignore_link);
7138 if (ret != LDB_SUCCESS) {
7139 talloc_free(tmp_ctx);
7144 * there are some cases where the target object doesn't exist, but it's
7145 * OK to ignore the linked attribute
7148 talloc_free(tmp_ctx);
7152 /* see if this link already exists */
7153 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7156 dsdb_dn->extra_part, 0,
7158 attr->syntax->ldap_oid,
7160 if (ret != LDB_SUCCESS) {
7161 talloc_free(tmp_ctx);
7167 /* see if this update is newer than what we have already */
7168 struct GUID invocation_id = GUID_zero();
7169 uint32_t version = 0;
7170 uint32_t originating_usn = 0;
7171 NTTIME change_time = 0;
7172 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7174 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7175 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7176 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
7177 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7179 if (!replmd_update_is_newer(&invocation_id,
7180 &la->meta_data.originating_invocation_id,
7182 la->meta_data.version,
7184 la->meta_data.originating_change_time)) {
7185 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7186 old_el->name, ldb_dn_get_linearized(msg->dn),
7187 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7188 talloc_free(tmp_ctx);
7192 /* get a seq_num for this change */
7193 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7194 if (ret != LDB_SUCCESS) {
7195 talloc_free(tmp_ctx);
7199 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7200 /* remove the existing backlink */
7201 ret = replmd_add_backlink(module, replmd_private,
7206 if (ret != LDB_SUCCESS) {
7207 talloc_free(tmp_ctx);
7212 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
7213 &la->meta_data.originating_invocation_id,
7214 la->meta_data.originating_usn, seq_num,
7215 la->meta_data.originating_change_time,
7216 la->meta_data.version,
7218 if (ret != LDB_SUCCESS) {
7219 talloc_free(tmp_ctx);
7224 /* add the new backlink */
7225 ret = replmd_add_backlink(module, replmd_private,
7230 if (ret != LDB_SUCCESS) {
7231 talloc_free(tmp_ctx);
7237 /* get a seq_num for this change */
7238 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7239 if (ret != LDB_SUCCESS) {
7240 talloc_free(tmp_ctx);
7244 * We know where the new one needs to be, from the *next
7245 * pointer into pdn_list.
7248 offset = old_el->num_values;
7250 if (next->dsdb_dn == NULL) {
7251 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7252 attr->syntax->ldap_oid);
7253 if (ret != LDB_SUCCESS) {
7257 offset = next - pdn_list;
7258 if (offset > old_el->num_values) {
7259 talloc_free(tmp_ctx);
7260 return LDB_ERR_OPERATIONS_ERROR;
7264 old_el->values = talloc_realloc(msg->elements, old_el->values,
7265 struct ldb_val, old_el->num_values+1);
7266 if (!old_el->values) {
7267 ldb_module_oom(module);
7268 return LDB_ERR_OPERATIONS_ERROR;
7271 if (offset != old_el->num_values) {
7272 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7273 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7276 old_el->num_values++;
7278 ret = replmd_build_la_val(tmp_ctx, &old_el->values[offset], dsdb_dn,
7279 &la->meta_data.originating_invocation_id,
7280 la->meta_data.originating_usn, seq_num,
7281 la->meta_data.originating_change_time,
7282 la->meta_data.version,
7284 if (ret != LDB_SUCCESS) {
7285 talloc_free(tmp_ctx);
7290 ret = replmd_add_backlink(module, replmd_private,
7295 if (ret != LDB_SUCCESS) {
7296 talloc_free(tmp_ctx);
7302 /* we only change whenChanged and uSNChanged if the seq_num
7304 ret = add_time_element(msg, "whenChanged", t);
7305 if (ret != LDB_SUCCESS) {
7306 talloc_free(tmp_ctx);
7311 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7312 if (ret != LDB_SUCCESS) {
7313 talloc_free(tmp_ctx);
7318 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7319 if (old_el == NULL) {
7320 talloc_free(tmp_ctx);
7321 return ldb_operr(ldb);
7324 ret = dsdb_check_single_valued_link(attr, old_el);
7325 if (ret != LDB_SUCCESS) {
7326 talloc_free(tmp_ctx);
7330 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7332 ret = linked_attr_modify(module, msg, parent);
7333 if (ret != LDB_SUCCESS) {
7334 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7336 ldb_ldif_message_redacted_string(ldb,
7338 LDB_CHANGETYPE_MODIFY,
7340 talloc_free(tmp_ctx);
7344 talloc_free(tmp_ctx);
7349 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7351 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7352 return replmd_extended_replicated_objects(module, req);
7355 return ldb_next_request(module, req);
7360 we hook into the transaction operations to allow us to
7361 perform the linked attribute updates at the end of the whole
7362 transaction. This allows a forward linked attribute to be created
7363 before the object is created. During a vampire, w2k8 sends us linked
7364 attributes before the objects they are part of.
7366 static int replmd_start_transaction(struct ldb_module *module)
7368 /* create our private structure for this transaction */
7369 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7370 struct replmd_private);
7371 replmd_txn_cleanup(replmd_private);
7373 /* free any leftover mod_usn records from cancelled
7375 while (replmd_private->ncs) {
7376 struct nc_entry *e = replmd_private->ncs;
7377 DLIST_REMOVE(replmd_private->ncs, e);
7381 replmd_private->originating_updates = false;
7383 return ldb_next_start_trans(module);
7387 on prepare commit we loop over our queued la_context structures and
7390 static int replmd_prepare_commit(struct ldb_module *module)
7392 struct replmd_private *replmd_private =
7393 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7394 struct la_entry *la, *prev;
7398 * Walk the list of linked attributes from DRS replication.
7400 * We walk backwards, to do the first entry first, as we
7401 * added the entries with DLIST_ADD() which puts them at the
7404 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7405 prev = DLIST_PREV(la);
7406 DLIST_REMOVE(replmd_private->la_list, la);
7407 ret = replmd_process_linked_attribute(module, replmd_private,
7409 if (ret != LDB_SUCCESS) {
7410 replmd_txn_cleanup(replmd_private);
7415 replmd_txn_cleanup(replmd_private);
7417 /* possibly change @REPLCHANGED */
7418 ret = replmd_notify_store(module, NULL);
7419 if (ret != LDB_SUCCESS) {
7423 return ldb_next_prepare_commit(module);
7426 static int replmd_del_transaction(struct ldb_module *module)
7428 struct replmd_private *replmd_private =
7429 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7430 replmd_txn_cleanup(replmd_private);
7432 return ldb_next_del_trans(module);
7436 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7437 .name = "repl_meta_data",
7438 .init_context = replmd_init,
7440 .modify = replmd_modify,
7441 .rename = replmd_rename,
7442 .del = replmd_delete,
7443 .extended = replmd_extended,
7444 .start_transaction = replmd_start_transaction,
7445 .prepare_commit = replmd_prepare_commit,
7446 .del_transaction = replmd_del_transaction,
7449 int ldb_repl_meta_data_module_init(const char *version)
7451 LDB_MODULE_CHECK_VERSION(version);
7452 return ldb_register_module(&ldb_repl_meta_data_module_ops);