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 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
56 * Deleted Objects Container
58 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
60 struct replmd_private {
62 struct la_entry *la_list;
64 struct nc_entry *prev, *next;
67 uint64_t mod_usn_urgent;
69 struct ldb_dn *schema_dn;
70 bool originating_updates;
75 struct la_entry *next, *prev;
76 struct drsuapi_DsReplicaLinkedAttribute *la;
79 struct replmd_replicated_request {
80 struct ldb_module *module;
81 struct ldb_request *req;
83 const struct dsdb_schema *schema;
84 struct GUID our_invocation_id;
86 /* the controls we pass down */
87 struct ldb_control **controls;
90 * Backlinks for the replmd_add() case (we want to create
91 * backlinks after creating the user, but before the end of
94 struct la_backlink *la_backlinks;
96 /* details for the mode where we apply a bunch of inbound replication meessages */
98 uint32_t index_current;
99 struct dsdb_extended_replicated_objects *objs;
101 struct ldb_message *search_msg;
102 struct GUID local_parent_guid;
110 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
111 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
112 static int replmd_check_upgrade_links(struct ldb_context *ldb,
113 struct parsed_dn *dns, uint32_t count,
114 struct ldb_message_element *el,
115 const char *ldap_oid);
117 enum urgent_situation {
118 REPL_URGENT_ON_CREATE = 1,
119 REPL_URGENT_ON_UPDATE = 2,
120 REPL_URGENT_ON_DELETE = 4
123 enum deletion_state {
124 OBJECT_NOT_DELETED=1,
131 static void replmd_deletion_state(struct ldb_module *module,
132 const struct ldb_message *msg,
133 enum deletion_state *current_state,
134 enum deletion_state *next_state)
137 bool enabled = false;
140 *current_state = OBJECT_REMOVED;
141 if (next_state != NULL) {
142 *next_state = OBJECT_REMOVED;
147 ret = dsdb_recyclebin_enabled(module, &enabled);
148 if (ret != LDB_SUCCESS) {
152 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
154 *current_state = OBJECT_TOMBSTONE;
155 if (next_state != NULL) {
156 *next_state = OBJECT_REMOVED;
161 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
162 *current_state = OBJECT_RECYCLED;
163 if (next_state != NULL) {
164 *next_state = OBJECT_REMOVED;
169 *current_state = OBJECT_DELETED;
170 if (next_state != NULL) {
171 *next_state = OBJECT_RECYCLED;
176 *current_state = OBJECT_NOT_DELETED;
177 if (next_state == NULL) {
182 *next_state = OBJECT_DELETED;
184 *next_state = OBJECT_TOMBSTONE;
188 static const struct {
189 const char *update_name;
190 enum urgent_situation repl_situation;
191 } urgent_objects[] = {
192 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
193 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
194 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
195 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
196 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
197 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
201 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
202 static const char *urgent_attrs[] = {
205 "userAccountControl",
210 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
211 enum urgent_situation situation)
214 for (i=0; urgent_objects[i].update_name; i++) {
216 if ((situation & urgent_objects[i].repl_situation) == 0) {
220 for (j=0; j<objectclass_el->num_values; j++) {
221 const struct ldb_val *v = &objectclass_el->values[j];
222 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
230 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
232 if (ldb_attr_in_list(urgent_attrs, el->name)) {
238 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
241 initialise the module
242 allocate the private structure and build the list
243 of partition DNs for use by replmd_notify()
245 static int replmd_init(struct ldb_module *module)
247 struct replmd_private *replmd_private;
248 struct ldb_context *ldb = ldb_module_get_ctx(module);
249 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
250 struct ldb_dn *samba_dsdb_dn;
251 struct ldb_result *res;
253 TALLOC_CTX *frame = talloc_stackframe();
254 replmd_private = talloc_zero(module, struct replmd_private);
255 if (replmd_private == NULL) {
258 return LDB_ERR_OPERATIONS_ERROR;
260 ldb_module_set_private(module, replmd_private);
262 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
264 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
265 if (!samba_dsdb_dn) {
270 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
271 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
272 if (ret == LDB_SUCCESS) {
273 replmd_private->sorted_links
274 = ldb_msg_check_string_attribute(res->msgs[0],
275 SAMBA_COMPATIBLE_FEATURES_ATTR,
276 SAMBA_SORTED_LINKS_FEATURE);
280 return ldb_next_init(module);
284 cleanup our per-transaction contexts
286 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
288 talloc_free(replmd_private->la_ctx);
289 replmd_private->la_list = NULL;
290 replmd_private->la_ctx = NULL;
296 struct la_backlink *next, *prev;
297 const char *attr_name;
298 struct ldb_dn *forward_dn;
299 struct GUID target_guid;
304 a ldb_modify request operating on modules below the
307 static int linked_attr_modify(struct ldb_module *module,
308 const struct ldb_message *message,
309 struct ldb_request *parent)
311 struct ldb_request *mod_req;
313 struct ldb_context *ldb = ldb_module_get_ctx(module);
314 TALLOC_CTX *tmp_ctx = talloc_new(module);
315 struct ldb_result *res;
317 res = talloc_zero(tmp_ctx, struct ldb_result);
319 talloc_free(tmp_ctx);
320 return ldb_oom(ldb_module_get_ctx(module));
323 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
327 ldb_modify_default_callback,
329 LDB_REQ_SET_LOCATION(mod_req);
330 if (ret != LDB_SUCCESS) {
331 talloc_free(tmp_ctx);
335 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
337 if (ret != LDB_SUCCESS) {
341 /* Run the new request */
342 ret = ldb_next_request(module, mod_req);
344 if (ret == LDB_SUCCESS) {
345 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
348 talloc_free(tmp_ctx);
353 process a backlinks we accumulated during a transaction, adding and
354 deleting the backlinks from the target objects
356 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
358 struct ldb_dn *target_dn, *source_dn;
360 struct ldb_context *ldb = ldb_module_get_ctx(module);
361 struct ldb_message *msg;
362 TALLOC_CTX *frame = talloc_stackframe();
368 - construct ldb_message
369 - either an add or a delete
371 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
372 if (ret != LDB_SUCCESS) {
373 struct GUID_txt_buf guid_str;
374 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
375 GUID_buf_string(&bl->target_guid, &guid_str)));
380 msg = ldb_msg_new(frame);
382 ldb_module_oom(module);
384 return LDB_ERR_OPERATIONS_ERROR;
387 source_dn = ldb_dn_copy(frame, bl->forward_dn);
389 ldb_module_oom(module);
391 return LDB_ERR_OPERATIONS_ERROR;
393 /* Filter down to the attributes we want in the backlink */
394 const char *accept[] = { "GUID", "SID", NULL };
395 ldb_dn_extended_filter(source_dn, accept);
398 /* construct a ldb_message for adding/deleting the backlink */
400 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
402 ldb_module_oom(module);
404 return LDB_ERR_OPERATIONS_ERROR;
406 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
407 if (ret != LDB_SUCCESS) {
411 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
413 /* a backlink should never be single valued. Unfortunately the
414 exchange schema has a attribute
415 msExchBridgeheadedLocalConnectorsDNBL which is single
416 valued and a backlink. We need to cope with that by
417 ignoring the single value flag */
418 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
420 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
421 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
422 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
423 cope with possible corruption where the backlink has
424 already been removed */
425 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
426 ldb_dn_get_linearized(target_dn),
427 ldb_dn_get_linearized(source_dn),
428 ldb_errstring(ldb)));
430 } else if (ret != LDB_SUCCESS) {
431 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
432 bl->active?"add":"remove",
433 ldb_dn_get_linearized(source_dn),
434 ldb_dn_get_linearized(target_dn),
444 add a backlink to the list of backlinks to add/delete in the prepare
447 forward_dn is stolen onto the defereed context
449 static int replmd_defer_add_backlink(struct ldb_module *module,
450 struct replmd_private *replmd_private,
451 const struct dsdb_schema *schema,
452 struct replmd_replicated_request *ac,
453 struct ldb_dn *forward_dn,
454 struct GUID *target_guid, bool active,
455 const struct dsdb_attribute *schema_attr,
456 struct ldb_request *parent)
458 const struct dsdb_attribute *target_attr;
459 struct la_backlink *bl;
461 bl = talloc(ac, struct la_backlink);
463 ldb_module_oom(module);
464 return LDB_ERR_OPERATIONS_ERROR;
467 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
470 * windows 2003 has a broken schema where the
471 * definition of msDS-IsDomainFor is missing (which is
472 * supposed to be the backlink of the
473 * msDS-HasDomainNCs attribute
478 bl->attr_name = target_attr->lDAPDisplayName;
479 bl->forward_dn = talloc_steal(bl, forward_dn);
480 bl->target_guid = *target_guid;
483 DLIST_ADD(ac->la_backlinks, bl);
489 add a backlink to the list of backlinks to add/delete in the prepare
492 static int replmd_add_backlink(struct ldb_module *module,
493 struct replmd_private *replmd_private,
494 const struct dsdb_schema *schema,
495 struct ldb_dn *forward_dn,
496 struct GUID *target_guid, bool active,
497 const struct dsdb_attribute *schema_attr,
498 struct ldb_request *parent)
500 const struct dsdb_attribute *target_attr;
501 struct la_backlink bl;
504 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
507 * windows 2003 has a broken schema where the
508 * definition of msDS-IsDomainFor is missing (which is
509 * supposed to be the backlink of the
510 * msDS-HasDomainNCs attribute
515 bl.attr_name = target_attr->lDAPDisplayName;
516 bl.forward_dn = forward_dn;
517 bl.target_guid = *target_guid;
520 ret = replmd_process_backlink(module, &bl, parent);
526 * Callback for most write operations in this module:
528 * notify the repl task that a object has changed. The notifies are
529 * gathered up in the replmd_private structure then written to the
530 * @REPLCHANGED object in each partition during the prepare_commit
532 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
535 struct replmd_replicated_request *ac =
536 talloc_get_type_abort(req->context, struct replmd_replicated_request);
537 struct replmd_private *replmd_private =
538 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
539 struct nc_entry *modified_partition;
540 struct ldb_control *partition_ctrl;
541 const struct dsdb_control_current_partition *partition;
543 struct ldb_control **controls;
545 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
547 controls = ares->controls;
548 if (ldb_request_get_control(ac->req,
549 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
551 * Remove the current partition control from what we pass up
552 * the chain if it hasn't been requested manually.
554 controls = ldb_controls_except_specified(ares->controls, ares,
558 if (ares->error != LDB_SUCCESS) {
559 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
560 return ldb_module_done(ac->req, controls,
561 ares->response, ares->error);
564 if (ares->type != LDB_REPLY_DONE) {
565 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
566 return ldb_module_done(ac->req, NULL,
567 NULL, LDB_ERR_OPERATIONS_ERROR);
570 if (ac->apply_mode == false) {
571 struct la_backlink *bl;
573 * process our backlink list after an replmd_add(),
574 * creating and deleting backlinks as necessary (this
575 * code is sync). The other cases are handled inline
578 for (bl=ac->la_backlinks; bl; bl=bl->next) {
579 ret = replmd_process_backlink(ac->module, bl, ac->req);
580 if (ret != LDB_SUCCESS) {
581 return ldb_module_done(ac->req, NULL,
587 if (!partition_ctrl) {
588 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
589 return ldb_module_done(ac->req, NULL,
590 NULL, LDB_ERR_OPERATIONS_ERROR);
593 partition = talloc_get_type_abort(partition_ctrl->data,
594 struct dsdb_control_current_partition);
596 if (ac->seq_num > 0) {
597 for (modified_partition = replmd_private->ncs; modified_partition;
598 modified_partition = modified_partition->next) {
599 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
604 if (modified_partition == NULL) {
605 modified_partition = talloc_zero(replmd_private, struct nc_entry);
606 if (!modified_partition) {
607 ldb_oom(ldb_module_get_ctx(ac->module));
608 return ldb_module_done(ac->req, NULL,
609 NULL, LDB_ERR_OPERATIONS_ERROR);
611 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
612 if (!modified_partition->dn) {
613 ldb_oom(ldb_module_get_ctx(ac->module));
614 return ldb_module_done(ac->req, NULL,
615 NULL, LDB_ERR_OPERATIONS_ERROR);
617 DLIST_ADD(replmd_private->ncs, modified_partition);
620 if (ac->seq_num > modified_partition->mod_usn) {
621 modified_partition->mod_usn = ac->seq_num;
623 modified_partition->mod_usn_urgent = ac->seq_num;
626 if (!ac->apply_mode) {
627 replmd_private->originating_updates = true;
631 if (ac->apply_mode) {
632 ret = replmd_replicated_apply_isDeleted(ac);
633 if (ret != LDB_SUCCESS) {
634 return ldb_module_done(ac->req, NULL, NULL, ret);
638 /* free the partition control container here, for the
639 * common path. Other cases will have it cleaned up
640 * eventually with the ares */
641 talloc_free(partition_ctrl);
642 return ldb_module_done(ac->req, controls,
643 ares->response, LDB_SUCCESS);
649 * update a @REPLCHANGED record in each partition if there have been
650 * any writes of replicated data in the partition
652 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
654 struct replmd_private *replmd_private =
655 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
657 while (replmd_private->ncs) {
659 struct nc_entry *modified_partition = replmd_private->ncs;
661 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
662 modified_partition->mod_usn,
663 modified_partition->mod_usn_urgent, parent);
664 if (ret != LDB_SUCCESS) {
665 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
666 ldb_dn_get_linearized(modified_partition->dn)));
670 if (ldb_dn_compare(modified_partition->dn,
671 replmd_private->schema_dn) == 0) {
672 struct ldb_result *ext_res;
673 ret = dsdb_module_extended(module,
674 replmd_private->schema_dn,
676 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
678 DSDB_FLAG_NEXT_MODULE,
680 if (ret != LDB_SUCCESS) {
683 talloc_free(ext_res);
686 DLIST_REMOVE(replmd_private->ncs, modified_partition);
687 talloc_free(modified_partition);
695 created a replmd_replicated_request context
697 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
698 struct ldb_request *req)
700 struct ldb_context *ldb;
701 struct replmd_replicated_request *ac;
702 const struct GUID *our_invocation_id;
704 ldb = ldb_module_get_ctx(module);
706 ac = talloc_zero(req, struct replmd_replicated_request);
715 ac->schema = dsdb_get_schema(ldb, ac);
717 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
718 "replmd_modify: no dsdb_schema loaded");
719 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
724 /* get our invocationId */
725 our_invocation_id = samdb_ntds_invocation_id(ldb);
726 if (!our_invocation_id) {
727 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
728 "replmd_add: unable to find invocationId\n");
732 ac->our_invocation_id = *our_invocation_id;
738 add a time element to a record
740 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
742 struct ldb_message_element *el;
746 if (ldb_msg_find_element(msg, attr) != NULL) {
750 s = ldb_timestring(msg, t);
752 return LDB_ERR_OPERATIONS_ERROR;
755 ret = ldb_msg_add_string(msg, attr, s);
756 if (ret != LDB_SUCCESS) {
760 el = ldb_msg_find_element(msg, attr);
761 /* always set as replace. This works because on add ops, the flag
763 el->flags = LDB_FLAG_MOD_REPLACE;
769 add a uint64_t element to a record
771 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
772 const char *attr, uint64_t v)
774 struct ldb_message_element *el;
777 if (ldb_msg_find_element(msg, attr) != NULL) {
781 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
782 if (ret != LDB_SUCCESS) {
786 el = ldb_msg_find_element(msg, attr);
787 /* always set as replace. This works because on add ops, the flag
789 el->flags = LDB_FLAG_MOD_REPLACE;
794 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
795 const struct replPropertyMetaData1 *m2,
796 const uint32_t *rdn_attid)
799 * This assignment seems inoccous, but it is critical for the
800 * system, as we need to do the comparisons as a unsigned
801 * quantity, not signed (enums are signed integers)
803 uint32_t attid_1 = m1->attid;
804 uint32_t attid_2 = m2->attid;
806 if (attid_1 == attid_2) {
811 * See above regarding this being an unsigned comparison.
812 * Otherwise when the high bit is set on non-standard
813 * attributes, they would end up first, before objectClass
816 return attid_1 > attid_2 ? 1 : -1;
819 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
820 struct replPropertyMetaDataCtr1 *ctr1,
823 if (ctr1->count == 0) {
824 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
825 "No elements found in replPropertyMetaData for %s!\n",
826 ldb_dn_get_linearized(dn));
827 return LDB_ERR_CONSTRAINT_VIOLATION;
830 /* the objectClass attribute is value 0x00000000, so must be first */
831 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
832 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
833 "No objectClass found in replPropertyMetaData for %s!\n",
834 ldb_dn_get_linearized(dn));
835 return LDB_ERR_OBJECT_CLASS_VIOLATION;
841 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
842 struct replPropertyMetaDataCtr1 *ctr1,
845 /* Note this is O(n^2) for the almost-sorted case, which this is */
846 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
847 replmd_replPropertyMetaData1_attid_sort);
848 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
851 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
852 const struct ldb_message_element *e2,
853 const struct dsdb_schema *schema)
855 const struct dsdb_attribute *a1;
856 const struct dsdb_attribute *a2;
859 * TODO: make this faster by caching the dsdb_attribute pointer
860 * on the ldb_messag_element
863 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
864 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
867 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
871 return strcasecmp(e1->name, e2->name);
873 if (a1->attributeID_id == a2->attributeID_id) {
876 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
879 static void replmd_ldb_message_sort(struct ldb_message *msg,
880 const struct dsdb_schema *schema)
882 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
885 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
886 const struct GUID *invocation_id, uint64_t seq_num,
887 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
889 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
891 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
892 struct ldb_message_element *el, struct parsed_dn **pdn,
893 const char *ldap_oid, struct ldb_request *parent);
896 fix up linked attributes in replmd_add.
897 This involves setting up the right meta-data in extended DN
898 components, and creating backlinks to the object
900 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
901 struct replmd_private *replmd_private,
902 struct ldb_message_element *el,
903 struct replmd_replicated_request *ac,
905 struct ldb_dn *forward_dn,
906 const struct dsdb_attribute *sa,
907 struct ldb_request *parent)
910 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
911 struct ldb_context *ldb = ldb_module_get_ctx(module);
912 struct parsed_dn *pdn;
913 /* We will take a reference to the schema in replmd_add_backlink */
914 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
915 struct ldb_val *new_values = NULL;
918 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
919 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
921 ldb_asprintf_errstring(ldb,
922 "Attribute %s is single valued but "
923 "more than one value has been supplied",
925 talloc_free(tmp_ctx);
926 return LDB_ERR_CONSTRAINT_VIOLATION;
929 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
930 sa->syntax->ldap_oid, parent);
931 if (ret != LDB_SUCCESS) {
932 talloc_free(tmp_ctx);
936 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
937 if (new_values == NULL) {
938 ldb_module_oom(module);
939 talloc_free(tmp_ctx);
940 return LDB_ERR_OPERATIONS_ERROR;
943 for (i = 0; i < el->num_values; i++) {
944 struct parsed_dn *p = &pdn[i];
945 if (i > 0 && parsed_dn_compare(p, &pdn[i - 1]) == 0) {
946 ldb_asprintf_errstring(ldb,
947 "Linked attribute %s has "
948 "multiple identical values", el->name);
949 talloc_free(tmp_ctx);
950 if (ldb_attr_cmp(el->name, "member") == 0) {
951 return LDB_ERR_ENTRY_ALREADY_EXISTS;
953 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
956 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
957 &ac->our_invocation_id,
958 ac->seq_num, ac->seq_num, now, 0, false);
959 if (ret != LDB_SUCCESS) {
960 talloc_free(tmp_ctx);
964 ret = replmd_defer_add_backlink(module, replmd_private,
966 forward_dn, &p->guid, true, sa,
968 if (ret != LDB_SUCCESS) {
969 talloc_free(tmp_ctx);
973 new_values[i] = *p->v;
975 el->values = talloc_steal(mem_ctx, new_values);
977 talloc_free(tmp_ctx);
981 static int replmd_add_make_extended_dn(struct ldb_request *req,
982 const DATA_BLOB *guid_blob,
983 struct ldb_dn **_extended_dn)
986 const DATA_BLOB *sid_blob;
987 /* Calculate an extended DN for any linked attributes */
988 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
990 return LDB_ERR_OPERATIONS_ERROR;
992 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
993 if (ret != LDB_SUCCESS) {
997 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
998 if (sid_blob != NULL) {
999 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1000 if (ret != LDB_SUCCESS) {
1004 *_extended_dn = extended_dn;
1009 intercept add requests
1011 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1013 struct ldb_context *ldb;
1014 struct ldb_control *control;
1015 struct replmd_replicated_request *ac;
1016 enum ndr_err_code ndr_err;
1017 struct ldb_request *down_req;
1018 struct ldb_message *msg;
1019 const DATA_BLOB *guid_blob;
1020 DATA_BLOB guid_blob_stack;
1022 uint8_t guid_data[16];
1023 struct replPropertyMetaDataBlob nmd;
1024 struct ldb_val nmd_value;
1025 struct ldb_dn *extended_dn = NULL;
1028 * The use of a time_t here seems odd, but as the NTTIME
1029 * elements are actually declared as NTTIME_1sec in the IDL,
1030 * getting a higher resolution timestamp is not required.
1032 time_t t = time(NULL);
1037 unsigned int functional_level;
1039 bool allow_add_guid = false;
1040 bool remove_current_guid = false;
1041 bool is_urgent = false;
1042 bool is_schema_nc = false;
1043 struct ldb_message_element *objectclass_el;
1044 struct replmd_private *replmd_private =
1045 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1047 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1048 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1050 allow_add_guid = true;
1053 /* do not manipulate our control entries */
1054 if (ldb_dn_is_special(req->op.add.message->dn)) {
1055 return ldb_next_request(module, req);
1058 ldb = ldb_module_get_ctx(module);
1060 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1062 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1063 if (guid_blob != NULL) {
1064 if (!allow_add_guid) {
1065 ldb_set_errstring(ldb,
1066 "replmd_add: it's not allowed to add an object with objectGUID!");
1067 return LDB_ERR_UNWILLING_TO_PERFORM;
1069 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1070 if (!NT_STATUS_IS_OK(status)) {
1071 ldb_set_errstring(ldb,
1072 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1073 return LDB_ERR_UNWILLING_TO_PERFORM;
1075 /* we remove this attribute as it can be a string and
1076 * will not be treated correctly and then we will re-add
1077 * it later on in the good format */
1078 remove_current_guid = true;
1082 guid = GUID_random();
1084 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1086 /* This can't fail */
1087 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1088 (ndr_push_flags_fn_t)ndr_push_GUID);
1089 guid_blob = &guid_blob_stack;
1092 ac = replmd_ctx_init(module, req);
1094 return ldb_module_oom(module);
1097 functional_level = dsdb_functional_level(ldb);
1099 /* Get a sequence number from the backend */
1100 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1101 if (ret != LDB_SUCCESS) {
1106 /* we have to copy the message as the caller might have it as a const */
1107 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1111 return LDB_ERR_OPERATIONS_ERROR;
1114 /* generated times */
1115 unix_to_nt_time(&now, t);
1116 time_str = ldb_timestring(msg, t);
1120 return LDB_ERR_OPERATIONS_ERROR;
1122 if (remove_current_guid) {
1123 ldb_msg_remove_attr(msg,"objectGUID");
1127 * remove autogenerated attributes
1129 ldb_msg_remove_attr(msg, "whenCreated");
1130 ldb_msg_remove_attr(msg, "whenChanged");
1131 ldb_msg_remove_attr(msg, "uSNCreated");
1132 ldb_msg_remove_attr(msg, "uSNChanged");
1133 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1136 * readd replicated attributes
1138 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1139 if (ret != LDB_SUCCESS) {
1145 /* build the replication meta_data */
1148 nmd.ctr.ctr1.count = msg->num_elements;
1149 nmd.ctr.ctr1.array = talloc_array(msg,
1150 struct replPropertyMetaData1,
1151 nmd.ctr.ctr1.count);
1152 if (!nmd.ctr.ctr1.array) {
1155 return LDB_ERR_OPERATIONS_ERROR;
1158 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1160 for (i=0; i < msg->num_elements;) {
1161 struct ldb_message_element *e = &msg->elements[i];
1162 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1163 const struct dsdb_attribute *sa;
1165 if (e->name[0] == '@') {
1170 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1172 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1173 "replmd_add: attribute '%s' not defined in schema\n",
1176 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1179 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1180 /* if the attribute is not replicated (0x00000001)
1181 * or constructed (0x00000004) it has no metadata
1187 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1188 if (extended_dn == NULL) {
1189 ret = replmd_add_make_extended_dn(req,
1192 if (ret != LDB_SUCCESS) {
1199 * Prepare the context for the backlinks and
1200 * create metadata for the forward links. The
1201 * backlinks are created in
1202 * replmd_op_callback() after the successful
1203 * ADD of the object.
1205 ret = replmd_add_fix_la(module, msg->elements,
1210 if (ret != LDB_SUCCESS) {
1214 /* linked attributes are not stored in
1215 replPropertyMetaData in FL above w2k */
1220 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1222 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1223 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1226 if (rdn_val == NULL) {
1229 return LDB_ERR_OPERATIONS_ERROR;
1232 rdn = (const char*)rdn_val->data;
1233 if (strcmp(rdn, "Deleted Objects") == 0) {
1235 * Set the originating_change_time to 29/12/9999 at 23:59:59
1236 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1238 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1240 m->originating_change_time = now;
1243 m->originating_change_time = now;
1245 m->originating_invocation_id = ac->our_invocation_id;
1246 m->originating_usn = ac->seq_num;
1247 m->local_usn = ac->seq_num;
1250 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1255 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1257 if (e->num_values != 0) {
1262 ldb_msg_remove_element(msg, e);
1265 /* fix meta data count */
1266 nmd.ctr.ctr1.count = ni;
1269 * sort meta data array
1271 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1272 if (ret != LDB_SUCCESS) {
1273 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1278 /* generated NDR encoded values */
1279 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1281 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1282 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1285 return LDB_ERR_OPERATIONS_ERROR;
1289 * add the autogenerated values
1291 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1292 if (ret != LDB_SUCCESS) {
1297 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1298 if (ret != LDB_SUCCESS) {
1303 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1304 if (ret != LDB_SUCCESS) {
1309 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1310 if (ret != LDB_SUCCESS) {
1315 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1316 if (ret != LDB_SUCCESS) {
1323 * sort the attributes by attid before storing the object
1325 replmd_ldb_message_sort(msg, ac->schema);
1328 * Assert that we do have an objectClass
1330 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1331 if (objectclass_el == NULL) {
1332 ldb_asprintf_errstring(ldb, __location__
1333 ": objectClass missing on %s\n",
1334 ldb_dn_get_linearized(msg->dn));
1336 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1338 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1339 REPL_URGENT_ON_CREATE);
1341 ac->is_urgent = is_urgent;
1342 ret = ldb_build_add_req(&down_req, ldb, ac,
1345 ac, replmd_op_callback,
1348 LDB_REQ_SET_LOCATION(down_req);
1349 if (ret != LDB_SUCCESS) {
1354 /* current partition control is needed by "replmd_op_callback" */
1355 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1356 ret = ldb_request_add_control(down_req,
1357 DSDB_CONTROL_CURRENT_PARTITION_OID,
1359 if (ret != LDB_SUCCESS) {
1365 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1366 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1367 if (ret != LDB_SUCCESS) {
1373 /* mark the control done */
1375 control->critical = 0;
1377 /* go on with the call chain */
1378 return ldb_next_request(module, down_req);
1383 * update the replPropertyMetaData for one element
1385 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1386 struct ldb_message *msg,
1387 struct ldb_message_element *el,
1388 struct ldb_message_element *old_el,
1389 struct replPropertyMetaDataBlob *omd,
1390 const struct dsdb_schema *schema,
1392 const struct GUID *our_invocation_id,
1395 bool is_forced_rodc,
1396 struct ldb_request *req)
1399 const struct dsdb_attribute *a;
1400 struct replPropertyMetaData1 *md1;
1401 bool may_skip = false;
1404 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1406 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1407 /* allow this to make it possible for dbcheck
1408 to remove bad attributes */
1412 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1414 return LDB_ERR_OPERATIONS_ERROR;
1417 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1419 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1424 * if the attribute's value haven't changed, and this isn't
1425 * just a delete of everything then return LDB_SUCCESS Unless
1426 * we have the provision control or if the attribute is
1427 * interSiteTopologyGenerator as this page explain:
1428 * http://support.microsoft.com/kb/224815 this attribute is
1429 * periodicaly written by the DC responsible for the intersite
1430 * generation in a given site
1432 * Unchanged could be deleting or replacing an already-gone
1433 * thing with an unconstrained delete/empty replace or a
1434 * replace with the same value, but not an add with the same
1435 * value because that could be about adding a duplicate (which
1436 * is for someone else to error out on).
1438 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1439 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1442 } else if (old_el == NULL && el->num_values == 0) {
1443 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1445 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1448 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1449 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1451 * We intentionally skip the version bump when attempting to
1454 * The control is set by dbcheck and expunge-tombstones which
1455 * both attempt to be non-replicating. Otherwise, making an
1456 * alteration to the replication state would trigger a
1457 * broadcast of all expunged objects.
1462 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1464 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1468 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1469 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1471 * allow this to make it possible for dbcheck
1472 * to rebuild broken metadata
1478 for (i=0; i<omd->ctr.ctr1.count; i++) {
1480 * First check if we find it under the msDS-IntID,
1481 * then check if we find it under the OID and
1484 * This allows the administrator to simply re-write
1485 * the attributes and so restore replication, which is
1486 * likely what they will try to do.
1488 if (attid == omd->ctr.ctr1.array[i].attid) {
1492 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1497 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1498 /* linked attributes are not stored in
1499 replPropertyMetaData in FL above w2k, but we do
1500 raise the seqnum for the object */
1501 if (*seq_num == 0 &&
1502 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1503 return LDB_ERR_OPERATIONS_ERROR;
1508 if (i == omd->ctr.ctr1.count) {
1509 /* we need to add a new one */
1510 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1511 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1512 if (omd->ctr.ctr1.array == NULL) {
1514 return LDB_ERR_OPERATIONS_ERROR;
1516 omd->ctr.ctr1.count++;
1517 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1520 /* Get a new sequence number from the backend. We only do this
1521 * if we have a change that requires a new
1522 * replPropertyMetaData element
1524 if (*seq_num == 0) {
1525 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1526 if (ret != LDB_SUCCESS) {
1527 return LDB_ERR_OPERATIONS_ERROR;
1531 md1 = &omd->ctr.ctr1.array[i];
1535 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1536 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1539 if (rdn_val == NULL) {
1541 return LDB_ERR_OPERATIONS_ERROR;
1544 rdn = (const char*)rdn_val->data;
1545 if (strcmp(rdn, "Deleted Objects") == 0) {
1547 * Set the originating_change_time to 29/12/9999 at 23:59:59
1548 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1550 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1552 md1->originating_change_time = now;
1555 md1->originating_change_time = now;
1557 md1->originating_invocation_id = *our_invocation_id;
1558 md1->originating_usn = *seq_num;
1559 md1->local_usn = *seq_num;
1561 if (is_forced_rodc) {
1562 /* Force version to 0 to be overriden later via replication */
1570 * Bump the replPropertyMetaData version on an attribute, and if it
1571 * has changed (or forced by leaving rdn_old NULL), update the value
1574 * This is important, as calling a modify operation may not change the
1575 * version number if the values appear unchanged, but a rename between
1576 * parents bumps this value.
1579 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1580 struct ldb_message *msg,
1581 const struct ldb_val *rdn_new,
1582 const struct ldb_val *rdn_old,
1583 struct replPropertyMetaDataBlob *omd,
1584 struct replmd_replicated_request *ar,
1587 bool is_forced_rodc)
1589 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1590 const struct dsdb_attribute *rdn_attr =
1591 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1592 const char *attr_name = rdn_attr != NULL ?
1593 rdn_attr->lDAPDisplayName :
1595 struct ldb_message_element new_el = {
1596 .flags = LDB_FLAG_MOD_REPLACE,
1599 .values = discard_const_p(struct ldb_val, rdn_new)
1601 struct ldb_message_element old_el = {
1602 .flags = LDB_FLAG_MOD_REPLACE,
1604 .num_values = rdn_old ? 1 : 0,
1605 .values = discard_const_p(struct ldb_val, rdn_old)
1608 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1609 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1610 if (ret != LDB_SUCCESS) {
1611 return ldb_oom(ldb);
1615 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1616 omd, ar->schema, &ar->seq_num,
1617 &ar->our_invocation_id,
1618 now, is_schema_nc, is_forced_rodc,
1623 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1625 uint32_t count = omd.ctr.ctr1.count;
1628 for (i=0; i < count; i++) {
1629 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1630 if (max < m.local_usn) {
1638 * update the replPropertyMetaData object each time we modify an
1639 * object. This is needed for DRS replication, as the merge on the
1640 * client is based on this object
1642 static int replmd_update_rpmd(struct ldb_module *module,
1643 const struct dsdb_schema *schema,
1644 struct ldb_request *req,
1645 const char * const *rename_attrs,
1646 struct ldb_message *msg, uint64_t *seq_num,
1647 time_t t, bool is_schema_nc,
1648 bool *is_urgent, bool *rodc)
1650 const struct ldb_val *omd_value;
1651 enum ndr_err_code ndr_err;
1652 struct replPropertyMetaDataBlob omd;
1655 const struct GUID *our_invocation_id;
1657 const char * const *attrs = NULL;
1658 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1659 struct ldb_result *res;
1660 struct ldb_context *ldb;
1661 struct ldb_message_element *objectclass_el;
1662 enum urgent_situation situation;
1663 bool rmd_is_provided;
1664 bool rmd_is_just_resorted = false;
1665 const char *not_rename_attrs[4 + msg->num_elements];
1666 bool is_forced_rodc = false;
1669 attrs = rename_attrs;
1671 for (i = 0; i < msg->num_elements; i++) {
1672 not_rename_attrs[i] = msg->elements[i].name;
1674 not_rename_attrs[i] = "replPropertyMetaData";
1675 not_rename_attrs[i+1] = "objectClass";
1676 not_rename_attrs[i+2] = "instanceType";
1677 not_rename_attrs[i+3] = NULL;
1678 attrs = not_rename_attrs;
1681 ldb = ldb_module_get_ctx(module);
1683 ret = samdb_rodc(ldb, rodc);
1684 if (ret != LDB_SUCCESS) {
1685 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1690 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1691 is_forced_rodc = true;
1694 our_invocation_id = samdb_ntds_invocation_id(ldb);
1695 if (!our_invocation_id) {
1696 /* this happens during an initial vampire while
1697 updating the schema */
1698 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1702 unix_to_nt_time(&now, t);
1704 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1705 rmd_is_provided = true;
1706 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1707 rmd_is_just_resorted = true;
1710 rmd_is_provided = false;
1713 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1714 * otherwise we consider we are updating */
1715 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1716 situation = REPL_URGENT_ON_DELETE;
1717 } else if (rename_attrs) {
1718 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1720 situation = REPL_URGENT_ON_UPDATE;
1723 if (rmd_is_provided) {
1724 /* In this case the change_replmetadata control was supplied */
1725 /* We check that it's the only attribute that is provided
1726 * (it's a rare case so it's better to keep the code simplier)
1727 * We also check that the highest local_usn is bigger or the same as
1730 if( msg->num_elements != 1 ||
1731 strncmp(msg->elements[0].name,
1732 "replPropertyMetaData", 20) ) {
1733 DEBUG(0,(__location__ ": changereplmetada control called without "\
1734 "a specified replPropertyMetaData attribute or with others\n"));
1735 return LDB_ERR_OPERATIONS_ERROR;
1737 if (situation != REPL_URGENT_ON_UPDATE) {
1738 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1739 return LDB_ERR_OPERATIONS_ERROR;
1741 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1743 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1744 ldb_dn_get_linearized(msg->dn)));
1745 return LDB_ERR_OPERATIONS_ERROR;
1747 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1748 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1749 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1750 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1751 ldb_dn_get_linearized(msg->dn)));
1752 return LDB_ERR_OPERATIONS_ERROR;
1755 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1756 DSDB_FLAG_NEXT_MODULE |
1757 DSDB_SEARCH_SHOW_RECYCLED |
1758 DSDB_SEARCH_SHOW_EXTENDED_DN |
1759 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1760 DSDB_SEARCH_REVEAL_INTERNALS, req);
1762 if (ret != LDB_SUCCESS) {
1766 if (rmd_is_just_resorted == false) {
1767 *seq_num = find_max_local_usn(omd);
1769 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1772 * The test here now allows for a new
1773 * replPropertyMetaData with no change, if was
1774 * just dbcheck re-sorting the values.
1776 if (*seq_num <= db_seq) {
1777 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1778 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1779 (long long)*seq_num, (long long)db_seq));
1780 return LDB_ERR_OPERATIONS_ERROR;
1785 /* search for the existing replPropertyMetaDataBlob. We need
1786 * to use REVEAL and ask for DNs in storage format to support
1787 * the check for values being the same in
1788 * replmd_update_rpmd_element()
1790 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1791 DSDB_FLAG_NEXT_MODULE |
1792 DSDB_SEARCH_SHOW_RECYCLED |
1793 DSDB_SEARCH_SHOW_EXTENDED_DN |
1794 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1795 DSDB_SEARCH_REVEAL_INTERNALS, req);
1796 if (ret != LDB_SUCCESS) {
1800 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1802 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1803 ldb_dn_get_linearized(msg->dn)));
1804 return LDB_ERR_OPERATIONS_ERROR;
1807 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1808 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1809 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1810 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1811 ldb_dn_get_linearized(msg->dn)));
1812 return LDB_ERR_OPERATIONS_ERROR;
1815 if (omd.version != 1) {
1816 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1817 omd.version, ldb_dn_get_linearized(msg->dn)));
1818 return LDB_ERR_OPERATIONS_ERROR;
1821 for (i=0; i<msg->num_elements;) {
1822 struct ldb_message_element *el = &msg->elements[i];
1823 struct ldb_message_element *old_el;
1825 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1826 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1827 &omd, schema, seq_num,
1832 if (ret != LDB_SUCCESS) {
1836 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1837 *is_urgent = replmd_check_urgent_attribute(el);
1840 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1845 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1847 if (el->num_values != 0) {
1852 ldb_msg_remove_element(msg, el);
1857 * Assert that we have an objectClass attribute - this is major
1858 * corruption if we don't have this!
1860 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1861 if (objectclass_el != NULL) {
1863 * Now check if this objectClass means we need to do urgent replication
1865 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1869 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1870 ldb_asprintf_errstring(ldb, __location__
1871 ": objectClass missing on %s\n",
1872 ldb_dn_get_linearized(msg->dn));
1873 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1877 * replmd_update_rpmd_element has done an update if the
1880 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1881 struct ldb_val *md_value;
1882 struct ldb_message_element *el;
1884 /*if we are RODC and this is a DRSR update then its ok*/
1885 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1886 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1887 && !is_forced_rodc) {
1888 unsigned instanceType;
1891 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1892 return LDB_ERR_REFERRAL;
1895 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1896 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1897 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1898 "cannot change replicated attribute on partial replica");
1902 md_value = talloc(msg, struct ldb_val);
1903 if (md_value == NULL) {
1905 return LDB_ERR_OPERATIONS_ERROR;
1908 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1909 if (ret != LDB_SUCCESS) {
1910 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1914 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1915 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1916 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1917 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1918 ldb_dn_get_linearized(msg->dn)));
1919 return LDB_ERR_OPERATIONS_ERROR;
1922 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1923 if (ret != LDB_SUCCESS) {
1924 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1925 ldb_dn_get_linearized(msg->dn)));
1930 el->values = md_value;
1936 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1938 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1940 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1941 &pdn2->dsdb_dn->extra_part);
1947 get a series of message element values as an array of DNs and GUIDs
1948 the result is sorted by GUID
1950 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1951 struct ldb_message_element *el, struct parsed_dn **pdn,
1952 const char *ldap_oid, struct ldb_request *parent)
1955 bool values_are_sorted = true;
1956 struct ldb_context *ldb = ldb_module_get_ctx(module);
1963 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1965 ldb_module_oom(module);
1966 return LDB_ERR_OPERATIONS_ERROR;
1969 for (i=0; i<el->num_values; i++) {
1970 struct ldb_val *v = &el->values[i];
1973 struct parsed_dn *p;
1977 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1978 if (p->dsdb_dn == NULL) {
1979 return LDB_ERR_INVALID_DN_SYNTAX;
1982 dn = p->dsdb_dn->dn;
1984 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
1985 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
1986 unlikely(GUID_all_zero(&p->guid))) {
1987 /* we got a DN without a GUID - go find the GUID */
1988 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
1989 if (ret != LDB_SUCCESS) {
1990 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1991 ldb_dn_get_linearized(dn));
1992 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1993 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1994 ldb_attr_cmp(el->name, "member") == 0) {
1995 return LDB_ERR_UNWILLING_TO_PERFORM;
1999 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2000 if (ret != LDB_SUCCESS) {
2003 } else if (!NT_STATUS_IS_OK(status)) {
2004 return LDB_ERR_OPERATIONS_ERROR;
2006 if (i > 0 && values_are_sorted) {
2007 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2009 values_are_sorted = false;
2012 /* keep a pointer to the original ldb_val */
2015 if (! values_are_sorted) {
2016 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2022 * Get a series of trusted message element values. The result is sorted by
2023 * GUID, even though the GUIDs might not be known. That works because we trust
2024 * the database to give us the elements like that if the
2025 * replmd_private->sorted_links flag is set.
2027 * We also ensure that the links are in the Functional Level 2003
2028 * linked attributes format.
2030 static int get_parsed_dns_trusted(struct ldb_module *module,
2031 struct replmd_private *replmd_private,
2032 TALLOC_CTX *mem_ctx,
2033 struct ldb_message_element *el,
2034 struct parsed_dn **pdn,
2035 const char *ldap_oid,
2036 struct ldb_request *parent)
2045 if (!replmd_private->sorted_links) {
2046 /* We need to sort the list. This is the slow old path we want
2049 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2051 if (ret != LDB_SUCCESS) {
2055 /* Here we get a list of 'struct parsed_dns' without the parsing */
2056 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2059 ldb_module_oom(module);
2060 return LDB_ERR_OPERATIONS_ERROR;
2063 for (i = 0; i < el->num_values; i++) {
2064 (*pdn)[i].v = &el->values[i];
2069 * This upgrades links to FL2003 style, and sorts the result
2070 * if that was needed.
2072 * TODO: Add a database feature that asserts we have no FL2000
2073 * style links to avoid this check or add a feature that
2074 * uses a similar check to find sorted/unsorted links
2075 * for an on-the-fly upgrade.
2078 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2079 *pdn, el->num_values,
2082 if (ret != LDB_SUCCESS) {
2090 build a new extended DN, including all meta data fields
2092 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2093 RMD_ADDTIME = originating_add_time
2094 RMD_INVOCID = originating_invocation_id
2095 RMD_CHANGETIME = originating_change_time
2096 RMD_ORIGINATING_USN = originating_usn
2097 RMD_LOCAL_USN = local_usn
2098 RMD_VERSION = version
2100 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2101 const struct GUID *invocation_id, uint64_t seq_num,
2102 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
2104 struct ldb_dn *dn = dsdb_dn->dn;
2105 const char *tstring, *usn_string, *flags_string;
2106 struct ldb_val tval;
2108 struct ldb_val usnv, local_usnv;
2109 struct ldb_val vers, flagsv;
2112 const char *dnstring;
2114 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2116 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2118 return LDB_ERR_OPERATIONS_ERROR;
2120 tval = data_blob_string_const(tstring);
2122 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
2124 return LDB_ERR_OPERATIONS_ERROR;
2126 usnv = data_blob_string_const(usn_string);
2128 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2130 return LDB_ERR_OPERATIONS_ERROR;
2132 local_usnv = data_blob_string_const(usn_string);
2134 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2136 return LDB_ERR_OPERATIONS_ERROR;
2138 vers = data_blob_string_const(vstring);
2140 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2141 if (!NT_STATUS_IS_OK(status)) {
2142 return LDB_ERR_OPERATIONS_ERROR;
2145 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2146 if (!flags_string) {
2147 return LDB_ERR_OPERATIONS_ERROR;
2149 flagsv = data_blob_string_const(flags_string);
2151 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2152 if (ret != LDB_SUCCESS) return ret;
2153 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
2154 if (ret != LDB_SUCCESS) return ret;
2155 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2156 if (ret != LDB_SUCCESS) return ret;
2157 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2158 if (ret != LDB_SUCCESS) return ret;
2159 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2160 if (ret != LDB_SUCCESS) return ret;
2161 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2162 if (ret != LDB_SUCCESS) return ret;
2163 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2164 if (ret != LDB_SUCCESS) return ret;
2166 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2167 if (dnstring == NULL) {
2168 return LDB_ERR_OPERATIONS_ERROR;
2170 *v = data_blob_string_const(dnstring);
2175 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2176 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2177 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2178 uint32_t version, bool deleted);
2181 check if any links need upgrading from w2k format
2183 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2184 struct parsed_dn *dns, uint32_t count,
2185 struct ldb_message_element *el,
2186 const char *ldap_oid)
2189 const struct GUID *invocation_id = NULL;
2190 for (i=0; i<count; i++) {
2194 if (dns[i].dsdb_dn == NULL) {
2195 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2197 if (ret != LDB_SUCCESS) {
2198 return LDB_ERR_INVALID_DN_SYNTAX;
2202 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2203 &version, "RMD_VERSION");
2204 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2206 * We optimistically assume they are all the same; if
2207 * the first one is fixed, they are all fixed.
2209 * If the first one was *not* fixed and we find a
2210 * later one that is, that is an occasion to shout
2216 DEBUG(0, ("Mixed w2k and fixed format "
2217 "linked attributes\n"));
2221 if (invocation_id == NULL) {
2222 invocation_id = samdb_ntds_invocation_id(ldb);
2223 if (invocation_id == NULL) {
2224 return LDB_ERR_OPERATIONS_ERROR;
2229 /* it's an old one that needs upgrading */
2230 ret = replmd_update_la_val(el->values, dns[i].v,
2231 dns[i].dsdb_dn, dns[i].dsdb_dn,
2232 invocation_id, 1, 1, 0, 0, false);
2233 if (ret != LDB_SUCCESS) {
2239 * This sort() is critical for the operation of
2240 * get_parsed_dns_trusted() because callers of this function
2241 * expect a sorted list, and FL2000 style links are not
2242 * sorted. In particular, as well as the upgrade case,
2243 * get_parsed_dns_trusted() is called from
2244 * replmd_delete_remove_link() even in FL2000 mode
2246 * We do not normally pay the cost of the qsort() due to the
2247 * early return in the RMD_VERSION found case.
2249 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2254 update an extended DN, including all meta data fields
2256 see replmd_build_la_val for value names
2258 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2259 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2260 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2261 uint32_t version, bool deleted)
2263 struct ldb_dn *dn = dsdb_dn->dn;
2264 const char *tstring, *usn_string, *flags_string;
2265 struct ldb_val tval;
2267 struct ldb_val usnv, local_usnv;
2268 struct ldb_val vers, flagsv;
2269 const struct ldb_val *old_addtime;
2270 uint32_t old_version;
2273 const char *dnstring;
2275 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2277 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2279 return LDB_ERR_OPERATIONS_ERROR;
2281 tval = data_blob_string_const(tstring);
2283 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2285 return LDB_ERR_OPERATIONS_ERROR;
2287 usnv = data_blob_string_const(usn_string);
2289 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2291 return LDB_ERR_OPERATIONS_ERROR;
2293 local_usnv = data_blob_string_const(usn_string);
2295 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2296 if (!NT_STATUS_IS_OK(status)) {
2297 return LDB_ERR_OPERATIONS_ERROR;
2300 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2301 if (!flags_string) {
2302 return LDB_ERR_OPERATIONS_ERROR;
2304 flagsv = data_blob_string_const(flags_string);
2306 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2307 if (ret != LDB_SUCCESS) return ret;
2309 /* get the ADDTIME from the original */
2310 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2311 if (old_addtime == NULL) {
2312 old_addtime = &tval;
2314 if (dsdb_dn != old_dsdb_dn ||
2315 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2316 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2317 if (ret != LDB_SUCCESS) return ret;
2320 /* use our invocation id */
2321 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2322 if (ret != LDB_SUCCESS) return ret;
2324 /* changetime is the current time */
2325 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2326 if (ret != LDB_SUCCESS) return ret;
2328 /* update the USN */
2329 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2330 if (ret != LDB_SUCCESS) return ret;
2332 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2333 if (ret != LDB_SUCCESS) return ret;
2335 /* increase the version by 1 */
2336 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2337 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2338 version = old_version+1;
2340 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2341 vers = data_blob_string_const(vstring);
2342 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2343 if (ret != LDB_SUCCESS) return ret;
2345 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2346 if (dnstring == NULL) {
2347 return LDB_ERR_OPERATIONS_ERROR;
2349 *v = data_blob_string_const(dnstring);
2355 handle adding a linked attribute
2357 static int replmd_modify_la_add(struct ldb_module *module,
2358 struct replmd_private *replmd_private,
2359 const struct dsdb_schema *schema,
2360 struct ldb_message *msg,
2361 struct ldb_message_element *el,
2362 struct ldb_message_element *old_el,
2363 const struct dsdb_attribute *schema_attr,
2366 struct ldb_dn *msg_dn,
2367 struct ldb_request *parent)
2370 struct parsed_dn *dns, *old_dns;
2371 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2373 struct ldb_val *new_values = NULL;
2374 unsigned old_num_values = old_el ? old_el->num_values : 0;
2375 unsigned num_values = 0;
2376 unsigned max_num_values;
2377 const struct GUID *invocation_id;
2378 struct ldb_context *ldb = ldb_module_get_ctx(module);
2380 unix_to_nt_time(&now, t);
2382 invocation_id = samdb_ntds_invocation_id(ldb);
2383 if (!invocation_id) {
2384 talloc_free(tmp_ctx);
2385 return LDB_ERR_OPERATIONS_ERROR;
2388 /* get the DNs to be added, fully parsed.
2390 * We need full parsing because they came off the wire and we don't
2391 * trust them, besides which we need their details to know where to put
2394 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2395 schema_attr->syntax->ldap_oid, parent);
2396 if (ret != LDB_SUCCESS) {
2397 talloc_free(tmp_ctx);
2401 /* get the existing DNs, lazily parsed */
2402 ret = get_parsed_dns_trusted(module, replmd_private,
2403 tmp_ctx, old_el, &old_dns,
2404 schema_attr->syntax->ldap_oid, parent);
2406 if (ret != LDB_SUCCESS) {
2407 talloc_free(tmp_ctx);
2411 max_num_values = old_num_values + el->num_values;
2412 if (max_num_values < old_num_values) {
2413 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2414 "old values: %u, new values: %u, sum: %u",
2415 old_num_values, el->num_values, max_num_values));
2416 talloc_free(tmp_ctx);
2417 return LDB_ERR_OPERATIONS_ERROR;
2420 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2422 if (new_values == NULL) {
2423 ldb_module_oom(module);
2424 talloc_free(tmp_ctx);
2425 return LDB_ERR_OPERATIONS_ERROR;
2429 * For each new value, find where it would go in the list. If there is
2430 * a matching GUID there, we update the existing value; otherwise we
2434 for (i = 0; i < el->num_values; i++) {
2435 struct parsed_dn *exact;
2436 struct parsed_dn *next;
2438 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2441 dns[i].dsdb_dn->extra_part, 0,
2443 schema_attr->syntax->ldap_oid,
2445 if (err != LDB_SUCCESS) {
2446 talloc_free(tmp_ctx);
2450 if (exact != NULL) {
2452 * We are trying to add one that exists, which is only
2453 * allowed if it was previously deleted.
2455 * When we do undelete a link we change it in place.
2456 * It will be copied across into the right spot in due
2460 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2462 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2463 struct GUID_txt_buf guid_str;
2464 ldb_asprintf_errstring(ldb,
2465 "Attribute %s already "
2466 "exists for target GUID %s",
2468 GUID_buf_string(&exact->guid,
2470 talloc_free(tmp_ctx);
2471 /* error codes for 'member' need to be
2473 if (ldb_attr_cmp(el->name, "member") == 0) {
2474 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2476 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2480 ret = replmd_update_la_val(new_values, exact->v,
2483 invocation_id, seq_num,
2484 seq_num, now, 0, false);
2485 if (ret != LDB_SUCCESS) {
2486 talloc_free(tmp_ctx);
2490 ret = replmd_add_backlink(module, replmd_private,
2497 if (ret != LDB_SUCCESS) {
2498 talloc_free(tmp_ctx);
2504 * Here we don't have an exact match.
2506 * If next is NULL, this one goes beyond the end of the
2507 * existing list, so we need to add all of those ones first.
2509 * If next is not NULL, we need to add all the ones before
2513 offset = old_num_values;
2515 /* next should have been parsed, but let's make sure */
2516 if (next->dsdb_dn == NULL) {
2517 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2518 schema_attr->syntax->ldap_oid);
2519 if (ret != LDB_SUCCESS) {
2523 offset = MIN(next - old_dns, old_num_values);
2526 /* put all the old ones before next on the list */
2527 for (; j < offset; j++) {
2528 new_values[num_values] = *old_dns[j].v;
2532 ret = replmd_add_backlink(module, replmd_private,
2537 /* Make the new linked attribute ldb_val. */
2538 ret = replmd_build_la_val(new_values, &new_values[num_values],
2539 dns[i].dsdb_dn, invocation_id,
2542 if (ret != LDB_SUCCESS) {
2543 talloc_free(tmp_ctx);
2547 if (ret != LDB_SUCCESS) {
2548 talloc_free(tmp_ctx);
2552 /* copy the rest of the old ones (if any) */
2553 for (; j < old_num_values; j++) {
2554 new_values[num_values] = *old_dns[j].v;
2558 talloc_steal(msg->elements, new_values);
2559 if (old_el != NULL) {
2560 talloc_steal(msg->elements, old_el->values);
2562 el->values = new_values;
2563 el->num_values = num_values;
2565 talloc_free(tmp_ctx);
2567 /* we now tell the backend to replace all existing values
2568 with the one we have constructed */
2569 el->flags = LDB_FLAG_MOD_REPLACE;
2576 handle deleting all active linked attributes
2578 static int replmd_modify_la_delete(struct ldb_module *module,
2579 struct replmd_private *replmd_private,
2580 const struct dsdb_schema *schema,
2581 struct ldb_message *msg,
2582 struct ldb_message_element *el,
2583 struct ldb_message_element *old_el,
2584 const struct dsdb_attribute *schema_attr,
2587 struct ldb_dn *msg_dn,
2588 struct ldb_request *parent)
2591 struct parsed_dn *dns, *old_dns;
2592 TALLOC_CTX *tmp_ctx = NULL;
2594 struct ldb_context *ldb = ldb_module_get_ctx(module);
2595 struct ldb_control *vanish_links_ctrl = NULL;
2596 bool vanish_links = false;
2597 unsigned int num_to_delete = el->num_values;
2599 const struct GUID *invocation_id;
2602 unix_to_nt_time(&now, t);
2604 invocation_id = samdb_ntds_invocation_id(ldb);
2605 if (!invocation_id) {
2606 return LDB_ERR_OPERATIONS_ERROR;
2609 if (old_el == NULL || old_el->num_values == 0) {
2610 /* there is nothing to delete... */
2611 if (num_to_delete == 0) {
2612 /* and we're deleting nothing, so that's OK */
2615 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2618 tmp_ctx = talloc_new(msg);
2619 if (tmp_ctx == NULL) {
2620 return LDB_ERR_OPERATIONS_ERROR;
2623 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2624 schema_attr->syntax->ldap_oid, parent);
2625 if (ret != LDB_SUCCESS) {
2626 talloc_free(tmp_ctx);
2630 ret = get_parsed_dns_trusted(module, replmd_private,
2631 tmp_ctx, old_el, &old_dns,
2632 schema_attr->syntax->ldap_oid, parent);
2634 if (ret != LDB_SUCCESS) {
2635 talloc_free(tmp_ctx);
2640 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2641 if (vanish_links_ctrl) {
2642 vanish_links = true;
2643 vanish_links_ctrl->critical = false;
2647 /* we empty out el->values here to avoid damage if we return early. */
2652 * If vanish links is set, we are actually removing members of
2653 * old_el->values; otherwise we are just marking them deleted.
2655 * There is a special case when no values are given: we remove them
2656 * all. When we have the vanish_links control we just have to remove
2657 * the backlinks and change our element to replace the existing values
2658 * with the empty list.
2661 if (num_to_delete == 0) {
2662 for (i = 0; i < old_el->num_values; i++) {
2663 struct parsed_dn *p = &old_dns[i];
2664 if (p->dsdb_dn == NULL) {
2665 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2666 schema_attr->syntax->ldap_oid);
2667 if (ret != LDB_SUCCESS) {
2671 ret = replmd_add_backlink(module, replmd_private,
2672 schema, msg_dn, &p->guid,
2675 if (ret != LDB_SUCCESS) {
2676 talloc_free(tmp_ctx);
2683 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2684 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2688 ret = replmd_update_la_val(old_el->values, p->v,
2689 p->dsdb_dn, p->dsdb_dn,
2690 invocation_id, seq_num,
2691 seq_num, now, 0, true);
2692 if (ret != LDB_SUCCESS) {
2693 talloc_free(tmp_ctx);
2699 el->flags = LDB_FLAG_MOD_REPLACE;
2700 talloc_free(tmp_ctx);
2706 for (i = 0; i < num_to_delete; i++) {
2707 struct parsed_dn *p = &dns[i];
2708 struct parsed_dn *exact = NULL;
2709 struct parsed_dn *next = NULL;
2710 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2713 p->dsdb_dn->extra_part, 0,
2715 schema_attr->syntax->ldap_oid,
2717 if (ret != LDB_SUCCESS) {
2718 talloc_free(tmp_ctx);
2721 if (exact == NULL) {
2722 struct GUID_txt_buf buf;
2723 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2724 "exist for target GUID %s",
2726 GUID_buf_string(&p->guid, &buf));
2727 if (ldb_attr_cmp(el->name, "member") == 0) {
2728 talloc_free(tmp_ctx);
2729 return LDB_ERR_UNWILLING_TO_PERFORM;
2731 talloc_free(tmp_ctx);
2732 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2737 if (CHECK_DEBUGLVL(5)) {
2738 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2739 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2740 struct GUID_txt_buf buf;
2741 const char *guid_str = \
2742 GUID_buf_string(&p->guid, &buf);
2743 DEBUG(5, ("Deleting deleted linked "
2744 "attribute %s to %s, because "
2745 "vanish_links control is set\n",
2746 el->name, guid_str));
2750 /* remove the backlink */
2751 ret = replmd_add_backlink(module,
2758 if (ret != LDB_SUCCESS) {
2759 talloc_free(tmp_ctx);
2763 /* We flag the deletion and tidy it up later. */
2768 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2770 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2771 struct GUID_txt_buf buf;
2772 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2773 ldb_asprintf_errstring(ldb, "Attribute %s already "
2774 "deleted for target GUID %s",
2775 el->name, guid_str);
2776 if (ldb_attr_cmp(el->name, "member") == 0) {
2777 talloc_free(tmp_ctx);
2778 return LDB_ERR_UNWILLING_TO_PERFORM;
2780 talloc_free(tmp_ctx);
2781 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2785 ret = replmd_update_la_val(old_el->values, exact->v,
2786 exact->dsdb_dn, exact->dsdb_dn,
2787 invocation_id, seq_num, seq_num,
2789 if (ret != LDB_SUCCESS) {
2790 talloc_free(tmp_ctx);
2793 ret = replmd_add_backlink(module, replmd_private,
2798 if (ret != LDB_SUCCESS) {
2799 talloc_free(tmp_ctx);
2806 for (i = 0; i < old_el->num_values; i++) {
2807 if (old_dns[i].v != NULL) {
2808 old_el->values[j] = *old_dns[i].v;
2812 old_el->num_values = j;
2815 el->values = talloc_steal(msg->elements, old_el->values);
2816 el->num_values = old_el->num_values;
2818 talloc_free(tmp_ctx);
2820 /* we now tell the backend to replace all existing values
2821 with the one we have constructed */
2822 el->flags = LDB_FLAG_MOD_REPLACE;
2828 handle replacing a linked attribute
2830 static int replmd_modify_la_replace(struct ldb_module *module,
2831 struct replmd_private *replmd_private,
2832 const struct dsdb_schema *schema,
2833 struct ldb_message *msg,
2834 struct ldb_message_element *el,
2835 struct ldb_message_element *old_el,
2836 const struct dsdb_attribute *schema_attr,
2839 struct ldb_dn *msg_dn,
2840 struct ldb_request *parent)
2842 unsigned int i, old_i, new_i;
2843 struct parsed_dn *dns, *old_dns;
2844 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2846 const struct GUID *invocation_id;
2847 struct ldb_context *ldb = ldb_module_get_ctx(module);
2848 struct ldb_val *new_values = NULL;
2849 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2850 unsigned int old_num_values;
2851 unsigned int repl_num_values;
2852 unsigned int max_num_values;
2855 unix_to_nt_time(&now, t);
2857 invocation_id = samdb_ntds_invocation_id(ldb);
2858 if (!invocation_id) {
2859 return LDB_ERR_OPERATIONS_ERROR;
2863 * The replace operation is unlike the replace and delete cases in that
2864 * we need to look at every existing link to see whether it is being
2865 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2867 * As we are trying to combine two sorted lists, the algorithm we use
2868 * is akin to the merge phase of a merge sort. We interleave the two
2869 * lists, doing different things depending on which side the current
2872 * There are three main cases, with some sub-cases.
2874 * - a DN is in the old list but not the new one. It needs to be
2875 * marked as deleted (but left in the list).
2876 * - maybe it is already deleted, and we have less to do.
2878 * - a DN is in both lists. The old data gets replaced by the new,
2879 * and the list doesn't grow. The old link may have been marked as
2880 * deleted, in which case we undelete it.
2882 * - a DN is in the new list only. We add it in the right place.
2885 old_num_values = old_el ? old_el->num_values : 0;
2886 repl_num_values = el->num_values;
2887 max_num_values = old_num_values + repl_num_values;
2889 if (max_num_values == 0) {
2890 /* There is nothing to do! */
2894 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2895 if (ret != LDB_SUCCESS) {
2896 talloc_free(tmp_ctx);
2900 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2902 if (ret != LDB_SUCCESS) {
2903 talloc_free(tmp_ctx);
2907 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2909 if (ret != LDB_SUCCESS) {
2910 talloc_free(tmp_ctx);
2914 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2915 if (new_values == NULL) {
2916 ldb_module_oom(module);
2917 talloc_free(tmp_ctx);
2918 return LDB_ERR_OPERATIONS_ERROR;
2923 for (i = 0; i < max_num_values; i++) {
2925 struct parsed_dn *old_p, *new_p;
2926 if (old_i < old_num_values && new_i < repl_num_values) {
2927 old_p = &old_dns[old_i];
2928 new_p = &dns[new_i];
2929 cmp = parsed_dn_compare(old_p, new_p);
2930 } else if (old_i < old_num_values) {
2931 /* the new list is empty, read the old list */
2932 old_p = &old_dns[old_i];
2935 } else if (new_i < repl_num_values) {
2936 /* the old list is empty, read new list */
2938 new_p = &dns[new_i];
2946 * An old ones that come before the next replacement
2947 * (if any). We mark it as deleted and add it to the
2950 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2951 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
2952 ret = replmd_update_la_val(new_values, old_p->v,
2958 if (ret != LDB_SUCCESS) {
2959 talloc_free(tmp_ctx);
2963 ret = replmd_add_backlink(module, replmd_private,
2966 &old_p->guid, false,
2969 if (ret != LDB_SUCCESS) {
2970 talloc_free(tmp_ctx);
2974 new_values[i] = *old_p->v;
2976 } else if (cmp == 0) {
2978 * We are overwriting one. If it was previously
2979 * deleted, we need to add a backlink.
2981 * Note that if any RMD_FLAGs in an extended new DN
2986 ret = replmd_update_la_val(new_values, old_p->v,
2992 if (ret != LDB_SUCCESS) {
2993 talloc_free(tmp_ctx);
2997 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2998 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
2999 ret = replmd_add_backlink(module, replmd_private,
3005 if (ret != LDB_SUCCESS) {
3006 talloc_free(tmp_ctx);
3011 new_values[i] = *old_p->v;
3016 * Replacements that don't match an existing one. We
3017 * just add them to the final list.
3019 ret = replmd_build_la_val(new_values,
3025 if (ret != LDB_SUCCESS) {
3026 talloc_free(tmp_ctx);
3029 ret = replmd_add_backlink(module, replmd_private,
3035 if (ret != LDB_SUCCESS) {
3036 talloc_free(tmp_ctx);
3039 new_values[i] = *new_p->v;
3043 if (old_el != NULL) {
3044 talloc_steal(msg->elements, old_el->values);
3046 el->values = talloc_steal(msg->elements, new_values);
3048 talloc_free(tmp_ctx);
3050 el->flags = LDB_FLAG_MOD_REPLACE;
3057 handle linked attributes in modify requests
3059 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3060 struct replmd_private *replmd_private,
3061 struct ldb_message *msg,
3062 uint64_t seq_num, time_t t,
3063 struct ldb_request *parent)
3065 struct ldb_result *res;
3068 struct ldb_context *ldb = ldb_module_get_ctx(module);
3069 struct ldb_message *old_msg;
3071 const struct dsdb_schema *schema;
3073 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3075 * Nothing special is required for modifying or vanishing links
3076 * in fl2000 since they are just strings in a multi-valued
3079 struct ldb_control *ctrl = ldb_request_get_control(parent,
3080 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3082 ctrl->critical = false;
3090 * We should restrict this to the intersection of the list of
3091 * linked attributes in the schema and the list of attributes
3094 * This will help performance a little, as otherwise we have
3095 * to allocate the entire object value-by-value.
3097 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3098 DSDB_FLAG_NEXT_MODULE |
3099 DSDB_SEARCH_SHOW_RECYCLED |
3100 DSDB_SEARCH_REVEAL_INTERNALS |
3101 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3103 if (ret != LDB_SUCCESS) {
3106 schema = dsdb_get_schema(ldb, res);
3108 return LDB_ERR_OPERATIONS_ERROR;
3111 old_msg = res->msgs[0];
3113 for (i=0; i<msg->num_elements; i++) {
3114 struct ldb_message_element *el = &msg->elements[i];
3115 struct ldb_message_element *old_el, *new_el;
3116 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3117 const struct dsdb_attribute *schema_attr
3118 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3120 ldb_asprintf_errstring(ldb,
3121 "%s: attribute %s is not a valid attribute in schema",
3122 __FUNCTION__, el->name);
3123 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3125 if (schema_attr->linkID == 0) {
3128 if ((schema_attr->linkID & 1) == 1) {
3129 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
3132 /* Odd is for the target. Illegal to modify */
3133 ldb_asprintf_errstring(ldb,
3134 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3135 return LDB_ERR_UNWILLING_TO_PERFORM;
3137 old_el = ldb_msg_find_element(old_msg, el->name);
3139 case LDB_FLAG_MOD_REPLACE:
3140 ret = replmd_modify_la_replace(module, replmd_private,
3141 schema, msg, el, old_el,
3142 schema_attr, seq_num, t,
3146 case LDB_FLAG_MOD_DELETE:
3147 ret = replmd_modify_la_delete(module, replmd_private,
3148 schema, msg, el, old_el,
3149 schema_attr, seq_num, t,
3153 case LDB_FLAG_MOD_ADD:
3154 ret = replmd_modify_la_add(module, replmd_private,
3155 schema, msg, el, old_el,
3156 schema_attr, seq_num, t,
3161 ldb_asprintf_errstring(ldb,
3162 "invalid flags 0x%x for %s linked attribute",
3163 el->flags, el->name);
3164 return LDB_ERR_UNWILLING_TO_PERFORM;
3166 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3167 ldb_asprintf_errstring(ldb,
3168 "Attribute %s is single valued but more than one value has been supplied",
3170 /* Return codes as found on Windows 2012r2 */
3171 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3172 return LDB_ERR_CONSTRAINT_VIOLATION;
3174 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3177 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3180 if (ret != LDB_SUCCESS) {
3184 ldb_msg_remove_attr(old_msg, el->name);
3186 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3187 new_el->num_values = el->num_values;
3188 new_el->values = talloc_steal(msg->elements, el->values);
3190 /* TODO: this relises a bit too heavily on the exact
3191 behaviour of ldb_msg_find_element and
3192 ldb_msg_remove_element */
3193 old_el = ldb_msg_find_element(msg, el->name);
3195 ldb_msg_remove_element(msg, old_el);
3205 static int send_rodc_referral(struct ldb_request *req,
3206 struct ldb_context *ldb,
3209 char *referral = NULL;
3210 struct loadparm_context *lp_ctx = NULL;
3211 struct ldb_dn *fsmo_role_dn = NULL;
3212 struct ldb_dn *role_owner_dn = NULL;
3213 const char *domain = NULL;
3216 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3217 struct loadparm_context);
3219 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3220 &fsmo_role_dn, &role_owner_dn);
3222 if (W_ERROR_IS_OK(werr)) {
3223 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3224 if (server_dn != NULL) {
3225 ldb_dn_remove_child_components(server_dn, 1);
3226 domain = samdb_dn_to_dnshostname(ldb, req,
3231 if (domain == NULL) {
3232 domain = lpcfg_dnsdomain(lp_ctx);
3235 referral = talloc_asprintf(req, "ldap://%s/%s",
3237 ldb_dn_get_linearized(dn));
3238 if (referral == NULL) {
3240 return LDB_ERR_OPERATIONS_ERROR;
3243 return ldb_module_send_referral(req, referral);
3247 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3249 struct ldb_context *ldb;
3250 struct replmd_replicated_request *ac;
3251 struct ldb_request *down_req;
3252 struct ldb_message *msg;
3253 time_t t = time(NULL);
3255 bool is_urgent = false, rodc = false;
3256 bool is_schema_nc = false;
3257 unsigned int functional_level;
3258 const struct ldb_message_element *guid_el = NULL;
3259 struct ldb_control *sd_propagation_control;
3260 struct replmd_private *replmd_private =
3261 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3263 /* do not manipulate our control entries */
3264 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3265 return ldb_next_request(module, req);
3268 sd_propagation_control = ldb_request_get_control(req,
3269 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3270 if (sd_propagation_control != NULL) {
3271 if (req->op.mod.message->num_elements != 1) {
3272 return ldb_module_operr(module);
3274 ret = strcmp(req->op.mod.message->elements[0].name,
3275 "nTSecurityDescriptor");
3277 return ldb_module_operr(module);
3280 return ldb_next_request(module, req);
3283 ldb = ldb_module_get_ctx(module);
3285 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3287 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3288 if (guid_el != NULL) {
3289 ldb_set_errstring(ldb,
3290 "replmd_modify: it's not allowed to change the objectGUID!");
3291 return LDB_ERR_CONSTRAINT_VIOLATION;
3294 ac = replmd_ctx_init(module, req);
3296 return ldb_module_oom(module);
3299 functional_level = dsdb_functional_level(ldb);
3301 /* we have to copy the message as the caller might have it as a const */
3302 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3306 return LDB_ERR_OPERATIONS_ERROR;
3309 ldb_msg_remove_attr(msg, "whenChanged");
3310 ldb_msg_remove_attr(msg, "uSNChanged");
3312 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3314 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3315 msg, &ac->seq_num, t, is_schema_nc,
3317 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3318 ret = send_rodc_referral(req, ldb, msg->dn);
3324 if (ret != LDB_SUCCESS) {
3329 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3330 msg, ac->seq_num, t, req);
3331 if (ret != LDB_SUCCESS) {
3337 * - replace the old object with the newly constructed one
3340 ac->is_urgent = is_urgent;
3342 ret = ldb_build_mod_req(&down_req, ldb, ac,
3345 ac, replmd_op_callback,
3347 LDB_REQ_SET_LOCATION(down_req);
3348 if (ret != LDB_SUCCESS) {
3353 /* current partition control is needed by "replmd_op_callback" */
3354 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3355 ret = ldb_request_add_control(down_req,
3356 DSDB_CONTROL_CURRENT_PARTITION_OID,
3358 if (ret != LDB_SUCCESS) {
3364 /* If we are in functional level 2000, then
3365 * replmd_modify_handle_linked_attribs will have done
3367 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3368 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3369 if (ret != LDB_SUCCESS) {
3375 talloc_steal(down_req, msg);
3377 /* we only change whenChanged and uSNChanged if the seq_num
3379 if (ac->seq_num != 0) {
3380 ret = add_time_element(msg, "whenChanged", t);
3381 if (ret != LDB_SUCCESS) {
3387 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3388 if (ret != LDB_SUCCESS) {
3395 /* go on with the call chain */
3396 return ldb_next_request(module, down_req);
3399 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3402 handle a rename request
3404 On a rename we need to do an extra ldb_modify which sets the
3405 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3407 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3409 struct ldb_context *ldb;
3410 struct replmd_replicated_request *ac;
3412 struct ldb_request *down_req;
3414 /* do not manipulate our control entries */
3415 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3416 return ldb_next_request(module, req);
3419 ldb = ldb_module_get_ctx(module);
3421 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3423 ac = replmd_ctx_init(module, req);
3425 return ldb_module_oom(module);
3428 ret = ldb_build_rename_req(&down_req, ldb, ac,
3429 ac->req->op.rename.olddn,
3430 ac->req->op.rename.newdn,
3432 ac, replmd_rename_callback,
3434 LDB_REQ_SET_LOCATION(down_req);
3435 if (ret != LDB_SUCCESS) {
3440 /* go on with the call chain */
3441 return ldb_next_request(module, down_req);
3444 /* After the rename is compleated, update the whenchanged etc */
3445 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3447 struct ldb_context *ldb;
3448 struct ldb_request *down_req;
3449 struct ldb_message *msg;
3450 const struct dsdb_attribute *rdn_attr;
3451 const char *rdn_name;
3452 const struct ldb_val *rdn_val;
3453 const char *attrs[5] = { NULL, };
3454 time_t t = time(NULL);
3456 bool is_urgent = false, rodc = false;
3458 struct replmd_replicated_request *ac =
3459 talloc_get_type(req->context, struct replmd_replicated_request);
3460 struct replmd_private *replmd_private =
3461 talloc_get_type(ldb_module_get_private(ac->module),
3462 struct replmd_private);
3464 ldb = ldb_module_get_ctx(ac->module);
3466 if (ares->error != LDB_SUCCESS) {
3467 return ldb_module_done(ac->req, ares->controls,
3468 ares->response, ares->error);
3471 if (ares->type != LDB_REPLY_DONE) {
3472 ldb_set_errstring(ldb,
3473 "invalid ldb_reply_type in callback");
3475 return ldb_module_done(ac->req, NULL, NULL,
3476 LDB_ERR_OPERATIONS_ERROR);
3480 * - replace the old object with the newly constructed one
3483 msg = ldb_msg_new(ac);
3486 return LDB_ERR_OPERATIONS_ERROR;
3489 msg->dn = ac->req->op.rename.newdn;
3491 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3493 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3494 if (rdn_name == NULL) {
3496 return ldb_module_done(ac->req, NULL, NULL,
3500 /* normalize the rdn attribute name */
3501 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3502 if (rdn_attr == NULL) {
3504 return ldb_module_done(ac->req, NULL, NULL,
3507 rdn_name = rdn_attr->lDAPDisplayName;
3509 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3510 if (rdn_val == NULL) {
3512 return ldb_module_done(ac->req, NULL, NULL,
3516 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3518 return ldb_module_done(ac->req, NULL, NULL,
3521 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3523 return ldb_module_done(ac->req, NULL, NULL,
3526 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3528 return ldb_module_done(ac->req, NULL, NULL,
3531 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3533 return ldb_module_done(ac->req, NULL, NULL,
3538 * here we let replmd_update_rpmd() only search for
3539 * the existing "replPropertyMetaData" and rdn_name attributes.
3541 * We do not want the existing "name" attribute as
3542 * the "name" attribute needs to get the version
3543 * updated on rename even if the rdn value hasn't changed.
3545 * This is the diff of the meta data, for a moved user
3546 * on a w2k8r2 server:
3549 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3550 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3551 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3552 * version : 0x00000001 (1)
3553 * reserved : 0x00000000 (0)
3554 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3555 * local_usn : 0x00000000000037a5 (14245)
3556 * array: struct replPropertyMetaData1
3557 * attid : DRSUAPI_ATTID_name (0x90001)
3558 * - version : 0x00000001 (1)
3559 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3560 * + version : 0x00000002 (2)
3561 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3562 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3563 * - originating_usn : 0x00000000000037a5 (14245)
3564 * - local_usn : 0x00000000000037a5 (14245)
3565 * + originating_usn : 0x0000000000003834 (14388)
3566 * + local_usn : 0x0000000000003834 (14388)
3567 * array: struct replPropertyMetaData1
3568 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3569 * version : 0x00000004 (4)
3571 attrs[0] = "replPropertyMetaData";
3572 attrs[1] = "objectClass";
3573 attrs[2] = "instanceType";
3574 attrs[3] = rdn_name;
3577 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3578 msg, &ac->seq_num, t,
3579 is_schema_nc, &is_urgent, &rodc);
3580 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3581 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3583 return ldb_module_done(req, NULL, NULL, ret);
3586 if (ret != LDB_SUCCESS) {
3588 return ldb_module_done(ac->req, NULL, NULL, ret);
3591 if (ac->seq_num == 0) {
3593 return ldb_module_done(ac->req, NULL, NULL,
3595 "internal error seq_num == 0"));
3597 ac->is_urgent = is_urgent;
3599 ret = ldb_build_mod_req(&down_req, ldb, ac,
3602 ac, replmd_op_callback,
3604 LDB_REQ_SET_LOCATION(down_req);
3605 if (ret != LDB_SUCCESS) {
3610 /* current partition control is needed by "replmd_op_callback" */
3611 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3612 ret = ldb_request_add_control(down_req,
3613 DSDB_CONTROL_CURRENT_PARTITION_OID,
3615 if (ret != LDB_SUCCESS) {
3621 talloc_steal(down_req, msg);
3623 ret = add_time_element(msg, "whenChanged", t);
3624 if (ret != LDB_SUCCESS) {
3630 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3631 if (ret != LDB_SUCCESS) {
3637 /* go on with the call chain - do the modify after the rename */
3638 return ldb_next_request(ac->module, down_req);
3642 * remove links from objects that point at this object when an object
3643 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3644 * RemoveObj which states that link removal due to the object being
3645 * deleted is NOT an originating update - they just go away!
3648 static int replmd_delete_remove_link(struct ldb_module *module,
3649 const struct dsdb_schema *schema,
3650 struct replmd_private *replmd_private,
3653 struct ldb_message_element *el,
3654 const struct dsdb_attribute *sa,
3655 struct ldb_request *parent)
3658 TALLOC_CTX *tmp_ctx = talloc_new(module);
3659 struct ldb_context *ldb = ldb_module_get_ctx(module);
3661 for (i=0; i<el->num_values; i++) {
3662 struct dsdb_dn *dsdb_dn;
3664 struct ldb_message *msg;
3665 const struct dsdb_attribute *target_attr;
3666 struct ldb_message_element *el2;
3668 struct ldb_val dn_val;
3669 uint32_t dsdb_flags = 0;
3670 const char *attrs[] = { NULL, NULL };
3671 struct ldb_result *link_res;
3672 struct ldb_message *link_msg;
3673 struct ldb_message_element *link_el;
3674 struct parsed_dn *link_dns;
3675 struct parsed_dn *p = NULL, *unused = NULL;
3677 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3681 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3683 talloc_free(tmp_ctx);
3684 return LDB_ERR_OPERATIONS_ERROR;
3687 /* remove the link */
3688 msg = ldb_msg_new(tmp_ctx);
3690 ldb_module_oom(module);
3691 talloc_free(tmp_ctx);
3692 return LDB_ERR_OPERATIONS_ERROR;
3696 msg->dn = dsdb_dn->dn;
3698 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3699 if (target_attr == NULL) {
3702 attrs[0] = target_attr->lDAPDisplayName;
3704 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3705 LDB_FLAG_MOD_DELETE, &el2);
3706 if (ret != LDB_SUCCESS) {
3707 ldb_module_oom(module);
3708 talloc_free(tmp_ctx);
3709 return LDB_ERR_OPERATIONS_ERROR;
3712 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3714 DSDB_FLAG_NEXT_MODULE |
3715 DSDB_SEARCH_SHOW_EXTENDED_DN,
3718 if (ret != LDB_SUCCESS) {
3719 talloc_free(tmp_ctx);
3723 link_msg = link_res->msgs[0];
3724 link_el = ldb_msg_find_element(link_msg,
3725 target_attr->lDAPDisplayName);
3726 if (link_el == NULL) {
3727 talloc_free(tmp_ctx);
3728 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3732 * This call 'upgrades' the links in link_dns, but we
3733 * do not commit the result back into the database, so
3734 * this is safe to call in FL2000 or on databases that
3735 * have been run at that level in the past.
3737 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3739 target_attr->syntax->ldap_oid, parent);
3740 if (ret != LDB_SUCCESS) {
3741 talloc_free(tmp_ctx);
3745 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3749 target_attr->syntax->ldap_oid, false);
3750 if (ret != LDB_SUCCESS) {
3751 talloc_free(tmp_ctx);
3756 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3757 "Failed to find forward link on %s "
3758 "as %s to remove backlink %s on %s",
3759 ldb_dn_get_linearized(msg->dn),
3760 target_attr->lDAPDisplayName,
3761 sa->lDAPDisplayName,
3762 ldb_dn_get_linearized(dn));
3763 talloc_free(tmp_ctx);
3764 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3768 /* This needs to get the Binary DN, by first searching */
3769 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3772 dn_val = data_blob_string_const(dn_str);
3773 el2->values = &dn_val;
3774 el2->num_values = 1;
3777 * Ensure that we tell the modification to vanish any linked
3778 * attributes (not simply mark them as isDeleted = TRUE)
3780 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3782 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3783 if (ret != LDB_SUCCESS) {
3784 talloc_free(tmp_ctx);
3788 talloc_free(tmp_ctx);
3794 handle update of replication meta data for deletion of objects
3796 This also handles the mapping of delete to a rename operation
3797 to allow deletes to be replicated.
3799 It also handles the incoming deleted objects, to ensure they are
3800 fully deleted here. In that case re_delete is true, and we do not
3801 use this as a signal to change the deleted state, just reinforce it.
3804 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3806 int ret = LDB_ERR_OTHER;
3807 bool retb, disallow_move_on_delete;
3808 struct ldb_dn *old_dn, *new_dn;
3809 const char *rdn_name;
3810 const struct ldb_val *rdn_value, *new_rdn_value;
3812 struct ldb_context *ldb = ldb_module_get_ctx(module);
3813 const struct dsdb_schema *schema;
3814 struct ldb_message *msg, *old_msg;
3815 struct ldb_message_element *el;
3816 TALLOC_CTX *tmp_ctx;
3817 struct ldb_result *res, *parent_res;
3818 static const char * const preserved_attrs[] = {
3819 /* yes, this really is a hard coded list. See MS-ADTS
3820 section 3.1.1.5.5.1.1 */
3823 "dNReferenceUpdate",
3834 "msDS-LastKnownRDN",
3840 "distinguishedName",
3844 "proxiedObjectName",
3846 "nTSecurityDescriptor",
3847 "replPropertyMetaData",
3849 "securityIdentifier",
3857 "userAccountControl",
3864 static const char * const all_attrs[] = {
3865 DSDB_SECRET_ATTRIBUTES,
3869 unsigned int i, el_count = 0;
3870 uint32_t dsdb_flags = 0;
3871 struct replmd_private *replmd_private;
3872 enum deletion_state deletion_state, next_deletion_state;
3874 if (ldb_dn_is_special(req->op.del.dn)) {
3875 return ldb_next_request(module, req);
3879 * We have to allow dbcheck to remove an object that
3880 * is beyond repair, and to do so totally. This could
3881 * mean we we can get a partial object from the other
3882 * DC, causing havoc, so dbcheck suggests
3883 * re-replication first. dbcheck sets both DBCHECK
3884 * and RELAX in this situation.
3886 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3887 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3888 /* really, really remove it */
3889 return ldb_next_request(module, req);
3892 tmp_ctx = talloc_new(ldb);
3895 return LDB_ERR_OPERATIONS_ERROR;
3898 schema = dsdb_get_schema(ldb, tmp_ctx);
3900 talloc_free(tmp_ctx);
3901 return LDB_ERR_OPERATIONS_ERROR;
3904 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3906 /* we need the complete msg off disk, so we can work out which
3907 attributes need to be removed */
3908 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3909 DSDB_FLAG_NEXT_MODULE |
3910 DSDB_SEARCH_SHOW_RECYCLED |
3911 DSDB_SEARCH_REVEAL_INTERNALS |
3912 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3913 if (ret != LDB_SUCCESS) {
3914 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3915 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3916 re_delete ? "re-delete" : "delete",
3917 ldb_dn_get_linearized(old_dn),
3918 ldb_errstring(ldb_module_get_ctx(module)));
3919 talloc_free(tmp_ctx);
3922 old_msg = res->msgs[0];
3924 replmd_deletion_state(module, old_msg,
3926 &next_deletion_state);
3928 /* This supports us noticing an incoming isDeleted and acting on it */
3930 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3931 next_deletion_state = deletion_state;
3934 if (next_deletion_state == OBJECT_REMOVED) {
3936 * We have to prevent objects being deleted, even if
3937 * the administrator really wants them gone, as
3938 * without the tombstone, we can get a partial object
3939 * from the other DC, causing havoc.
3941 * The only other valid case is when the 180 day
3942 * timeout has expired, when relax is specified.
3944 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3945 /* it is already deleted - really remove it this time */
3946 talloc_free(tmp_ctx);
3947 return ldb_next_request(module, req);
3950 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3951 "This check is to prevent corruption of the replicated state.",
3952 ldb_dn_get_linearized(old_msg->dn));
3953 return LDB_ERR_UNWILLING_TO_PERFORM;
3956 rdn_name = ldb_dn_get_rdn_name(old_dn);
3957 rdn_value = ldb_dn_get_rdn_val(old_dn);
3958 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3959 talloc_free(tmp_ctx);
3960 return ldb_operr(ldb);
3963 msg = ldb_msg_new(tmp_ctx);
3965 ldb_module_oom(module);
3966 talloc_free(tmp_ctx);
3967 return LDB_ERR_OPERATIONS_ERROR;
3972 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3973 disallow_move_on_delete =
3974 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3975 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3977 /* work out where we will be renaming this object to */
3978 if (!disallow_move_on_delete) {
3979 struct ldb_dn *deleted_objects_dn;
3980 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3981 &deleted_objects_dn);
3984 * We should not move objects if we can't find the
3985 * deleted objects DN. Not moving (or otherwise
3986 * harming) the Deleted Objects DN itself is handled
3989 if (re_delete && (ret != LDB_SUCCESS)) {
3990 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3991 if (new_dn == NULL) {
3992 ldb_module_oom(module);
3993 talloc_free(tmp_ctx);
3994 return LDB_ERR_OPERATIONS_ERROR;
3996 } else if (ret != LDB_SUCCESS) {
3997 /* this is probably an attempted delete on a partition
3998 * that doesn't allow delete operations, such as the
3999 * schema partition */
4000 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4001 ldb_dn_get_linearized(old_dn));
4002 talloc_free(tmp_ctx);
4003 return LDB_ERR_UNWILLING_TO_PERFORM;
4005 new_dn = deleted_objects_dn;
4008 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4009 if (new_dn == NULL) {
4010 ldb_module_oom(module);
4011 talloc_free(tmp_ctx);
4012 return LDB_ERR_OPERATIONS_ERROR;
4016 /* get the objects GUID from the search we just did */
4017 guid = samdb_result_guid(old_msg, "objectGUID");
4019 if (deletion_state == OBJECT_NOT_DELETED) {
4020 /* Add a formatted child */
4021 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
4023 ldb_dn_escape_value(tmp_ctx, *rdn_value),
4024 GUID_string(tmp_ctx, &guid));
4026 ldb_asprintf_errstring(ldb, __location__
4027 ": Unable to add a formatted child to dn: %s",
4028 ldb_dn_get_linearized(new_dn));
4029 talloc_free(tmp_ctx);
4030 return LDB_ERR_OPERATIONS_ERROR;
4033 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4034 if (ret != LDB_SUCCESS) {
4035 ldb_asprintf_errstring(ldb, __location__
4036 ": Failed to add isDeleted string to the msg");
4037 talloc_free(tmp_ctx);
4040 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4043 * No matter what has happened with other renames etc, try again to
4044 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4047 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4048 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4050 ldb_asprintf_errstring(ldb, __location__
4051 ": Unable to add a prepare rdn of %s",
4052 ldb_dn_get_linearized(rdn));
4053 talloc_free(tmp_ctx);
4054 return LDB_ERR_OPERATIONS_ERROR;
4056 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4058 retb = ldb_dn_add_child(new_dn, rdn);
4060 ldb_asprintf_errstring(ldb, __location__
4061 ": Unable to add rdn %s to base dn: %s",
4062 ldb_dn_get_linearized(rdn),
4063 ldb_dn_get_linearized(new_dn));
4064 talloc_free(tmp_ctx);
4065 return LDB_ERR_OPERATIONS_ERROR;
4070 now we need to modify the object in the following ways:
4072 - add isDeleted=TRUE
4073 - update rDN and name, with new rDN
4074 - remove linked attributes
4075 - remove objectCategory and sAMAccountType
4076 - remove attribs not on the preserved list
4077 - preserved if in above list, or is rDN
4078 - remove all linked attribs from this object
4079 - remove all links from other objects to this object
4080 - add lastKnownParent
4081 - update replPropertyMetaData?
4083 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4086 if (deletion_state == OBJECT_NOT_DELETED) {
4087 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4088 char *parent_dn_str = NULL;
4090 /* we need the storage form of the parent GUID */
4091 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4093 DSDB_FLAG_NEXT_MODULE |
4094 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4095 DSDB_SEARCH_REVEAL_INTERNALS|
4096 DSDB_SEARCH_SHOW_RECYCLED, req);
4097 if (ret != LDB_SUCCESS) {
4098 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4099 "repmd_delete: Failed to %s %s, "
4100 "because we failed to find it's parent (%s): %s",
4101 re_delete ? "re-delete" : "delete",
4102 ldb_dn_get_linearized(old_dn),
4103 ldb_dn_get_linearized(parent_dn),
4104 ldb_errstring(ldb_module_get_ctx(module)));
4105 talloc_free(tmp_ctx);
4110 * Now we can use the DB version,
4111 * it will have the extended DN info in it
4113 parent_dn = parent_res->msgs[0]->dn;
4114 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4117 if (parent_dn_str == NULL) {
4118 talloc_free(tmp_ctx);
4119 return ldb_module_oom(module);
4122 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4124 if (ret != LDB_SUCCESS) {
4125 ldb_asprintf_errstring(ldb, __location__
4126 ": Failed to add lastKnownParent "
4127 "string when deleting %s",
4128 ldb_dn_get_linearized(old_dn));
4129 talloc_free(tmp_ctx);
4132 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4134 if (next_deletion_state == OBJECT_DELETED) {
4135 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4136 if (ret != LDB_SUCCESS) {
4137 ldb_asprintf_errstring(ldb, __location__
4138 ": Failed to add msDS-LastKnownRDN "
4139 "string when deleting %s",
4140 ldb_dn_get_linearized(old_dn));
4141 talloc_free(tmp_ctx);
4144 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4148 switch (next_deletion_state) {
4150 case OBJECT_RECYCLED:
4151 case OBJECT_TOMBSTONE:
4154 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4155 * describes what must be removed from a tombstone
4158 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4159 * describes what must be removed from a recycled
4165 * we also mark it as recycled, meaning this object can't be
4166 * recovered (we are stripping its attributes).
4167 * This is done only if we have this schema object of course ...
4168 * This behavior is identical to the one of Windows 2008R2 which
4169 * always set the isRecycled attribute, even if the recycle-bin is
4170 * not activated and what ever the forest level is.
4172 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4173 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4174 if (ret != LDB_SUCCESS) {
4175 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4176 ldb_module_oom(module);
4177 talloc_free(tmp_ctx);
4180 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4183 replmd_private = talloc_get_type(ldb_module_get_private(module),
4184 struct replmd_private);
4185 /* work out which of the old attributes we will be removing */
4186 for (i=0; i<old_msg->num_elements; i++) {
4187 const struct dsdb_attribute *sa;
4188 el = &old_msg->elements[i];
4189 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4191 talloc_free(tmp_ctx);
4192 return LDB_ERR_OPERATIONS_ERROR;
4194 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4195 /* don't remove the rDN */
4198 if (sa->linkID & 1) {
4200 we have a backlink in this object
4201 that needs to be removed. We're not
4202 allowed to remove it directly
4203 however, so we instead setup a
4204 modify to delete the corresponding
4207 ret = replmd_delete_remove_link(module, schema,
4211 if (ret != LDB_SUCCESS) {
4212 const char *old_dn_str
4213 = ldb_dn_get_linearized(old_dn);
4214 ldb_asprintf_errstring(ldb,
4216 ": Failed to remove backlink of "
4217 "%s when deleting %s: %s",
4220 ldb_errstring(ldb));
4221 talloc_free(tmp_ctx);
4222 return LDB_ERR_OPERATIONS_ERROR;
4224 /* now we continue, which means we
4225 won't remove this backlink
4229 } else if (sa->linkID == 0) {
4230 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4233 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4238 * Ensure that we tell the modification to vanish any linked
4239 * attributes (not simply mark them as isDeleted = TRUE)
4241 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4243 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4244 if (ret != LDB_SUCCESS) {
4245 talloc_free(tmp_ctx);
4246 ldb_module_oom(module);
4253 case OBJECT_DELETED:
4255 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4256 * describes what must be removed from a deleted
4260 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4261 if (ret != LDB_SUCCESS) {
4262 talloc_free(tmp_ctx);
4263 ldb_module_oom(module);
4267 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4268 if (ret != LDB_SUCCESS) {
4269 talloc_free(tmp_ctx);
4270 ldb_module_oom(module);
4280 if (deletion_state == OBJECT_NOT_DELETED) {
4281 const struct dsdb_attribute *sa;
4283 /* work out what the new rdn value is, for updating the
4284 rDN and name fields */
4285 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4286 if (new_rdn_value == NULL) {
4287 talloc_free(tmp_ctx);
4288 return ldb_operr(ldb);
4291 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4293 talloc_free(tmp_ctx);
4294 return LDB_ERR_OPERATIONS_ERROR;
4297 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4299 if (ret != LDB_SUCCESS) {
4300 talloc_free(tmp_ctx);
4303 el->flags = LDB_FLAG_MOD_REPLACE;
4305 el = ldb_msg_find_element(old_msg, "name");
4307 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4308 if (ret != LDB_SUCCESS) {
4309 talloc_free(tmp_ctx);
4312 el->flags = LDB_FLAG_MOD_REPLACE;
4317 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4322 * No matter what has happned with other renames, try again to
4323 * get this to be under the deleted DN.
4325 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4326 /* now rename onto the new DN */
4327 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4328 if (ret != LDB_SUCCESS){
4329 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4330 ldb_dn_get_linearized(old_dn),
4331 ldb_dn_get_linearized(new_dn),
4332 ldb_errstring(ldb)));
4333 talloc_free(tmp_ctx);
4339 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4340 if (ret != LDB_SUCCESS) {
4341 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4342 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4343 talloc_free(tmp_ctx);
4347 talloc_free(tmp_ctx);
4349 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4352 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4354 return replmd_delete_internals(module, req, false);
4358 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4363 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4365 int ret = LDB_ERR_OTHER;
4366 /* TODO: do some error mapping */
4368 /* Let the caller know the full WERROR */
4369 ar->objs->error = status;
4375 static struct replPropertyMetaData1 *
4376 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4377 enum drsuapi_DsAttributeId attid)
4380 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4382 for (i = 0; i < rpmd_ctr->count; i++) {
4383 if (rpmd_ctr->array[i].attid == attid) {
4384 return &rpmd_ctr->array[i];
4392 return true if an update is newer than an existing entry
4393 see section 5.11 of MS-ADTS
4395 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4396 const struct GUID *update_invocation_id,
4397 uint32_t current_version,
4398 uint32_t update_version,
4399 NTTIME current_change_time,
4400 NTTIME update_change_time)
4402 if (update_version != current_version) {
4403 return update_version > current_version;
4405 if (update_change_time != current_change_time) {
4406 return update_change_time > current_change_time;
4408 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4411 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4412 struct replPropertyMetaData1 *new_m)
4414 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4415 &new_m->originating_invocation_id,
4418 cur_m->originating_change_time,
4419 new_m->originating_change_time);
4422 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4423 struct replPropertyMetaData1 *cur_m,
4424 struct replPropertyMetaData1 *new_m)
4429 * If the new replPropertyMetaData entry for this attribute is
4430 * not provided (this happens in the case where we look for
4431 * ATTID_name, but the name was not changed), then the local
4432 * state is clearly still current, as the remote
4433 * server didn't send it due to being older the high watermark
4436 if (new_m == NULL) {
4440 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4442 * if we compare equal then do an
4443 * update. This is used when a client
4444 * asks for a FULL_SYNC, and can be
4445 * used to recover a corrupt
4448 * This call is a bit tricky, what we
4449 * are doing it turning the 'is_newer'
4450 * call into a 'not is older' by
4451 * swapping cur_m and new_m, and negating the
4454 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4457 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4467 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
4469 const struct ldb_val *rdn_val;
4470 const char *rdn_name;
4471 struct ldb_dn *new_dn;
4473 rdn_val = ldb_dn_get_rdn_val(dn);
4474 rdn_name = ldb_dn_get_rdn_name(dn);
4475 if (!rdn_val || !rdn_name) {
4479 new_dn = ldb_dn_copy(mem_ctx, dn);
4484 if (!ldb_dn_remove_child_components(new_dn, 1)) {
4488 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
4490 ldb_dn_escape_value(new_dn, *rdn_val),
4491 GUID_string(new_dn, guid))) {
4500 perform a modify operation which sets the rDN and name attributes to
4501 their current values. This has the effect of changing these
4502 attributes to have been last updated by the current DC. This is
4503 needed to ensure that renames performed as part of conflict
4504 resolution are propogated to other DCs
4506 static int replmd_name_modify(struct replmd_replicated_request *ar,
4507 struct ldb_request *req, struct ldb_dn *dn)
4509 struct ldb_message *msg;
4510 const char *rdn_name;
4511 const struct ldb_val *rdn_val;
4512 const struct dsdb_attribute *rdn_attr;
4515 msg = ldb_msg_new(req);
4521 rdn_name = ldb_dn_get_rdn_name(dn);
4522 if (rdn_name == NULL) {
4526 /* normalize the rdn attribute name */
4527 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4528 if (rdn_attr == NULL) {
4531 rdn_name = rdn_attr->lDAPDisplayName;
4533 rdn_val = ldb_dn_get_rdn_val(dn);
4534 if (rdn_val == NULL) {
4538 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4541 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4544 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4547 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4552 * We have to mark this as a replicated update otherwise
4553 * schema_data may reject a rename in the schema partition
4556 ret = dsdb_module_modify(ar->module, msg,
4557 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4559 if (ret != LDB_SUCCESS) {
4560 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4561 ldb_dn_get_linearized(dn),
4562 ldb_errstring(ldb_module_get_ctx(ar->module))));
4572 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4573 ldb_dn_get_linearized(dn)));
4574 return LDB_ERR_OPERATIONS_ERROR;
4579 callback for conflict DN handling where we have renamed the incoming
4580 record. After renaming it, we need to ensure the change of name and
4581 rDN for the incoming record is seen as an originating update by this DC.
4583 This also handles updating lastKnownParent for entries sent to lostAndFound
4585 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4587 struct replmd_replicated_request *ar =
4588 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4589 struct ldb_dn *conflict_dn = NULL;
4592 if (ares->error != LDB_SUCCESS) {
4593 /* call the normal callback for everything except success */
4594 return replmd_op_callback(req, ares);
4597 switch (req->operation) {
4599 conflict_dn = req->op.add.message->dn;
4602 conflict_dn = req->op.mod.message->dn;
4605 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4608 /* perform a modify of the rDN and name of the record */
4609 ret = replmd_name_modify(ar, req, conflict_dn);
4610 if (ret != LDB_SUCCESS) {
4612 return replmd_op_callback(req, ares);
4615 if (ar->objs->objects[ar->index_current].last_known_parent) {
4616 struct ldb_message *msg = ldb_msg_new(req);
4618 ldb_module_oom(ar->module);
4619 return LDB_ERR_OPERATIONS_ERROR;
4622 msg->dn = req->op.add.message->dn;
4624 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4625 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4626 if (ret != LDB_SUCCESS) {
4627 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4628 ldb_module_oom(ar->module);
4631 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4633 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4634 if (ret != LDB_SUCCESS) {
4635 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4636 ldb_dn_get_linearized(msg->dn),
4637 ldb_errstring(ldb_module_get_ctx(ar->module))));
4643 return replmd_op_callback(req, ares);
4647 callback for replmd_replicated_apply_add()
4648 This copes with the creation of conflict records in the case where
4649 the DN exists, but with a different objectGUID
4651 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))
4653 struct ldb_dn *conflict_dn;
4654 struct replmd_replicated_request *ar =
4655 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4656 struct ldb_result *res;
4657 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4659 const struct ldb_val *omd_value;
4660 struct replPropertyMetaDataBlob omd, *rmd;
4661 enum ndr_err_code ndr_err;
4662 bool rename_incoming_record, rodc;
4663 struct replPropertyMetaData1 *rmd_name, *omd_name;
4664 struct ldb_message *msg;
4665 struct ldb_request *down_req = NULL;
4667 /* call the normal callback for success */
4668 if (ares->error == LDB_SUCCESS) {
4669 return callback(req, ares);
4673 * we have a conflict, and need to decide if we will keep the
4674 * new record or the old record
4677 msg = ar->objs->objects[ar->index_current].msg;
4678 conflict_dn = msg->dn;
4680 /* For failures other than conflicts, fail the whole operation here */
4681 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4682 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4683 ldb_dn_get_linearized(conflict_dn),
4684 ldb_errstring(ldb_module_get_ctx(ar->module)));
4686 return ldb_module_done(ar->req, NULL, NULL,
4687 LDB_ERR_OPERATIONS_ERROR);
4690 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4691 if (ret != LDB_SUCCESS) {
4692 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)));
4693 return ldb_module_done(ar->req, NULL, NULL,
4694 LDB_ERR_OPERATIONS_ERROR);
4700 * We are on an RODC, or were a GC for this
4701 * partition, so we have to fail this until
4702 * someone who owns the partition sorts it
4705 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4706 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4707 " - We must fail the operation until a master for this partition resolves the conflict",
4708 ldb_dn_get_linearized(conflict_dn));
4713 * first we need the replPropertyMetaData attribute from the
4714 * local, conflicting record
4716 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4718 DSDB_FLAG_NEXT_MODULE |
4719 DSDB_SEARCH_SHOW_DELETED |
4720 DSDB_SEARCH_SHOW_RECYCLED, req);
4721 if (ret != LDB_SUCCESS) {
4722 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4723 ldb_dn_get_linearized(conflict_dn)));
4727 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4728 if (omd_value == NULL) {
4729 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4730 ldb_dn_get_linearized(conflict_dn)));
4734 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4735 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4736 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4737 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4738 ldb_dn_get_linearized(conflict_dn)));
4742 rmd = ar->objs->objects[ar->index_current].meta_data;
4745 * we decide which is newer based on the RPMD on the name
4746 * attribute. See [MS-DRSR] ResolveNameConflict.
4748 * We expect omd_name to be present, as this is from a local
4749 * search, but while rmd_name should have been given to us by
4750 * the remote server, if it is missing we just prefer the
4752 * replmd_replPropertyMetaData1_new_should_be_taken()
4754 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4755 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4757 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4758 ldb_dn_get_linearized(conflict_dn)));
4763 * Should we preserve the current record, and so rename the
4764 * incoming record to be a conflict?
4766 rename_incoming_record
4767 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4768 omd_name, rmd_name);
4770 if (rename_incoming_record) {
4772 struct ldb_dn *new_dn;
4774 guid = samdb_result_guid(msg, "objectGUID");
4775 if (GUID_all_zero(&guid)) {
4776 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4777 ldb_dn_get_linearized(conflict_dn)));
4780 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4781 if (new_dn == NULL) {
4782 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4783 ldb_dn_get_linearized(conflict_dn)));
4787 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4788 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4790 /* re-submit the request, but with the new DN */
4791 callback = replmd_op_name_modify_callback;
4794 /* we are renaming the existing record */
4796 struct ldb_dn *new_dn;
4798 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4799 if (GUID_all_zero(&guid)) {
4800 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4801 ldb_dn_get_linearized(conflict_dn)));
4805 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4806 if (new_dn == NULL) {
4807 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4808 ldb_dn_get_linearized(conflict_dn)));
4812 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4813 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4815 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4816 DSDB_FLAG_OWN_MODULE, req);
4817 if (ret != LDB_SUCCESS) {
4818 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4819 ldb_dn_get_linearized(conflict_dn),
4820 ldb_dn_get_linearized(new_dn),
4821 ldb_errstring(ldb_module_get_ctx(ar->module))));
4826 * now we need to ensure that the rename is seen as an
4827 * originating update. We do that with a modify.
4829 ret = replmd_name_modify(ar, req, new_dn);
4830 if (ret != LDB_SUCCESS) {
4834 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4835 ldb_dn_get_linearized(req->op.add.message->dn)));
4838 ret = ldb_build_add_req(&down_req,
4839 ldb_module_get_ctx(ar->module),
4846 if (ret != LDB_SUCCESS) {
4849 LDB_REQ_SET_LOCATION(down_req);
4851 /* current partition control needed by "repmd_op_callback" */
4852 ret = ldb_request_add_control(down_req,
4853 DSDB_CONTROL_CURRENT_PARTITION_OID,
4855 if (ret != LDB_SUCCESS) {
4856 return replmd_replicated_request_error(ar, ret);
4859 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4860 /* this tells the partition module to make it a
4861 partial replica if creating an NC */
4862 ret = ldb_request_add_control(down_req,
4863 DSDB_CONTROL_PARTIAL_REPLICA,
4865 if (ret != LDB_SUCCESS) {
4866 return replmd_replicated_request_error(ar, ret);
4871 * Finally we re-run the add, otherwise the new record won't
4872 * exist, as we are here because of that exact failure!
4874 return ldb_next_request(ar->module, down_req);
4877 /* on failure make the caller get the error. This means
4878 * replication will stop with an error, but there is not much
4881 return ldb_module_done(ar->req, NULL, NULL,
4886 callback for replmd_replicated_apply_add()
4887 This copes with the creation of conflict records in the case where
4888 the DN exists, but with a different objectGUID
4890 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4892 struct replmd_replicated_request *ar =
4893 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4895 if (ar->objs->objects[ar->index_current].last_known_parent) {
4896 /* This is like a conflict DN, where we put the object in LostAndFound
4897 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4898 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4901 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4905 this is called when a new object comes in over DRS
4907 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4909 struct ldb_context *ldb;
4910 struct ldb_request *change_req;
4911 enum ndr_err_code ndr_err;
4912 struct ldb_message *msg;
4913 struct replPropertyMetaDataBlob *md;
4914 struct ldb_val md_value;
4917 bool remote_isDeleted = false;
4920 time_t t = time(NULL);
4921 const struct ldb_val *rdn_val;
4922 struct replmd_private *replmd_private =
4923 talloc_get_type(ldb_module_get_private(ar->module),
4924 struct replmd_private);
4925 unix_to_nt_time(&now, t);
4927 ldb = ldb_module_get_ctx(ar->module);
4928 msg = ar->objs->objects[ar->index_current].msg;
4929 md = ar->objs->objects[ar->index_current].meta_data;
4930 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4932 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4933 if (ret != LDB_SUCCESS) {
4934 return replmd_replicated_request_error(ar, ret);
4937 ret = dsdb_msg_add_guid(msg,
4938 &ar->objs->objects[ar->index_current].object_guid,
4940 if (ret != LDB_SUCCESS) {
4941 return replmd_replicated_request_error(ar, ret);
4944 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4945 if (ret != LDB_SUCCESS) {
4946 return replmd_replicated_request_error(ar, ret);
4949 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4950 if (ret != LDB_SUCCESS) {
4951 return replmd_replicated_request_error(ar, ret);
4954 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4955 if (ret != LDB_SUCCESS) {
4956 return replmd_replicated_request_error(ar, ret);
4959 /* remove any message elements that have zero values */
4960 for (i=0; i<msg->num_elements; i++) {
4961 struct ldb_message_element *el = &msg->elements[i];
4963 if (el->num_values == 0) {
4964 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4965 ldb_asprintf_errstring(ldb, __location__
4966 ": empty objectClass sent on %s, aborting replication\n",
4967 ldb_dn_get_linearized(msg->dn));
4968 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4971 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4973 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4974 msg->num_elements--;
4981 struct GUID_txt_buf guid_txt;
4983 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4984 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4985 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4990 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4991 "isDeleted", false);
4994 * the meta data array is already sorted by the caller, except
4995 * for the RDN, which needs to be added.
4999 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5000 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5001 md, ar, now, is_schema_nc,
5003 if (ret != LDB_SUCCESS) {
5004 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5005 return replmd_replicated_request_error(ar, ret);
5008 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5009 if (ret != LDB_SUCCESS) {
5010 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5011 return replmd_replicated_request_error(ar, ret);
5014 for (i=0; i < md->ctr.ctr1.count; i++) {
5015 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5017 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5018 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5019 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5020 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5021 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5023 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5024 if (ret != LDB_SUCCESS) {
5025 return replmd_replicated_request_error(ar, ret);
5028 replmd_ldb_message_sort(msg, ar->schema);
5030 if (!remote_isDeleted) {
5031 ret = dsdb_module_schedule_sd_propagation(ar->module,
5032 ar->objs->partition_dn,
5034 if (ret != LDB_SUCCESS) {
5035 return replmd_replicated_request_error(ar, ret);
5039 ar->isDeleted = remote_isDeleted;
5041 ret = ldb_build_add_req(&change_req,
5047 replmd_op_add_callback,
5049 LDB_REQ_SET_LOCATION(change_req);
5050 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5052 /* current partition control needed by "repmd_op_callback" */
5053 ret = ldb_request_add_control(change_req,
5054 DSDB_CONTROL_CURRENT_PARTITION_OID,
5056 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5058 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5059 /* this tells the partition module to make it a
5060 partial replica if creating an NC */
5061 ret = ldb_request_add_control(change_req,
5062 DSDB_CONTROL_PARTIAL_REPLICA,
5064 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5067 return ldb_next_request(ar->module, change_req);
5070 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5071 struct ldb_reply *ares)
5073 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5074 struct replmd_replicated_request);
5078 return ldb_module_done(ar->req, NULL, NULL,
5079 LDB_ERR_OPERATIONS_ERROR);
5083 * The error NO_SUCH_OBJECT is not expected, unless the search
5084 * base is the partition DN, and that case doesn't happen here
5085 * because then we wouldn't get a parent_guid_value in any
5088 if (ares->error != LDB_SUCCESS) {
5089 return ldb_module_done(ar->req, ares->controls,
5090 ares->response, ares->error);
5093 switch (ares->type) {
5094 case LDB_REPLY_ENTRY:
5096 struct ldb_message *parent_msg = ares->message;
5097 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5098 struct ldb_dn *parent_dn;
5101 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5102 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5103 /* Per MS-DRSR 4.1.10.6.10
5104 * FindBestParentObject we need to move this
5105 * new object under a deleted object to
5107 struct ldb_dn *nc_root;
5109 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5110 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5111 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5112 "No suitable NC root found for %s. "
5113 "We need to move this object because parent object %s "
5114 "is deleted, but this object is not.",
5115 ldb_dn_get_linearized(msg->dn),
5116 ldb_dn_get_linearized(parent_msg->dn));
5117 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5118 } else if (ret != LDB_SUCCESS) {
5119 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5120 "Unable to find NC root for %s: %s. "
5121 "We need to move this object because parent object %s "
5122 "is deleted, but this object is not.",
5123 ldb_dn_get_linearized(msg->dn),
5124 ldb_errstring(ldb_module_get_ctx(ar->module)),
5125 ldb_dn_get_linearized(parent_msg->dn));
5126 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5129 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5131 DS_GUID_LOSTANDFOUND_CONTAINER,
5133 if (ret != LDB_SUCCESS) {
5134 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5135 "Unable to find LostAndFound Container for %s "
5136 "in partition %s: %s. "
5137 "We need to move this object because parent object %s "
5138 "is deleted, but this object is not.",
5139 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5140 ldb_errstring(ldb_module_get_ctx(ar->module)),
5141 ldb_dn_get_linearized(parent_msg->dn));
5142 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5144 ar->objs->objects[ar->index_current].last_known_parent
5145 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5149 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5152 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5154 comp_num = ldb_dn_get_comp_num(msg->dn);
5156 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5158 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5161 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5163 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5167 case LDB_REPLY_REFERRAL:
5168 /* we ignore referrals */
5171 case LDB_REPLY_DONE:
5173 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5174 struct GUID_txt_buf str_buf;
5175 if (ar->search_msg != NULL) {
5176 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5177 "No parent with GUID %s found for object locally known as %s",
5178 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5179 ldb_dn_get_linearized(ar->search_msg->dn));
5181 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5182 "No parent with GUID %s found for object remotely known as %s",
5183 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5184 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5188 * This error code is really important, as it
5189 * is the flag back to the callers to retry
5190 * this with DRSUAPI_DRS_GET_ANC, and so get
5191 * the parent objects before the child
5194 return ldb_module_done(ar->req, NULL, NULL,
5195 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5198 if (ar->search_msg != NULL) {
5199 ret = replmd_replicated_apply_merge(ar);
5201 ret = replmd_replicated_apply_add(ar);
5203 if (ret != LDB_SUCCESS) {
5204 return ldb_module_done(ar->req, NULL, NULL, ret);
5213 * Look for the parent object, so we put the new object in the right
5214 * place This is akin to NameObject in MS-DRSR - this routine and the
5215 * callbacks find the right parent name, and correct name for this
5219 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5221 struct ldb_context *ldb;
5225 struct ldb_request *search_req;
5226 static const char *attrs[] = {"isDeleted", NULL};
5227 struct GUID_txt_buf guid_str_buf;
5229 ldb = ldb_module_get_ctx(ar->module);
5231 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5232 if (ar->search_msg != NULL) {
5233 return replmd_replicated_apply_merge(ar);
5235 return replmd_replicated_apply_add(ar);
5239 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5242 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5243 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5245 ret = ldb_build_search_req(&search_req,
5248 ar->objs->partition_dn,
5254 replmd_replicated_apply_search_for_parent_callback,
5256 LDB_REQ_SET_LOCATION(search_req);
5258 ret = dsdb_request_add_controls(search_req,
5259 DSDB_SEARCH_SHOW_RECYCLED|
5260 DSDB_SEARCH_SHOW_DELETED|
5261 DSDB_SEARCH_SHOW_EXTENDED_DN);
5262 if (ret != LDB_SUCCESS) {
5266 return ldb_next_request(ar->module, search_req);
5270 handle renames that come in over DRS replication
5272 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5273 struct ldb_message *msg,
5274 struct ldb_request *parent,
5278 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5279 struct ldb_result *res;
5280 struct ldb_dn *conflict_dn;
5281 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5282 const struct ldb_val *omd_value;
5283 struct replPropertyMetaDataBlob omd, *rmd;
5284 enum ndr_err_code ndr_err;
5285 bool rename_incoming_record, rodc;
5286 struct replPropertyMetaData1 *rmd_name, *omd_name;
5287 struct ldb_dn *new_dn;
5290 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5291 ldb_dn_get_linearized(ar->search_msg->dn),
5292 ldb_dn_get_linearized(msg->dn)));
5295 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5296 DSDB_FLAG_NEXT_MODULE, ar->req);
5297 if (ret == LDB_SUCCESS) {
5298 talloc_free(tmp_ctx);
5303 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5304 talloc_free(tmp_ctx);
5305 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5306 ldb_dn_get_linearized(ar->search_msg->dn),
5307 ldb_dn_get_linearized(msg->dn),
5308 ldb_errstring(ldb_module_get_ctx(ar->module)));
5312 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5313 if (ret != LDB_SUCCESS) {
5314 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5315 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5316 ldb_errstring(ldb_module_get_ctx(ar->module)));
5317 return LDB_ERR_OPERATIONS_ERROR;
5320 * we have a conflict, and need to decide if we will keep the
5321 * new record or the old record
5324 conflict_dn = msg->dn;
5328 * We are on an RODC, or were a GC for this
5329 * partition, so we have to fail this until
5330 * someone who owns the partition sorts it
5333 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5334 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5335 " - We must fail the operation until a master for this partition resolves the conflict",
5336 ldb_dn_get_linearized(conflict_dn));
5341 * first we need the replPropertyMetaData attribute from the
5344 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5346 DSDB_FLAG_NEXT_MODULE |
5347 DSDB_SEARCH_SHOW_DELETED |
5348 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5349 if (ret != LDB_SUCCESS) {
5350 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5351 ldb_dn_get_linearized(conflict_dn)));
5355 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5356 if (omd_value == NULL) {
5357 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5358 ldb_dn_get_linearized(conflict_dn)));
5362 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5363 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5364 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5365 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5366 ldb_dn_get_linearized(conflict_dn)));
5370 rmd = ar->objs->objects[ar->index_current].meta_data;
5373 * we decide which is newer based on the RPMD on the name
5374 * attribute. See [MS-DRSR] ResolveNameConflict.
5376 * We expect omd_name to be present, as this is from a local
5377 * search, but while rmd_name should have been given to us by
5378 * the remote server, if it is missing we just prefer the
5380 * replmd_replPropertyMetaData1_new_should_be_taken()
5382 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5383 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5385 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5386 ldb_dn_get_linearized(conflict_dn)));
5391 * Should we preserve the current record, and so rename the
5392 * incoming record to be a conflict?
5394 rename_incoming_record =
5395 !replmd_replPropertyMetaData1_new_should_be_taken(
5396 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5397 omd_name, rmd_name);
5399 if (rename_incoming_record) {
5401 new_dn = replmd_conflict_dn(msg, msg->dn,
5402 &ar->objs->objects[ar->index_current].object_guid);
5403 if (new_dn == NULL) {
5404 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5405 "Failed to form conflict DN for %s\n",
5406 ldb_dn_get_linearized(msg->dn));
5408 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5411 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5412 DSDB_FLAG_NEXT_MODULE, ar->req);
5413 if (ret != LDB_SUCCESS) {
5414 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5415 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5416 ldb_dn_get_linearized(conflict_dn),
5417 ldb_dn_get_linearized(ar->search_msg->dn),
5418 ldb_dn_get_linearized(new_dn),
5419 ldb_errstring(ldb_module_get_ctx(ar->module)));
5420 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5428 /* we are renaming the existing record */
5430 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5431 if (GUID_all_zero(&guid)) {
5432 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5433 ldb_dn_get_linearized(conflict_dn)));
5437 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
5438 if (new_dn == NULL) {
5439 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5440 ldb_dn_get_linearized(conflict_dn)));
5444 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5445 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5447 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5448 DSDB_FLAG_OWN_MODULE, ar->req);
5449 if (ret != LDB_SUCCESS) {
5450 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5451 ldb_dn_get_linearized(conflict_dn),
5452 ldb_dn_get_linearized(new_dn),
5453 ldb_errstring(ldb_module_get_ctx(ar->module))));
5458 * now we need to ensure that the rename is seen as an
5459 * originating update. We do that with a modify.
5461 ret = replmd_name_modify(ar, ar->req, new_dn);
5462 if (ret != LDB_SUCCESS) {
5466 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5467 ldb_dn_get_linearized(ar->search_msg->dn),
5468 ldb_dn_get_linearized(msg->dn)));
5471 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5472 DSDB_FLAG_NEXT_MODULE, ar->req);
5473 if (ret != LDB_SUCCESS) {
5474 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5475 ldb_dn_get_linearized(ar->search_msg->dn),
5476 ldb_dn_get_linearized(msg->dn),
5477 ldb_errstring(ldb_module_get_ctx(ar->module))));
5483 * On failure make the caller get the error
5484 * This means replication will stop with an error,
5485 * but there is not much else we can do. In the
5486 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5490 talloc_free(tmp_ctx);
5495 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5497 struct ldb_context *ldb;
5498 struct ldb_request *change_req;
5499 enum ndr_err_code ndr_err;
5500 struct ldb_message *msg;
5501 struct replPropertyMetaDataBlob *rmd;
5502 struct replPropertyMetaDataBlob omd;
5503 const struct ldb_val *omd_value;
5504 struct replPropertyMetaDataBlob nmd;
5505 struct ldb_val nmd_value;
5506 struct GUID remote_parent_guid;
5509 unsigned int removed_attrs = 0;
5511 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5512 bool isDeleted = false;
5513 bool local_isDeleted = false;
5514 bool remote_isDeleted = false;
5515 bool take_remote_isDeleted = false;
5516 bool sd_updated = false;
5517 bool renamed = false;
5518 bool is_schema_nc = false;
5520 const struct ldb_val *old_rdn, *new_rdn;
5521 struct replmd_private *replmd_private =
5522 talloc_get_type(ldb_module_get_private(ar->module),
5523 struct replmd_private);
5525 time_t t = time(NULL);
5526 unix_to_nt_time(&now, t);
5528 ldb = ldb_module_get_ctx(ar->module);
5529 msg = ar->objs->objects[ar->index_current].msg;
5531 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5533 rmd = ar->objs->objects[ar->index_current].meta_data;
5537 /* find existing meta data */
5538 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5540 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5541 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5542 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5543 nt_status = ndr_map_error2ntstatus(ndr_err);
5544 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5547 if (omd.version != 1) {
5548 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5553 struct GUID_txt_buf guid_txt;
5555 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5556 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
5559 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5561 ndr_print_struct_string(s,
5562 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5563 "existing replPropertyMetaData",
5565 ndr_print_struct_string(s,
5566 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5567 "incoming replPropertyMetaData",
5572 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5573 "isDeleted", false);
5574 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5575 "isDeleted", false);
5578 * Fill in the remote_parent_guid with the GUID or an all-zero
5581 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5582 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5584 remote_parent_guid = GUID_zero();
5588 * To ensure we follow a complex rename chain around, we have
5589 * to confirm that the DN is the same (mostly to confirm the
5590 * RDN) and the parentGUID is the same.
5592 * This ensures we keep things under the correct parent, which
5593 * replmd_replicated_handle_rename() will do.
5596 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5597 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5601 * handle renames, even just by case that come in over
5602 * DRS. Changes in the parent DN don't hit us here,
5603 * because the search for a parent will clean up those
5606 * We also have already filtered out the case where
5607 * the peer has an older name to what we have (see
5608 * replmd_replicated_apply_search_callback())
5610 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5613 if (ret != LDB_SUCCESS) {
5614 ldb_debug(ldb, LDB_DEBUG_FATAL,
5615 "replmd_replicated_request rename %s => %s failed - %s\n",
5616 ldb_dn_get_linearized(ar->search_msg->dn),
5617 ldb_dn_get_linearized(msg->dn),
5618 ldb_errstring(ldb));
5619 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5622 if (renamed == true) {
5624 * Set the callback to one that will fix up the name
5625 * metadata on the new conflict DN
5627 callback = replmd_op_name_modify_callback;
5632 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5633 nmd.ctr.ctr1.array = talloc_array(ar,
5634 struct replPropertyMetaData1,
5635 nmd.ctr.ctr1.count);
5636 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5638 /* first copy the old meta data */
5639 for (i=0; i < omd.ctr.ctr1.count; i++) {
5640 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5645 /* now merge in the new meta data */
5646 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5649 for (j=0; j < ni; j++) {
5652 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5656 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5657 ar->objs->dsdb_repl_flags,
5658 &nmd.ctr.ctr1.array[j],
5659 &rmd->ctr.ctr1.array[i]);
5661 /* replace the entry */
5662 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5663 if (ar->seq_num == 0) {
5664 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5665 if (ret != LDB_SUCCESS) {
5666 return replmd_replicated_request_error(ar, ret);
5669 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5670 switch (nmd.ctr.ctr1.array[j].attid) {
5671 case DRSUAPI_ATTID_ntSecurityDescriptor:
5674 case DRSUAPI_ATTID_isDeleted:
5675 take_remote_isDeleted = true;
5684 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5685 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5686 msg->elements[i-removed_attrs].name,
5687 ldb_dn_get_linearized(msg->dn),
5688 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5691 /* we don't want to apply this change so remove the attribute */
5692 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5699 if (found) continue;
5701 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5702 if (ar->seq_num == 0) {
5703 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5704 if (ret != LDB_SUCCESS) {
5705 return replmd_replicated_request_error(ar, ret);
5708 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5709 switch (nmd.ctr.ctr1.array[ni].attid) {
5710 case DRSUAPI_ATTID_ntSecurityDescriptor:
5713 case DRSUAPI_ATTID_isDeleted:
5714 take_remote_isDeleted = true;
5723 * finally correct the size of the meta_data array
5725 nmd.ctr.ctr1.count = ni;
5727 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5728 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5731 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5732 &nmd, ar, now, is_schema_nc,
5734 if (ret != LDB_SUCCESS) {
5735 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5736 return replmd_replicated_request_error(ar, ret);
5740 * sort the new meta data array
5742 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5743 if (ret != LDB_SUCCESS) {
5744 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5749 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5752 * This also controls SD propagation below
5754 if (take_remote_isDeleted) {
5755 isDeleted = remote_isDeleted;
5757 isDeleted = local_isDeleted;
5760 ar->isDeleted = isDeleted;
5763 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5765 if (msg->num_elements == 0) {
5766 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5769 return replmd_replicated_apply_isDeleted(ar);
5772 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5773 ar->index_current, msg->num_elements);
5779 if (sd_updated && !isDeleted) {
5780 ret = dsdb_module_schedule_sd_propagation(ar->module,
5781 ar->objs->partition_dn,
5783 if (ret != LDB_SUCCESS) {
5784 return ldb_operr(ldb);
5788 /* create the meta data value */
5789 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5790 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5791 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5792 nt_status = ndr_map_error2ntstatus(ndr_err);
5793 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5797 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5798 * and replPopertyMetaData attributes
5800 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5801 if (ret != LDB_SUCCESS) {
5802 return replmd_replicated_request_error(ar, ret);
5804 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5805 if (ret != LDB_SUCCESS) {
5806 return replmd_replicated_request_error(ar, ret);
5808 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5809 if (ret != LDB_SUCCESS) {
5810 return replmd_replicated_request_error(ar, ret);
5813 replmd_ldb_message_sort(msg, ar->schema);
5815 /* we want to replace the old values */
5816 for (i=0; i < msg->num_elements; i++) {
5817 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5818 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5819 if (msg->elements[i].num_values == 0) {
5820 ldb_asprintf_errstring(ldb, __location__
5821 ": objectClass removed on %s, aborting replication\n",
5822 ldb_dn_get_linearized(msg->dn));
5823 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5829 struct GUID_txt_buf guid_txt;
5831 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5832 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5833 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5838 ret = ldb_build_mod_req(&change_req,
5846 LDB_REQ_SET_LOCATION(change_req);
5847 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5849 /* current partition control needed by "repmd_op_callback" */
5850 ret = ldb_request_add_control(change_req,
5851 DSDB_CONTROL_CURRENT_PARTITION_OID,
5853 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5855 return ldb_next_request(ar->module, change_req);
5858 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5859 struct ldb_reply *ares)
5861 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5862 struct replmd_replicated_request);
5866 return ldb_module_done(ar->req, NULL, NULL,
5867 LDB_ERR_OPERATIONS_ERROR);
5869 if (ares->error != LDB_SUCCESS &&
5870 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5871 return ldb_module_done(ar->req, ares->controls,
5872 ares->response, ares->error);
5875 switch (ares->type) {
5876 case LDB_REPLY_ENTRY:
5877 ar->search_msg = talloc_steal(ar, ares->message);
5880 case LDB_REPLY_REFERRAL:
5881 /* we ignore referrals */
5884 case LDB_REPLY_DONE:
5886 struct replPropertyMetaData1 *md_remote;
5887 struct replPropertyMetaData1 *md_local;
5889 struct replPropertyMetaDataBlob omd;
5890 const struct ldb_val *omd_value;
5891 struct replPropertyMetaDataBlob *rmd;
5892 struct ldb_message *msg;
5894 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5895 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5898 * This is the ADD case, find the appropriate parent,
5899 * as this object doesn't exist locally:
5901 if (ar->search_msg == NULL) {
5902 ret = replmd_replicated_apply_search_for_parent(ar);
5903 if (ret != LDB_SUCCESS) {
5904 return ldb_module_done(ar->req, NULL, NULL, ret);
5911 * Otherwise, in the MERGE case, work out if we are
5912 * attempting a rename, and if so find the parent the
5913 * newly renamed object wants to belong under (which
5914 * may not be the parent in it's attached string DN
5916 rmd = ar->objs->objects[ar->index_current].meta_data;
5920 /* find existing meta data */
5921 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5923 enum ndr_err_code ndr_err;
5924 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5925 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5926 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5927 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5928 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5931 if (omd.version != 1) {
5932 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5936 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5938 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5939 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5940 && GUID_all_zero(&ar->local_parent_guid)) {
5941 DEBUG(0, ("Refusing to replicate new version of %s "
5942 "as local object has an all-zero parentGUID attribute, "
5943 "despite not being an NC root\n",
5944 ldb_dn_get_linearized(ar->search_msg->dn)));
5945 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5949 * now we need to check for double renames. We could have a
5950 * local rename pending which our replication partner hasn't
5951 * received yet. We choose which one wins by looking at the
5952 * attribute stamps on the two objects, the newer one wins.
5954 * This also simply applies the correct algorithms for
5955 * determining if a change was made to name at all, or
5956 * if the object has just been renamed under the same
5959 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5960 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5962 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5963 ldb_dn_get_linearized(ar->search_msg->dn)));
5964 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5968 * if there is no name attribute given then we have to assume the
5969 * object we've received has the older name
5971 if (replmd_replPropertyMetaData1_new_should_be_taken(
5972 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5973 md_local, md_remote)) {
5974 struct GUID_txt_buf p_guid_local;
5975 struct GUID_txt_buf p_guid_remote;
5976 msg = ar->objs->objects[ar->index_current].msg;
5978 /* Merge on the existing object, with rename */
5980 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5981 "as incoming object changing to %s under %s\n",
5982 ldb_dn_get_linearized(ar->search_msg->dn),
5983 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5984 ldb_dn_get_linearized(msg->dn),
5985 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5987 ret = replmd_replicated_apply_search_for_parent(ar);
5989 struct GUID_txt_buf p_guid_local;
5990 struct GUID_txt_buf p_guid_remote;
5991 msg = ar->objs->objects[ar->index_current].msg;
5994 * Merge on the existing object, force no
5995 * rename (code below just to explain why in
5999 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6000 ldb_dn_get_linearized(msg->dn)) == 0) {
6001 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6002 GUID_equal(&ar->local_parent_guid,
6003 ar->objs->objects[ar->index_current].parent_guid)
6005 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6006 "despite incoming object changing parent to %s\n",
6007 ldb_dn_get_linearized(ar->search_msg->dn),
6008 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6009 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6013 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6014 " and rejecting older rename to %s under %s\n",
6015 ldb_dn_get_linearized(ar->search_msg->dn),
6016 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6017 ldb_dn_get_linearized(msg->dn),
6018 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6022 * This assignment ensures that the strcmp()
6023 * and GUID_equal() calls in
6024 * replmd_replicated_apply_merge() avoids the
6027 ar->objs->objects[ar->index_current].parent_guid =
6028 &ar->local_parent_guid;
6030 msg->dn = ar->search_msg->dn;
6031 ret = replmd_replicated_apply_merge(ar);
6033 if (ret != LDB_SUCCESS) {
6034 return ldb_module_done(ar->req, NULL, NULL, ret);
6043 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6045 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6047 struct ldb_context *ldb;
6051 struct ldb_request *search_req;
6052 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6053 "parentGUID", "instanceType",
6054 "replPropertyMetaData", "nTSecurityDescriptor",
6055 "isDeleted", NULL };
6056 struct GUID_txt_buf guid_str_buf;
6058 if (ar->index_current >= ar->objs->num_objects) {
6059 /* done with it, go to next stage */
6060 return replmd_replicated_uptodate_vector(ar);
6063 ldb = ldb_module_get_ctx(ar->module);
6064 ar->search_msg = NULL;
6065 ar->isDeleted = false;
6067 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6070 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6071 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6073 ret = ldb_build_search_req(&search_req,
6076 ar->objs->partition_dn,
6082 replmd_replicated_apply_search_callback,
6084 LDB_REQ_SET_LOCATION(search_req);
6086 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6088 if (ret != LDB_SUCCESS) {
6092 return ldb_next_request(ar->module, search_req);
6096 * This is essentially a wrapper for replmd_replicated_apply_next()
6098 * This is needed to ensure that both codepaths call this handler.
6100 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6102 struct ldb_dn *deleted_objects_dn;
6103 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6104 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6105 &deleted_objects_dn);
6106 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6108 * Do a delete here again, so that if there is
6109 * anything local that conflicts with this
6110 * object being deleted, it is removed. This
6111 * includes links. See MS-DRSR 4.1.10.6.9
6114 * If the object is already deleted, and there
6115 * is no more work required, it doesn't do
6119 /* This has been updated to point to the DN we eventually did the modify on */
6121 struct ldb_request *del_req;
6122 struct ldb_result *res;
6124 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6126 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6130 res = talloc_zero(tmp_ctx, struct ldb_result);
6132 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6133 talloc_free(tmp_ctx);
6137 /* Build a delete request, which hopefully will artually turn into nothing */
6138 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6142 ldb_modify_default_callback,
6144 LDB_REQ_SET_LOCATION(del_req);
6145 if (ret != LDB_SUCCESS) {
6146 talloc_free(tmp_ctx);
6151 * This is the guts of the call, call back
6152 * into our delete code, but setting the
6153 * re_delete flag so we delete anything that
6154 * shouldn't be there on a deleted or recycled
6157 ret = replmd_delete_internals(ar->module, del_req, true);
6158 if (ret == LDB_SUCCESS) {
6159 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6162 talloc_free(tmp_ctx);
6163 if (ret != LDB_SUCCESS) {
6168 ar->index_current++;
6169 return replmd_replicated_apply_next(ar);
6172 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6173 struct ldb_reply *ares)
6175 struct ldb_context *ldb;
6176 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6177 struct replmd_replicated_request);
6178 ldb = ldb_module_get_ctx(ar->module);
6181 return ldb_module_done(ar->req, NULL, NULL,
6182 LDB_ERR_OPERATIONS_ERROR);
6184 if (ares->error != LDB_SUCCESS) {
6185 return ldb_module_done(ar->req, ares->controls,
6186 ares->response, ares->error);
6189 if (ares->type != LDB_REPLY_DONE) {
6190 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6191 return ldb_module_done(ar->req, NULL, NULL,
6192 LDB_ERR_OPERATIONS_ERROR);
6197 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6200 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6202 struct ldb_context *ldb;
6203 struct ldb_request *change_req;
6204 enum ndr_err_code ndr_err;
6205 struct ldb_message *msg;
6206 struct replUpToDateVectorBlob ouv;
6207 const struct ldb_val *ouv_value;
6208 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6209 struct replUpToDateVectorBlob nuv;
6210 struct ldb_val nuv_value;
6211 struct ldb_message_element *nuv_el = NULL;
6212 struct ldb_message_element *orf_el = NULL;
6213 struct repsFromToBlob nrf;
6214 struct ldb_val *nrf_value = NULL;
6215 struct ldb_message_element *nrf_el = NULL;
6219 time_t t = time(NULL);
6222 uint32_t instanceType;
6224 ldb = ldb_module_get_ctx(ar->module);
6225 ruv = ar->objs->uptodateness_vector;
6231 unix_to_nt_time(&now, t);
6233 if (ar->search_msg == NULL) {
6234 /* this happens for a REPL_OBJ call where we are
6235 creating the target object by replicating it. The
6236 subdomain join code does this for the partition DN
6238 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6239 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6242 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6243 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6244 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6245 ldb_dn_get_linearized(ar->search_msg->dn)));
6246 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6250 * first create the new replUpToDateVector
6252 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6254 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6255 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6256 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6257 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6258 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6261 if (ouv.version != 2) {
6262 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6267 * the new uptodateness vector will at least
6268 * contain 1 entry, one for the source_dsa
6270 * plus optional values from our old vector and the one from the source_dsa
6272 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6273 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6274 nuv.ctr.ctr2.cursors = talloc_array(ar,
6275 struct drsuapi_DsReplicaCursor2,
6276 nuv.ctr.ctr2.count);
6277 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6279 /* first copy the old vector */
6280 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6281 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6285 /* merge in the source_dsa vector is available */
6286 for (i=0; (ruv && i < ruv->count); i++) {
6289 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6290 &ar->our_invocation_id)) {
6294 for (j=0; j < ni; j++) {
6295 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6296 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6302 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6303 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6308 if (found) continue;
6310 /* if it's not there yet, add it */
6311 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6316 * finally correct the size of the cursors array
6318 nuv.ctr.ctr2.count = ni;
6323 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6326 * create the change ldb_message
6328 msg = ldb_msg_new(ar);
6329 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6330 msg->dn = ar->search_msg->dn;
6332 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6333 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6334 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6335 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6336 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6338 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6339 if (ret != LDB_SUCCESS) {
6340 return replmd_replicated_request_error(ar, ret);
6342 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6345 * now create the new repsFrom value from the given repsFromTo1 structure
6349 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6350 nrf.ctr.ctr1.last_attempt = now;
6351 nrf.ctr.ctr1.last_success = now;
6352 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6355 * first see if we already have a repsFrom value for the current source dsa
6356 * if so we'll later replace this value
6358 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6360 for (i=0; i < orf_el->num_values; i++) {
6361 struct repsFromToBlob *trf;
6363 trf = talloc(ar, struct repsFromToBlob);
6364 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6366 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6367 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6368 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6369 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6370 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6373 if (trf->version != 1) {
6374 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6378 * we compare the source dsa objectGUID not the invocation_id
6379 * because we want only one repsFrom value per source dsa
6380 * and when the invocation_id of the source dsa has changed we don't need
6381 * the old repsFrom with the old invocation_id
6383 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6384 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6390 nrf_value = &orf_el->values[i];
6395 * copy over all old values to the new ldb_message
6397 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6398 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6403 * if we haven't found an old repsFrom value for the current source dsa
6404 * we'll add a new value
6407 struct ldb_val zero_value;
6408 ZERO_STRUCT(zero_value);
6409 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6410 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6412 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6415 /* we now fill the value which is already attached to ldb_message */
6416 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6418 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6419 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6420 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6421 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6425 * the ldb_message_element for the attribute, has all the old values and the new one
6426 * so we'll replace the whole attribute with all values
6428 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6430 if (CHECK_DEBUGLVL(4)) {
6431 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
6432 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6436 /* prepare the ldb_modify() request */
6437 ret = ldb_build_mod_req(&change_req,
6443 replmd_replicated_uptodate_modify_callback,
6445 LDB_REQ_SET_LOCATION(change_req);
6446 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6448 return ldb_next_request(ar->module, change_req);
6451 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6452 struct ldb_reply *ares)
6454 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6455 struct replmd_replicated_request);
6459 return ldb_module_done(ar->req, NULL, NULL,
6460 LDB_ERR_OPERATIONS_ERROR);
6462 if (ares->error != LDB_SUCCESS &&
6463 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6464 return ldb_module_done(ar->req, ares->controls,
6465 ares->response, ares->error);
6468 switch (ares->type) {
6469 case LDB_REPLY_ENTRY:
6470 ar->search_msg = talloc_steal(ar, ares->message);
6473 case LDB_REPLY_REFERRAL:
6474 /* we ignore referrals */
6477 case LDB_REPLY_DONE:
6478 ret = replmd_replicated_uptodate_modify(ar);
6479 if (ret != LDB_SUCCESS) {
6480 return ldb_module_done(ar->req, NULL, NULL, ret);
6489 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6491 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6492 struct replmd_private *replmd_private =
6493 talloc_get_type_abort(ldb_module_get_private(ar->module),
6494 struct replmd_private);
6496 static const char *attrs[] = {
6497 "replUpToDateVector",
6502 struct ldb_request *search_req;
6504 ar->search_msg = NULL;
6507 * Let the caller know that we did an originating updates
6509 ar->objs->originating_updates = replmd_private->originating_updates;
6511 ret = ldb_build_search_req(&search_req,
6514 ar->objs->partition_dn,
6520 replmd_replicated_uptodate_search_callback,
6522 LDB_REQ_SET_LOCATION(search_req);
6523 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6525 return ldb_next_request(ar->module, search_req);
6530 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6532 struct ldb_context *ldb;
6533 struct dsdb_extended_replicated_objects *objs;
6534 struct replmd_replicated_request *ar;
6535 struct ldb_control **ctrls;
6538 struct replmd_private *replmd_private =
6539 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6541 ldb = ldb_module_get_ctx(module);
6543 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6545 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6547 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6548 return LDB_ERR_PROTOCOL_ERROR;
6551 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6552 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6553 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6554 return LDB_ERR_PROTOCOL_ERROR;
6557 ar = replmd_ctx_init(module, req);
6559 return LDB_ERR_OPERATIONS_ERROR;
6561 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6562 ar->apply_mode = true;
6564 ar->schema = dsdb_get_schema(ldb, ar);
6566 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6568 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6569 return LDB_ERR_CONSTRAINT_VIOLATION;
6572 ctrls = req->controls;
6574 if (req->controls) {
6575 req->controls = talloc_memdup(ar, req->controls,
6576 talloc_get_size(req->controls));
6577 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6580 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6581 if (ret != LDB_SUCCESS) {
6585 /* If this change contained linked attributes in the body
6586 * (rather than in the links section) we need to update
6587 * backlinks in linked_attributes */
6588 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6589 if (ret != LDB_SUCCESS) {
6593 ar->controls = req->controls;
6594 req->controls = ctrls;
6596 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
6598 /* save away the linked attributes for the end of the
6600 for (i=0; i<ar->objs->linked_attributes_count; i++) {
6601 struct la_entry *la_entry;
6603 if (replmd_private->la_ctx == NULL) {
6604 replmd_private->la_ctx = talloc_new(replmd_private);
6606 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6607 if (la_entry == NULL) {
6609 return LDB_ERR_OPERATIONS_ERROR;
6611 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6612 if (la_entry->la == NULL) {
6613 talloc_free(la_entry);
6615 return LDB_ERR_OPERATIONS_ERROR;
6617 *la_entry->la = ar->objs->linked_attributes[i];
6619 /* we need to steal the non-scalars so they stay
6620 around until the end of the transaction */
6621 talloc_steal(la_entry->la, la_entry->la->identifier);
6622 talloc_steal(la_entry->la, la_entry->la->value.blob);
6624 DLIST_ADD(replmd_private->la_list, la_entry);
6627 return replmd_replicated_apply_next(ar);
6631 process one linked attribute structure
6633 static int replmd_process_linked_attribute(struct ldb_module *module,
6634 struct replmd_private *replmd_private,
6635 struct la_entry *la_entry,
6636 struct ldb_request *parent)
6638 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6639 struct ldb_context *ldb = ldb_module_get_ctx(module);
6640 struct ldb_message *msg;
6641 struct ldb_message *target_msg = NULL;
6642 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6643 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6645 const struct dsdb_attribute *attr;
6646 struct dsdb_dn *dsdb_dn;
6647 uint64_t seq_num = 0;
6648 struct ldb_message_element *old_el;
6650 time_t t = time(NULL);
6651 struct ldb_result *res;
6652 struct ldb_result *target_res;
6653 const char *attrs[4];
6654 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6655 struct parsed_dn *pdn_list, *pdn, *next;
6656 struct GUID guid = GUID_zero();
6658 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6660 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6661 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6664 linked_attributes[0]:
6665 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6667 identifier: struct drsuapi_DsReplicaObjectIdentifier
6668 __ndr_size : 0x0000003a (58)
6669 __ndr_size_sid : 0x00000000 (0)
6670 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6672 __ndr_size_dn : 0x00000000 (0)
6674 attid : DRSUAPI_ATTID_member (0x1F)
6675 value: struct drsuapi_DsAttributeValue
6676 __ndr_size : 0x0000007e (126)
6678 blob : DATA_BLOB length=126
6679 flags : 0x00000001 (1)
6680 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6681 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6682 meta_data: struct drsuapi_DsReplicaMetaData
6683 version : 0x00000015 (21)
6684 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6685 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6686 originating_usn : 0x000000000001e19c (123292)
6688 (for cases where the link is to a normal DN)
6689 &target: struct drsuapi_DsReplicaObjectIdentifier3
6690 __ndr_size : 0x0000007e (126)
6691 __ndr_size_sid : 0x0000001c (28)
6692 guid : 7639e594-db75-4086-b0d4-67890ae46031
6693 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6694 __ndr_size_dn : 0x00000022 (34)
6695 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6698 /* find the attribute being modified */
6699 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6701 struct GUID_txt_buf guid_str;
6702 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6704 GUID_buf_string(&la->identifier->guid,
6706 talloc_free(tmp_ctx);
6707 return LDB_ERR_OPERATIONS_ERROR;
6710 attrs[0] = attr->lDAPDisplayName;
6711 attrs[1] = "isDeleted";
6712 attrs[2] = "isRecycled";
6715 /* get the existing message from the db for the object with
6716 this GUID, returning attribute being modified. We will then
6717 use this msg as the basis for a modify call */
6718 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6719 DSDB_FLAG_NEXT_MODULE |
6720 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6721 DSDB_SEARCH_SHOW_RECYCLED |
6722 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6723 DSDB_SEARCH_REVEAL_INTERNALS,
6725 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6726 if (ret != LDB_SUCCESS) {
6727 talloc_free(tmp_ctx);
6730 if (res->count != 1) {
6731 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6732 GUID_string(tmp_ctx, &la->identifier->guid));
6733 talloc_free(tmp_ctx);
6734 return LDB_ERR_NO_SUCH_OBJECT;
6739 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6740 * ProcessLinkValue, because link updates are not applied to
6741 * recycled and tombstone objects. We don't have to delete
6742 * any existing link, that should have happened when the
6743 * object deletion was replicated or initiated.
6746 replmd_deletion_state(module, msg, &deletion_state, NULL);
6748 if (deletion_state >= OBJECT_RECYCLED) {
6749 talloc_free(tmp_ctx);
6753 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6754 if (old_el == NULL) {
6755 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6756 if (ret != LDB_SUCCESS) {
6757 ldb_module_oom(module);
6758 talloc_free(tmp_ctx);
6759 return LDB_ERR_OPERATIONS_ERROR;
6762 old_el->flags = LDB_FLAG_MOD_REPLACE;
6765 /* parse the existing links */
6766 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
6767 attr->syntax->ldap_oid, parent);
6769 if (ret != LDB_SUCCESS) {
6770 talloc_free(tmp_ctx);
6774 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6775 if (!W_ERROR_IS_OK(status)) {
6776 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6777 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6778 talloc_free(tmp_ctx);
6779 return LDB_ERR_OPERATIONS_ERROR;
6782 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6783 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6785 * This strange behaviour (allowing a NULL/missing
6786 * GUID) originally comes from:
6788 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6789 * Author: Andrew Tridgell <tridge@samba.org>
6790 * Date: Mon Dec 21 21:21:55 2009 +1100
6792 * s4-drs: cope better with NULL GUIDS from DRS
6794 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6795 * need to match by DN if possible when seeing if we should update an
6798 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6801 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6802 dsdb_dn->dn, attrs2,
6803 DSDB_FLAG_NEXT_MODULE |
6804 DSDB_SEARCH_SHOW_RECYCLED |
6805 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6806 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6808 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6809 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6811 ldb_dn_get_linearized(dsdb_dn->dn),
6812 ldb_dn_get_linearized(msg->dn));
6813 talloc_free(tmp_ctx);
6814 return LDB_ERR_OPERATIONS_ERROR;
6816 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6817 NULL, LDB_SCOPE_SUBTREE,
6819 DSDB_FLAG_NEXT_MODULE |
6820 DSDB_SEARCH_SHOW_RECYCLED |
6821 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6822 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6825 GUID_string(tmp_ctx, &guid));
6828 if (ret != LDB_SUCCESS) {
6829 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6830 GUID_string(tmp_ctx, &guid),
6831 ldb_errstring(ldb_module_get_ctx(module)));
6832 talloc_free(tmp_ctx);
6836 if (target_res->count == 0) {
6837 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6838 GUID_string(tmp_ctx, &guid),
6839 ldb_dn_get_linearized(dsdb_dn->dn)));
6840 } else if (target_res->count != 1) {
6841 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6842 GUID_string(tmp_ctx, &guid));
6843 talloc_free(tmp_ctx);
6844 return LDB_ERR_OPERATIONS_ERROR;
6846 target_msg = target_res->msgs[0];
6847 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6851 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6852 * ProcessLinkValue, because link updates are not applied to
6853 * recycled and tombstone objects. We don't have to delete
6854 * any existing link, that should have happened when the
6855 * object deletion was replicated or initiated.
6857 replmd_deletion_state(module, target_msg,
6858 &target_deletion_state, NULL);
6860 if (target_deletion_state >= OBJECT_RECYCLED) {
6861 talloc_free(tmp_ctx);
6865 /* see if this link already exists */
6866 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
6869 dsdb_dn->extra_part, 0,
6871 attr->syntax->ldap_oid,
6873 if (ret != LDB_SUCCESS) {
6874 talloc_free(tmp_ctx);
6880 /* see if this update is newer than what we have already */
6881 struct GUID invocation_id = GUID_zero();
6882 uint32_t version = 0;
6883 uint32_t originating_usn = 0;
6884 NTTIME change_time = 0;
6885 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6887 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6888 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6889 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6890 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6892 if (!replmd_update_is_newer(&invocation_id,
6893 &la->meta_data.originating_invocation_id,
6895 la->meta_data.version,
6897 la->meta_data.originating_change_time)) {
6898 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6899 old_el->name, ldb_dn_get_linearized(msg->dn),
6900 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6901 talloc_free(tmp_ctx);
6905 /* get a seq_num for this change */
6906 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6907 if (ret != LDB_SUCCESS) {
6908 talloc_free(tmp_ctx);
6912 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6913 /* remove the existing backlink */
6914 ret = replmd_add_backlink(module, replmd_private,
6919 if (ret != LDB_SUCCESS) {
6920 talloc_free(tmp_ctx);
6925 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6926 &la->meta_data.originating_invocation_id,
6927 la->meta_data.originating_usn, seq_num,
6928 la->meta_data.originating_change_time,
6929 la->meta_data.version,
6931 if (ret != LDB_SUCCESS) {
6932 talloc_free(tmp_ctx);
6937 /* add the new backlink */
6938 ret = replmd_add_backlink(module, replmd_private,
6943 if (ret != LDB_SUCCESS) {
6944 talloc_free(tmp_ctx);
6950 /* get a seq_num for this change */
6951 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6952 if (ret != LDB_SUCCESS) {
6953 talloc_free(tmp_ctx);
6957 * We know where the new one needs to be, from the *next
6958 * pointer into pdn_list.
6961 offset = old_el->num_values;
6963 if (next->dsdb_dn == NULL) {
6964 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
6965 attr->syntax->ldap_oid);
6966 if (ret != LDB_SUCCESS) {
6970 offset = next - pdn_list;
6971 if (offset > old_el->num_values) {
6972 talloc_free(tmp_ctx);
6973 return LDB_ERR_OPERATIONS_ERROR;
6977 old_el->values = talloc_realloc(msg->elements, old_el->values,
6978 struct ldb_val, old_el->num_values+1);
6979 if (!old_el->values) {
6980 ldb_module_oom(module);
6981 return LDB_ERR_OPERATIONS_ERROR;
6984 if (offset != old_el->num_values) {
6985 memmove(&old_el->values[offset + 1], &old_el->values[offset],
6986 (old_el->num_values - offset) * sizeof(old_el->values[0]));
6989 old_el->num_values++;
6991 ret = replmd_build_la_val(tmp_ctx, &old_el->values[offset], dsdb_dn,
6992 &la->meta_data.originating_invocation_id,
6993 la->meta_data.originating_usn, seq_num,
6994 la->meta_data.originating_change_time,
6995 la->meta_data.version,
6997 if (ret != LDB_SUCCESS) {
6998 talloc_free(tmp_ctx);
7003 ret = replmd_add_backlink(module, replmd_private,
7008 if (ret != LDB_SUCCESS) {
7009 talloc_free(tmp_ctx);
7015 /* we only change whenChanged and uSNChanged if the seq_num
7017 ret = add_time_element(msg, "whenChanged", t);
7018 if (ret != LDB_SUCCESS) {
7019 talloc_free(tmp_ctx);
7024 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7025 if (ret != LDB_SUCCESS) {
7026 talloc_free(tmp_ctx);
7031 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7032 if (old_el == NULL) {
7033 talloc_free(tmp_ctx);
7034 return ldb_operr(ldb);
7037 ret = dsdb_check_single_valued_link(attr, old_el);
7038 if (ret != LDB_SUCCESS) {
7039 talloc_free(tmp_ctx);
7043 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7045 ret = linked_attr_modify(module, msg, parent);
7046 if (ret != LDB_SUCCESS) {
7047 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7049 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
7050 talloc_free(tmp_ctx);
7054 talloc_free(tmp_ctx);
7059 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7061 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7062 return replmd_extended_replicated_objects(module, req);
7065 return ldb_next_request(module, req);
7070 we hook into the transaction operations to allow us to
7071 perform the linked attribute updates at the end of the whole
7072 transaction. This allows a forward linked attribute to be created
7073 before the object is created. During a vampire, w2k8 sends us linked
7074 attributes before the objects they are part of.
7076 static int replmd_start_transaction(struct ldb_module *module)
7078 /* create our private structure for this transaction */
7079 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7080 struct replmd_private);
7081 replmd_txn_cleanup(replmd_private);
7083 /* free any leftover mod_usn records from cancelled
7085 while (replmd_private->ncs) {
7086 struct nc_entry *e = replmd_private->ncs;
7087 DLIST_REMOVE(replmd_private->ncs, e);
7091 replmd_private->originating_updates = false;
7093 return ldb_next_start_trans(module);
7097 on prepare commit we loop over our queued la_context structures and
7100 static int replmd_prepare_commit(struct ldb_module *module)
7102 struct replmd_private *replmd_private =
7103 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7104 struct la_entry *la, *prev;
7108 * Walk the list of linked attributes from DRS replication.
7110 * We walk backwards, to do the first entry first, as we
7111 * added the entries with DLIST_ADD() which puts them at the
7114 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7115 prev = DLIST_PREV(la);
7116 DLIST_REMOVE(replmd_private->la_list, la);
7117 ret = replmd_process_linked_attribute(module, replmd_private,
7119 if (ret != LDB_SUCCESS) {
7120 replmd_txn_cleanup(replmd_private);
7125 replmd_txn_cleanup(replmd_private);
7127 /* possibly change @REPLCHANGED */
7128 ret = replmd_notify_store(module, NULL);
7129 if (ret != LDB_SUCCESS) {
7133 return ldb_next_prepare_commit(module);
7136 static int replmd_del_transaction(struct ldb_module *module)
7138 struct replmd_private *replmd_private =
7139 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7140 replmd_txn_cleanup(replmd_private);
7142 return ldb_next_del_trans(module);
7146 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7147 .name = "repl_meta_data",
7148 .init_context = replmd_init,
7150 .modify = replmd_modify,
7151 .rename = replmd_rename,
7152 .del = replmd_delete,
7153 .extended = replmd_extended,
7154 .start_transaction = replmd_start_transaction,
7155 .prepare_commit = replmd_prepare_commit,
7156 .del_transaction = replmd_del_transaction,
7159 int ldb_repl_meta_data_module_init(const char *version)
7161 LDB_MODULE_CHECK_VERSION(version);
7162 return ldb_register_module(&ldb_repl_meta_data_module_ops);