4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 Copyright (C) Andrew Tridgell 2005-2009
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "dsdb/common/util.h"
43 #include "../libds/common/flags.h"
44 #include "librpc/gen_ndr/irpc.h"
45 #include "librpc/gen_ndr/ndr_misc.h"
46 #include "librpc/gen_ndr/ndr_drsuapi.h"
47 #include "librpc/gen_ndr/ndr_drsblobs.h"
48 #include "param/param.h"
49 #include "libcli/security/security.h"
50 #include "lib/util/dlinklist.h"
51 #include "dsdb/samdb/ldb_modules/util.h"
52 #include "lib/util/tsort.h"
55 #define DBGC_CLASS DBGC_DRS_REPL
57 /* the RMD_VERSION for linked attributes starts from 1 */
58 #define RMD_VERSION_INITIAL 1
61 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
62 * Deleted Objects Container
64 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
66 struct replmd_private {
68 struct la_entry *la_list;
70 struct nc_entry *prev, *next;
73 uint64_t mod_usn_urgent;
75 struct ldb_dn *schema_dn;
76 bool originating_updates;
81 struct la_entry *next, *prev;
82 struct drsuapi_DsReplicaLinkedAttribute *la;
83 uint32_t dsdb_repl_flags;
86 struct replmd_replicated_request {
87 struct ldb_module *module;
88 struct ldb_request *req;
90 const struct dsdb_schema *schema;
91 struct GUID our_invocation_id;
93 /* the controls we pass down */
94 struct ldb_control **controls;
97 * Backlinks for the replmd_add() case (we want to create
98 * backlinks after creating the user, but before the end of
101 struct la_backlink *la_backlinks;
103 /* details for the mode where we apply a bunch of inbound replication meessages */
105 uint32_t index_current;
106 struct dsdb_extended_replicated_objects *objs;
108 struct ldb_message *search_msg;
109 struct GUID local_parent_guid;
119 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
120 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
121 static int replmd_check_upgrade_links(struct ldb_context *ldb,
122 struct parsed_dn *dns, uint32_t count,
123 struct ldb_message_element *el,
124 const char *ldap_oid);
125 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
126 struct la_entry *la);
127 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
128 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
129 uint64_t usn, uint64_t local_usn, NTTIME nttime,
130 uint32_t version, bool deleted);
132 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
133 struct ldb_context *ldb,
135 const char *rdn_name,
136 const struct ldb_val *rdn_value,
139 enum urgent_situation {
140 REPL_URGENT_ON_CREATE = 1,
141 REPL_URGENT_ON_UPDATE = 2,
142 REPL_URGENT_ON_DELETE = 4
145 enum deletion_state {
146 OBJECT_NOT_DELETED=1,
153 static void replmd_deletion_state(struct ldb_module *module,
154 const struct ldb_message *msg,
155 enum deletion_state *current_state,
156 enum deletion_state *next_state)
159 bool enabled = false;
162 *current_state = OBJECT_REMOVED;
163 if (next_state != NULL) {
164 *next_state = OBJECT_REMOVED;
169 ret = dsdb_recyclebin_enabled(module, &enabled);
170 if (ret != LDB_SUCCESS) {
174 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
176 *current_state = OBJECT_TOMBSTONE;
177 if (next_state != NULL) {
178 *next_state = OBJECT_REMOVED;
183 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
184 *current_state = OBJECT_RECYCLED;
185 if (next_state != NULL) {
186 *next_state = OBJECT_REMOVED;
191 *current_state = OBJECT_DELETED;
192 if (next_state != NULL) {
193 *next_state = OBJECT_RECYCLED;
198 *current_state = OBJECT_NOT_DELETED;
199 if (next_state == NULL) {
204 *next_state = OBJECT_DELETED;
206 *next_state = OBJECT_TOMBSTONE;
210 static const struct {
211 const char *update_name;
212 enum urgent_situation repl_situation;
213 } urgent_objects[] = {
214 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
215 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
216 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
217 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
218 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
219 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
223 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
224 static const char *urgent_attrs[] = {
227 "userAccountControl",
232 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
233 enum urgent_situation situation)
236 for (i=0; urgent_objects[i].update_name; i++) {
238 if ((situation & urgent_objects[i].repl_situation) == 0) {
242 for (j=0; j<objectclass_el->num_values; j++) {
243 const struct ldb_val *v = &objectclass_el->values[j];
244 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
252 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
254 if (ldb_attr_in_list(urgent_attrs, el->name)) {
260 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
263 initialise the module
264 allocate the private structure and build the list
265 of partition DNs for use by replmd_notify()
267 static int replmd_init(struct ldb_module *module)
269 struct replmd_private *replmd_private;
270 struct ldb_context *ldb = ldb_module_get_ctx(module);
271 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
272 struct ldb_dn *samba_dsdb_dn;
273 struct ldb_result *res;
275 TALLOC_CTX *frame = talloc_stackframe();
276 replmd_private = talloc_zero(module, struct replmd_private);
277 if (replmd_private == NULL) {
280 return LDB_ERR_OPERATIONS_ERROR;
282 ldb_module_set_private(module, replmd_private);
284 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
286 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
287 if (!samba_dsdb_dn) {
292 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
293 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
294 if (ret == LDB_SUCCESS) {
295 replmd_private->sorted_links
296 = ldb_msg_check_string_attribute(res->msgs[0],
297 SAMBA_COMPATIBLE_FEATURES_ATTR,
298 SAMBA_SORTED_LINKS_FEATURE);
302 return ldb_next_init(module);
306 cleanup our per-transaction contexts
308 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
310 talloc_free(replmd_private->la_ctx);
311 replmd_private->la_list = NULL;
312 replmd_private->la_ctx = NULL;
318 struct la_backlink *next, *prev;
319 const char *attr_name;
320 struct ldb_dn *forward_dn;
321 struct GUID target_guid;
326 a ldb_modify request operating on modules below the
329 static int linked_attr_modify(struct ldb_module *module,
330 const struct ldb_message *message,
331 struct ldb_request *parent)
333 struct ldb_request *mod_req;
335 struct ldb_context *ldb = ldb_module_get_ctx(module);
336 TALLOC_CTX *tmp_ctx = talloc_new(module);
337 struct ldb_result *res;
339 res = talloc_zero(tmp_ctx, struct ldb_result);
341 talloc_free(tmp_ctx);
342 return ldb_oom(ldb_module_get_ctx(module));
345 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
349 ldb_modify_default_callback,
351 LDB_REQ_SET_LOCATION(mod_req);
352 if (ret != LDB_SUCCESS) {
353 talloc_free(tmp_ctx);
357 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
359 if (ret != LDB_SUCCESS) {
363 /* Run the new request */
364 ret = ldb_next_request(module, mod_req);
366 if (ret == LDB_SUCCESS) {
367 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
370 talloc_free(tmp_ctx);
375 process a backlinks we accumulated during a transaction, adding and
376 deleting the backlinks from the target objects
378 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
380 struct ldb_dn *target_dn, *source_dn;
382 struct ldb_context *ldb = ldb_module_get_ctx(module);
383 struct ldb_message *msg;
384 TALLOC_CTX *frame = talloc_stackframe();
390 - construct ldb_message
391 - either an add or a delete
393 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
394 if (ret != LDB_SUCCESS) {
395 struct GUID_txt_buf guid_str;
396 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
397 GUID_buf_string(&bl->target_guid, &guid_str));
398 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
403 msg = ldb_msg_new(frame);
405 ldb_module_oom(module);
407 return LDB_ERR_OPERATIONS_ERROR;
410 source_dn = ldb_dn_copy(frame, bl->forward_dn);
412 ldb_module_oom(module);
414 return LDB_ERR_OPERATIONS_ERROR;
416 /* Filter down to the attributes we want in the backlink */
417 const char *accept[] = { "GUID", "SID", NULL };
418 ldb_dn_extended_filter(source_dn, accept);
421 /* construct a ldb_message for adding/deleting the backlink */
423 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
425 ldb_module_oom(module);
427 return LDB_ERR_OPERATIONS_ERROR;
429 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
430 if (ret != LDB_SUCCESS) {
434 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
436 /* a backlink should never be single valued. Unfortunately the
437 exchange schema has a attribute
438 msExchBridgeheadedLocalConnectorsDNBL which is single
439 valued and a backlink. We need to cope with that by
440 ignoring the single value flag */
441 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
443 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
444 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
445 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
446 cope with possible corruption where the backlink has
447 already been removed */
448 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
449 ldb_dn_get_linearized(target_dn),
450 ldb_dn_get_linearized(source_dn),
451 ldb_errstring(ldb)));
453 } else if (ret != LDB_SUCCESS) {
454 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
455 bl->active?"add":"remove",
456 ldb_dn_get_linearized(source_dn),
457 ldb_dn_get_linearized(target_dn),
467 add a backlink to the list of backlinks to add/delete in the prepare
470 forward_dn is stolen onto the defereed context
472 static int replmd_defer_add_backlink(struct ldb_module *module,
473 struct replmd_private *replmd_private,
474 const struct dsdb_schema *schema,
475 struct replmd_replicated_request *ac,
476 struct ldb_dn *forward_dn,
477 struct GUID *target_guid, bool active,
478 const struct dsdb_attribute *schema_attr,
479 struct ldb_request *parent)
481 const struct dsdb_attribute *target_attr;
482 struct la_backlink *bl;
484 bl = talloc(ac, struct la_backlink);
486 ldb_module_oom(module);
487 return LDB_ERR_OPERATIONS_ERROR;
490 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
493 * windows 2003 has a broken schema where the
494 * definition of msDS-IsDomainFor is missing (which is
495 * supposed to be the backlink of the
496 * msDS-HasDomainNCs attribute
501 bl->attr_name = target_attr->lDAPDisplayName;
502 bl->forward_dn = talloc_steal(bl, forward_dn);
503 bl->target_guid = *target_guid;
506 DLIST_ADD(ac->la_backlinks, bl);
512 add a backlink to the list of backlinks to add/delete in the prepare
515 static int replmd_add_backlink(struct ldb_module *module,
516 struct replmd_private *replmd_private,
517 const struct dsdb_schema *schema,
518 struct ldb_dn *forward_dn,
519 struct GUID *target_guid, bool active,
520 const struct dsdb_attribute *schema_attr,
521 struct ldb_request *parent)
523 const struct dsdb_attribute *target_attr;
524 struct la_backlink bl;
527 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
530 * windows 2003 has a broken schema where the
531 * definition of msDS-IsDomainFor is missing (which is
532 * supposed to be the backlink of the
533 * msDS-HasDomainNCs attribute
538 bl.attr_name = target_attr->lDAPDisplayName;
539 bl.forward_dn = forward_dn;
540 bl.target_guid = *target_guid;
543 ret = replmd_process_backlink(module, &bl, parent);
549 * Callback for most write operations in this module:
551 * notify the repl task that a object has changed. The notifies are
552 * gathered up in the replmd_private structure then written to the
553 * @REPLCHANGED object in each partition during the prepare_commit
555 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
558 struct replmd_replicated_request *ac =
559 talloc_get_type_abort(req->context, struct replmd_replicated_request);
560 struct replmd_private *replmd_private =
561 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
562 struct nc_entry *modified_partition;
563 struct ldb_control *partition_ctrl;
564 const struct dsdb_control_current_partition *partition;
566 struct ldb_control **controls;
568 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
570 controls = ares->controls;
571 if (ldb_request_get_control(ac->req,
572 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
574 * Remove the current partition control from what we pass up
575 * the chain if it hasn't been requested manually.
577 controls = ldb_controls_except_specified(ares->controls, ares,
581 if (ares->error != LDB_SUCCESS) {
582 struct GUID_txt_buf guid_txt;
583 struct ldb_message *msg = NULL;
586 if (ac->apply_mode == false) {
587 DBG_NOTICE("Originating update failure. Error is: %s\n",
588 ldb_strerror(ares->error));
589 return ldb_module_done(ac->req, controls,
590 ares->response, ares->error);
593 msg = ac->objs->objects[ac->index_current].msg;
595 * Set at DBG_NOTICE as once these start to happe, they
596 * will happen a lot until resolved, due to repeated
597 * replication. The caller will probably print the
598 * ldb error string anyway.
600 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
601 ldb_dn_get_linearized(msg->dn),
602 ldb_strerror(ares->error));
604 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
609 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
610 ac->search_msg == NULL ? "ADD" : "MODIFY",
611 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
615 return ldb_module_done(ac->req, controls,
616 ares->response, ares->error);
619 if (ares->type != LDB_REPLY_DONE) {
620 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
621 return ldb_module_done(ac->req, NULL,
622 NULL, LDB_ERR_OPERATIONS_ERROR);
625 if (ac->apply_mode == false) {
626 struct la_backlink *bl;
628 * process our backlink list after an replmd_add(),
629 * creating and deleting backlinks as necessary (this
630 * code is sync). The other cases are handled inline
633 for (bl=ac->la_backlinks; bl; bl=bl->next) {
634 ret = replmd_process_backlink(ac->module, bl, ac->req);
635 if (ret != LDB_SUCCESS) {
636 return ldb_module_done(ac->req, NULL,
642 if (!partition_ctrl) {
643 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
644 return ldb_module_done(ac->req, NULL,
645 NULL, LDB_ERR_OPERATIONS_ERROR);
648 partition = talloc_get_type_abort(partition_ctrl->data,
649 struct dsdb_control_current_partition);
651 if (ac->seq_num > 0) {
652 for (modified_partition = replmd_private->ncs; modified_partition;
653 modified_partition = modified_partition->next) {
654 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
659 if (modified_partition == NULL) {
660 modified_partition = talloc_zero(replmd_private, struct nc_entry);
661 if (!modified_partition) {
662 ldb_oom(ldb_module_get_ctx(ac->module));
663 return ldb_module_done(ac->req, NULL,
664 NULL, LDB_ERR_OPERATIONS_ERROR);
666 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
667 if (!modified_partition->dn) {
668 ldb_oom(ldb_module_get_ctx(ac->module));
669 return ldb_module_done(ac->req, NULL,
670 NULL, LDB_ERR_OPERATIONS_ERROR);
672 DLIST_ADD(replmd_private->ncs, modified_partition);
675 if (ac->seq_num > modified_partition->mod_usn) {
676 modified_partition->mod_usn = ac->seq_num;
678 modified_partition->mod_usn_urgent = ac->seq_num;
681 if (!ac->apply_mode) {
682 replmd_private->originating_updates = true;
686 if (ac->apply_mode) {
687 ret = replmd_replicated_apply_isDeleted(ac);
688 if (ret != LDB_SUCCESS) {
689 return ldb_module_done(ac->req, NULL, NULL, ret);
693 /* free the partition control container here, for the
694 * common path. Other cases will have it cleaned up
695 * eventually with the ares */
696 talloc_free(partition_ctrl);
697 return ldb_module_done(ac->req, controls,
698 ares->response, LDB_SUCCESS);
704 * update a @REPLCHANGED record in each partition if there have been
705 * any writes of replicated data in the partition
707 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
709 struct replmd_private *replmd_private =
710 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
712 while (replmd_private->ncs) {
714 struct nc_entry *modified_partition = replmd_private->ncs;
716 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
717 modified_partition->mod_usn,
718 modified_partition->mod_usn_urgent, parent);
719 if (ret != LDB_SUCCESS) {
720 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
721 ldb_dn_get_linearized(modified_partition->dn)));
725 if (ldb_dn_compare(modified_partition->dn,
726 replmd_private->schema_dn) == 0) {
727 struct ldb_result *ext_res;
728 ret = dsdb_module_extended(module,
729 replmd_private->schema_dn,
731 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
733 DSDB_FLAG_NEXT_MODULE,
735 if (ret != LDB_SUCCESS) {
738 talloc_free(ext_res);
741 DLIST_REMOVE(replmd_private->ncs, modified_partition);
742 talloc_free(modified_partition);
750 created a replmd_replicated_request context
752 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
753 struct ldb_request *req)
755 struct ldb_context *ldb;
756 struct replmd_replicated_request *ac;
757 const struct GUID *our_invocation_id;
759 ldb = ldb_module_get_ctx(module);
761 ac = talloc_zero(req, struct replmd_replicated_request);
770 ac->schema = dsdb_get_schema(ldb, ac);
772 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
773 "replmd_modify: no dsdb_schema loaded");
774 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
779 /* get our invocationId */
780 our_invocation_id = samdb_ntds_invocation_id(ldb);
781 if (!our_invocation_id) {
782 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
783 "replmd_add: unable to find invocationId\n");
787 ac->our_invocation_id = *our_invocation_id;
793 add a time element to a record
795 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
797 struct ldb_message_element *el;
801 if (ldb_msg_find_element(msg, attr) != NULL) {
805 s = ldb_timestring(msg, t);
807 return LDB_ERR_OPERATIONS_ERROR;
810 ret = ldb_msg_add_string(msg, attr, s);
811 if (ret != LDB_SUCCESS) {
815 el = ldb_msg_find_element(msg, attr);
816 /* always set as replace. This works because on add ops, the flag
818 el->flags = LDB_FLAG_MOD_REPLACE;
824 add a uint64_t element to a record
826 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
827 const char *attr, uint64_t v)
829 struct ldb_message_element *el;
832 if (ldb_msg_find_element(msg, attr) != NULL) {
836 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
837 if (ret != LDB_SUCCESS) {
841 el = ldb_msg_find_element(msg, attr);
842 /* always set as replace. This works because on add ops, the flag
844 el->flags = LDB_FLAG_MOD_REPLACE;
849 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
850 const struct replPropertyMetaData1 *m2,
851 const uint32_t *rdn_attid)
854 * This assignment seems inoccous, but it is critical for the
855 * system, as we need to do the comparisons as a unsigned
856 * quantity, not signed (enums are signed integers)
858 uint32_t attid_1 = m1->attid;
859 uint32_t attid_2 = m2->attid;
861 if (attid_1 == attid_2) {
866 * See above regarding this being an unsigned comparison.
867 * Otherwise when the high bit is set on non-standard
868 * attributes, they would end up first, before objectClass
871 return attid_1 > attid_2 ? 1 : -1;
874 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
875 struct replPropertyMetaDataCtr1 *ctr1,
878 if (ctr1->count == 0) {
879 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
880 "No elements found in replPropertyMetaData for %s!\n",
881 ldb_dn_get_linearized(dn));
882 return LDB_ERR_CONSTRAINT_VIOLATION;
885 /* the objectClass attribute is value 0x00000000, so must be first */
886 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
887 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
888 "No objectClass found in replPropertyMetaData for %s!\n",
889 ldb_dn_get_linearized(dn));
890 return LDB_ERR_OBJECT_CLASS_VIOLATION;
896 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
897 struct replPropertyMetaDataCtr1 *ctr1,
900 /* Note this is O(n^2) for the almost-sorted case, which this is */
901 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
902 replmd_replPropertyMetaData1_attid_sort);
903 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
906 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
907 const struct ldb_message_element *e2,
908 const struct dsdb_schema *schema)
910 const struct dsdb_attribute *a1;
911 const struct dsdb_attribute *a2;
914 * TODO: make this faster by caching the dsdb_attribute pointer
915 * on the ldb_messag_element
918 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
919 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
922 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
926 return strcasecmp(e1->name, e2->name);
928 if (a1->attributeID_id == a2->attributeID_id) {
931 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
934 static void replmd_ldb_message_sort(struct ldb_message *msg,
935 const struct dsdb_schema *schema)
937 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
940 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
941 const struct GUID *invocation_id,
942 uint64_t local_usn, NTTIME nttime);
944 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
946 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
947 struct ldb_message_element *el, struct parsed_dn **pdn,
948 const char *ldap_oid, struct ldb_request *parent);
950 static int check_parsed_dn_duplicates(struct ldb_module *module,
951 struct ldb_message_element *el,
952 struct parsed_dn *pdn);
955 fix up linked attributes in replmd_add.
956 This involves setting up the right meta-data in extended DN
957 components, and creating backlinks to the object
959 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
960 struct replmd_private *replmd_private,
961 struct ldb_message_element *el,
962 struct replmd_replicated_request *ac,
964 struct ldb_dn *forward_dn,
965 const struct dsdb_attribute *sa,
966 struct ldb_request *parent)
969 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
970 struct ldb_context *ldb = ldb_module_get_ctx(module);
971 struct parsed_dn *pdn;
972 /* We will take a reference to the schema in replmd_add_backlink */
973 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
974 struct ldb_val *new_values = NULL;
977 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
978 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
980 ldb_asprintf_errstring(ldb,
981 "Attribute %s is single valued but "
982 "more than one value has been supplied",
984 talloc_free(tmp_ctx);
985 return LDB_ERR_CONSTRAINT_VIOLATION;
988 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
989 sa->syntax->ldap_oid, parent);
990 if (ret != LDB_SUCCESS) {
991 talloc_free(tmp_ctx);
995 ret = check_parsed_dn_duplicates(module, el, pdn);
996 if (ret != LDB_SUCCESS) {
997 talloc_free(tmp_ctx);
1001 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1002 if (new_values == NULL) {
1003 ldb_module_oom(module);
1004 talloc_free(tmp_ctx);
1005 return LDB_ERR_OPERATIONS_ERROR;
1008 for (i = 0; i < el->num_values; i++) {
1009 struct parsed_dn *p = &pdn[i];
1010 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1011 &ac->our_invocation_id,
1013 if (ret != LDB_SUCCESS) {
1014 talloc_free(tmp_ctx);
1018 ret = replmd_defer_add_backlink(module, replmd_private,
1020 forward_dn, &p->guid, true, sa,
1022 if (ret != LDB_SUCCESS) {
1023 talloc_free(tmp_ctx);
1027 new_values[i] = *p->v;
1029 el->values = talloc_steal(mem_ctx, new_values);
1031 talloc_free(tmp_ctx);
1035 static int replmd_add_make_extended_dn(struct ldb_request *req,
1036 const DATA_BLOB *guid_blob,
1037 struct ldb_dn **_extended_dn)
1040 const DATA_BLOB *sid_blob;
1041 /* Calculate an extended DN for any linked attributes */
1042 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1044 return LDB_ERR_OPERATIONS_ERROR;
1046 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1047 if (ret != LDB_SUCCESS) {
1051 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1052 if (sid_blob != NULL) {
1053 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1054 if (ret != LDB_SUCCESS) {
1058 *_extended_dn = extended_dn;
1063 intercept add requests
1065 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1067 struct ldb_context *ldb;
1068 struct ldb_control *control;
1069 struct replmd_replicated_request *ac;
1070 enum ndr_err_code ndr_err;
1071 struct ldb_request *down_req;
1072 struct ldb_message *msg;
1073 const DATA_BLOB *guid_blob;
1074 DATA_BLOB guid_blob_stack;
1076 uint8_t guid_data[16];
1077 struct replPropertyMetaDataBlob nmd;
1078 struct ldb_val nmd_value;
1079 struct ldb_dn *extended_dn = NULL;
1082 * The use of a time_t here seems odd, but as the NTTIME
1083 * elements are actually declared as NTTIME_1sec in the IDL,
1084 * getting a higher resolution timestamp is not required.
1086 time_t t = time(NULL);
1091 unsigned int functional_level;
1093 bool allow_add_guid = false;
1094 bool remove_current_guid = false;
1095 bool is_urgent = false;
1096 bool is_schema_nc = false;
1097 struct ldb_message_element *objectclass_el;
1098 struct replmd_private *replmd_private =
1099 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1101 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1102 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1104 allow_add_guid = true;
1107 /* do not manipulate our control entries */
1108 if (ldb_dn_is_special(req->op.add.message->dn)) {
1109 return ldb_next_request(module, req);
1112 ldb = ldb_module_get_ctx(module);
1114 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1116 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1117 if (guid_blob != NULL) {
1118 if (!allow_add_guid) {
1119 ldb_set_errstring(ldb,
1120 "replmd_add: it's not allowed to add an object with objectGUID!");
1121 return LDB_ERR_UNWILLING_TO_PERFORM;
1123 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1124 if (!NT_STATUS_IS_OK(status)) {
1125 ldb_set_errstring(ldb,
1126 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1127 return LDB_ERR_UNWILLING_TO_PERFORM;
1129 /* we remove this attribute as it can be a string and
1130 * will not be treated correctly and then we will re-add
1131 * it later on in the good format */
1132 remove_current_guid = true;
1136 guid = GUID_random();
1138 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1140 /* This can't fail */
1141 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1142 (ndr_push_flags_fn_t)ndr_push_GUID);
1143 guid_blob = &guid_blob_stack;
1146 ac = replmd_ctx_init(module, req);
1148 return ldb_module_oom(module);
1151 functional_level = dsdb_functional_level(ldb);
1153 /* Get a sequence number from the backend */
1154 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1155 if (ret != LDB_SUCCESS) {
1160 /* we have to copy the message as the caller might have it as a const */
1161 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1165 return LDB_ERR_OPERATIONS_ERROR;
1168 /* generated times */
1169 unix_to_nt_time(&now, t);
1170 time_str = ldb_timestring(msg, t);
1174 return LDB_ERR_OPERATIONS_ERROR;
1176 if (remove_current_guid) {
1177 ldb_msg_remove_attr(msg,"objectGUID");
1181 * remove autogenerated attributes
1183 ldb_msg_remove_attr(msg, "whenCreated");
1184 ldb_msg_remove_attr(msg, "whenChanged");
1185 ldb_msg_remove_attr(msg, "uSNCreated");
1186 ldb_msg_remove_attr(msg, "uSNChanged");
1187 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1190 * readd replicated attributes
1192 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1193 if (ret != LDB_SUCCESS) {
1199 /* build the replication meta_data */
1202 nmd.ctr.ctr1.count = msg->num_elements;
1203 nmd.ctr.ctr1.array = talloc_array(msg,
1204 struct replPropertyMetaData1,
1205 nmd.ctr.ctr1.count);
1206 if (!nmd.ctr.ctr1.array) {
1209 return LDB_ERR_OPERATIONS_ERROR;
1212 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1214 for (i=0; i < msg->num_elements;) {
1215 struct ldb_message_element *e = &msg->elements[i];
1216 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1217 const struct dsdb_attribute *sa;
1219 if (e->name[0] == '@') {
1224 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1226 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1227 "replmd_add: attribute '%s' not defined in schema\n",
1230 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1233 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1234 /* if the attribute is not replicated (0x00000001)
1235 * or constructed (0x00000004) it has no metadata
1241 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1242 if (extended_dn == NULL) {
1243 ret = replmd_add_make_extended_dn(req,
1246 if (ret != LDB_SUCCESS) {
1253 * Prepare the context for the backlinks and
1254 * create metadata for the forward links. The
1255 * backlinks are created in
1256 * replmd_op_callback() after the successful
1257 * ADD of the object.
1259 ret = replmd_add_fix_la(module, msg->elements,
1264 if (ret != LDB_SUCCESS) {
1268 /* linked attributes are not stored in
1269 replPropertyMetaData in FL above w2k */
1274 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1276 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1277 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1280 if (rdn_val == NULL) {
1283 return LDB_ERR_OPERATIONS_ERROR;
1286 rdn = (const char*)rdn_val->data;
1287 if (strcmp(rdn, "Deleted Objects") == 0) {
1289 * Set the originating_change_time to 29/12/9999 at 23:59:59
1290 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1292 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1294 m->originating_change_time = now;
1297 m->originating_change_time = now;
1299 m->originating_invocation_id = ac->our_invocation_id;
1300 m->originating_usn = ac->seq_num;
1301 m->local_usn = ac->seq_num;
1304 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1309 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1311 if (e->num_values != 0) {
1316 ldb_msg_remove_element(msg, e);
1319 /* fix meta data count */
1320 nmd.ctr.ctr1.count = ni;
1323 * sort meta data array
1325 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1326 if (ret != LDB_SUCCESS) {
1327 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1332 /* generated NDR encoded values */
1333 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1335 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1336 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1339 return LDB_ERR_OPERATIONS_ERROR;
1343 * add the autogenerated values
1345 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1346 if (ret != LDB_SUCCESS) {
1351 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1352 if (ret != LDB_SUCCESS) {
1357 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1358 if (ret != LDB_SUCCESS) {
1363 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1364 if (ret != LDB_SUCCESS) {
1369 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1370 if (ret != LDB_SUCCESS) {
1377 * sort the attributes by attid before storing the object
1379 replmd_ldb_message_sort(msg, ac->schema);
1382 * Assert that we do have an objectClass
1384 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1385 if (objectclass_el == NULL) {
1386 ldb_asprintf_errstring(ldb, __location__
1387 ": objectClass missing on %s\n",
1388 ldb_dn_get_linearized(msg->dn));
1390 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1392 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1393 REPL_URGENT_ON_CREATE);
1395 ac->is_urgent = is_urgent;
1396 ret = ldb_build_add_req(&down_req, ldb, ac,
1399 ac, replmd_op_callback,
1402 LDB_REQ_SET_LOCATION(down_req);
1403 if (ret != LDB_SUCCESS) {
1408 /* current partition control is needed by "replmd_op_callback" */
1409 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1410 ret = ldb_request_add_control(down_req,
1411 DSDB_CONTROL_CURRENT_PARTITION_OID,
1413 if (ret != LDB_SUCCESS) {
1419 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1420 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1421 if (ret != LDB_SUCCESS) {
1427 /* mark the control done */
1429 control->critical = 0;
1431 /* go on with the call chain */
1432 return ldb_next_request(module, down_req);
1437 * update the replPropertyMetaData for one element
1439 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1440 struct ldb_message *msg,
1441 struct ldb_message_element *el,
1442 struct ldb_message_element *old_el,
1443 struct replPropertyMetaDataBlob *omd,
1444 const struct dsdb_schema *schema,
1446 const struct GUID *our_invocation_id,
1449 bool is_forced_rodc,
1450 struct ldb_request *req)
1453 const struct dsdb_attribute *a;
1454 struct replPropertyMetaData1 *md1;
1455 bool may_skip = false;
1458 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1460 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1461 /* allow this to make it possible for dbcheck
1462 to remove bad attributes */
1466 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1468 return LDB_ERR_OPERATIONS_ERROR;
1471 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1473 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1478 * if the attribute's value haven't changed, and this isn't
1479 * just a delete of everything then return LDB_SUCCESS Unless
1480 * we have the provision control or if the attribute is
1481 * interSiteTopologyGenerator as this page explain:
1482 * http://support.microsoft.com/kb/224815 this attribute is
1483 * periodicaly written by the DC responsible for the intersite
1484 * generation in a given site
1486 * Unchanged could be deleting or replacing an already-gone
1487 * thing with an unconstrained delete/empty replace or a
1488 * replace with the same value, but not an add with the same
1489 * value because that could be about adding a duplicate (which
1490 * is for someone else to error out on).
1492 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1493 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1496 } else if (old_el == NULL && el->num_values == 0) {
1497 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1499 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1502 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1503 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1505 * We intentionally skip the version bump when attempting to
1508 * The control is set by dbcheck and expunge-tombstones which
1509 * both attempt to be non-replicating. Otherwise, making an
1510 * alteration to the replication state would trigger a
1511 * broadcast of all expunged objects.
1516 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1518 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1522 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1523 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1525 * allow this to make it possible for dbcheck
1526 * to rebuild broken metadata
1532 for (i=0; i<omd->ctr.ctr1.count; i++) {
1534 * First check if we find it under the msDS-IntID,
1535 * then check if we find it under the OID and
1538 * This allows the administrator to simply re-write
1539 * the attributes and so restore replication, which is
1540 * likely what they will try to do.
1542 if (attid == omd->ctr.ctr1.array[i].attid) {
1546 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1551 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1552 /* linked attributes are not stored in
1553 replPropertyMetaData in FL above w2k, but we do
1554 raise the seqnum for the object */
1555 if (*seq_num == 0 &&
1556 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1557 return LDB_ERR_OPERATIONS_ERROR;
1562 if (i == omd->ctr.ctr1.count) {
1563 /* we need to add a new one */
1564 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1565 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1566 if (omd->ctr.ctr1.array == NULL) {
1568 return LDB_ERR_OPERATIONS_ERROR;
1570 omd->ctr.ctr1.count++;
1571 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1574 /* Get a new sequence number from the backend. We only do this
1575 * if we have a change that requires a new
1576 * replPropertyMetaData element
1578 if (*seq_num == 0) {
1579 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1580 if (ret != LDB_SUCCESS) {
1581 return LDB_ERR_OPERATIONS_ERROR;
1585 md1 = &omd->ctr.ctr1.array[i];
1589 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1590 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1593 if (rdn_val == NULL) {
1595 return LDB_ERR_OPERATIONS_ERROR;
1598 rdn = (const char*)rdn_val->data;
1599 if (strcmp(rdn, "Deleted Objects") == 0) {
1601 * Set the originating_change_time to 29/12/9999 at 23:59:59
1602 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1604 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1606 md1->originating_change_time = now;
1609 md1->originating_change_time = now;
1611 md1->originating_invocation_id = *our_invocation_id;
1612 md1->originating_usn = *seq_num;
1613 md1->local_usn = *seq_num;
1615 if (is_forced_rodc) {
1616 /* Force version to 0 to be overriden later via replication */
1624 * Bump the replPropertyMetaData version on an attribute, and if it
1625 * has changed (or forced by leaving rdn_old NULL), update the value
1628 * This is important, as calling a modify operation may not change the
1629 * version number if the values appear unchanged, but a rename between
1630 * parents bumps this value.
1633 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1634 struct ldb_message *msg,
1635 const struct ldb_val *rdn_new,
1636 const struct ldb_val *rdn_old,
1637 struct replPropertyMetaDataBlob *omd,
1638 struct replmd_replicated_request *ar,
1641 bool is_forced_rodc)
1643 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1644 const struct dsdb_attribute *rdn_attr =
1645 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1646 const char *attr_name = rdn_attr != NULL ?
1647 rdn_attr->lDAPDisplayName :
1649 struct ldb_message_element new_el = {
1650 .flags = LDB_FLAG_MOD_REPLACE,
1653 .values = discard_const_p(struct ldb_val, rdn_new)
1655 struct ldb_message_element old_el = {
1656 .flags = LDB_FLAG_MOD_REPLACE,
1658 .num_values = rdn_old ? 1 : 0,
1659 .values = discard_const_p(struct ldb_val, rdn_old)
1662 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1663 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1664 if (ret != LDB_SUCCESS) {
1665 return ldb_oom(ldb);
1669 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1670 omd, ar->schema, &ar->seq_num,
1671 &ar->our_invocation_id,
1672 now, is_schema_nc, is_forced_rodc,
1677 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1679 uint32_t count = omd.ctr.ctr1.count;
1682 for (i=0; i < count; i++) {
1683 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1684 if (max < m.local_usn) {
1692 * update the replPropertyMetaData object each time we modify an
1693 * object. This is needed for DRS replication, as the merge on the
1694 * client is based on this object
1696 static int replmd_update_rpmd(struct ldb_module *module,
1697 const struct dsdb_schema *schema,
1698 struct ldb_request *req,
1699 const char * const *rename_attrs,
1700 struct ldb_message *msg, uint64_t *seq_num,
1701 time_t t, bool is_schema_nc,
1702 bool *is_urgent, bool *rodc)
1704 const struct ldb_val *omd_value;
1705 enum ndr_err_code ndr_err;
1706 struct replPropertyMetaDataBlob omd;
1709 const struct GUID *our_invocation_id;
1711 const char * const *attrs = NULL;
1712 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1713 struct ldb_result *res;
1714 struct ldb_context *ldb;
1715 struct ldb_message_element *objectclass_el;
1716 enum urgent_situation situation;
1717 bool rmd_is_provided;
1718 bool rmd_is_just_resorted = false;
1719 const char *not_rename_attrs[4 + msg->num_elements];
1720 bool is_forced_rodc = false;
1723 attrs = rename_attrs;
1725 for (i = 0; i < msg->num_elements; i++) {
1726 not_rename_attrs[i] = msg->elements[i].name;
1728 not_rename_attrs[i] = "replPropertyMetaData";
1729 not_rename_attrs[i+1] = "objectClass";
1730 not_rename_attrs[i+2] = "instanceType";
1731 not_rename_attrs[i+3] = NULL;
1732 attrs = not_rename_attrs;
1735 ldb = ldb_module_get_ctx(module);
1737 ret = samdb_rodc(ldb, rodc);
1738 if (ret != LDB_SUCCESS) {
1739 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1744 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1745 is_forced_rodc = true;
1748 our_invocation_id = samdb_ntds_invocation_id(ldb);
1749 if (!our_invocation_id) {
1750 /* this happens during an initial vampire while
1751 updating the schema */
1752 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1756 unix_to_nt_time(&now, t);
1758 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1759 rmd_is_provided = true;
1760 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1761 rmd_is_just_resorted = true;
1764 rmd_is_provided = false;
1767 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1768 * otherwise we consider we are updating */
1769 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1770 situation = REPL_URGENT_ON_DELETE;
1771 } else if (rename_attrs) {
1772 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1774 situation = REPL_URGENT_ON_UPDATE;
1777 if (rmd_is_provided) {
1778 /* In this case the change_replmetadata control was supplied */
1779 /* We check that it's the only attribute that is provided
1780 * (it's a rare case so it's better to keep the code simplier)
1781 * We also check that the highest local_usn is bigger or the same as
1784 if( msg->num_elements != 1 ||
1785 strncmp(msg->elements[0].name,
1786 "replPropertyMetaData", 20) ) {
1787 DEBUG(0,(__location__ ": changereplmetada control called without "\
1788 "a specified replPropertyMetaData attribute or with others\n"));
1789 return LDB_ERR_OPERATIONS_ERROR;
1791 if (situation != REPL_URGENT_ON_UPDATE) {
1792 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1793 return LDB_ERR_OPERATIONS_ERROR;
1795 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1797 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1798 ldb_dn_get_linearized(msg->dn)));
1799 return LDB_ERR_OPERATIONS_ERROR;
1801 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1802 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1803 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1804 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1805 ldb_dn_get_linearized(msg->dn)));
1806 return LDB_ERR_OPERATIONS_ERROR;
1809 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1810 DSDB_FLAG_NEXT_MODULE |
1811 DSDB_SEARCH_SHOW_RECYCLED |
1812 DSDB_SEARCH_SHOW_EXTENDED_DN |
1813 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1814 DSDB_SEARCH_REVEAL_INTERNALS, req);
1816 if (ret != LDB_SUCCESS) {
1820 if (rmd_is_just_resorted == false) {
1821 *seq_num = find_max_local_usn(omd);
1823 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1826 * The test here now allows for a new
1827 * replPropertyMetaData with no change, if was
1828 * just dbcheck re-sorting the values.
1830 if (*seq_num <= db_seq) {
1831 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1832 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1833 (long long)*seq_num, (long long)db_seq));
1834 return LDB_ERR_OPERATIONS_ERROR;
1839 /* search for the existing replPropertyMetaDataBlob. We need
1840 * to use REVEAL and ask for DNs in storage format to support
1841 * the check for values being the same in
1842 * replmd_update_rpmd_element()
1844 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1845 DSDB_FLAG_NEXT_MODULE |
1846 DSDB_SEARCH_SHOW_RECYCLED |
1847 DSDB_SEARCH_SHOW_EXTENDED_DN |
1848 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1849 DSDB_SEARCH_REVEAL_INTERNALS, req);
1850 if (ret != LDB_SUCCESS) {
1854 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1856 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1857 ldb_dn_get_linearized(msg->dn)));
1858 return LDB_ERR_OPERATIONS_ERROR;
1861 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1862 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1863 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1864 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1865 ldb_dn_get_linearized(msg->dn)));
1866 return LDB_ERR_OPERATIONS_ERROR;
1869 if (omd.version != 1) {
1870 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1871 omd.version, ldb_dn_get_linearized(msg->dn)));
1872 return LDB_ERR_OPERATIONS_ERROR;
1875 for (i=0; i<msg->num_elements;) {
1876 struct ldb_message_element *el = &msg->elements[i];
1877 struct ldb_message_element *old_el;
1879 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1880 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1881 &omd, schema, seq_num,
1886 if (ret != LDB_SUCCESS) {
1890 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1891 *is_urgent = replmd_check_urgent_attribute(el);
1894 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1899 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1901 if (el->num_values != 0) {
1906 ldb_msg_remove_element(msg, el);
1911 * Assert that we have an objectClass attribute - this is major
1912 * corruption if we don't have this!
1914 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1915 if (objectclass_el != NULL) {
1917 * Now check if this objectClass means we need to do urgent replication
1919 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1923 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1924 ldb_asprintf_errstring(ldb, __location__
1925 ": objectClass missing on %s\n",
1926 ldb_dn_get_linearized(msg->dn));
1927 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1931 * replmd_update_rpmd_element has done an update if the
1934 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1935 struct ldb_val *md_value;
1936 struct ldb_message_element *el;
1938 /*if we are RODC and this is a DRSR update then its ok*/
1939 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1940 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1941 && !is_forced_rodc) {
1942 unsigned instanceType;
1945 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1946 return LDB_ERR_REFERRAL;
1949 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1950 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1951 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1952 "cannot change replicated attribute on partial replica");
1956 md_value = talloc(msg, struct ldb_val);
1957 if (md_value == NULL) {
1959 return LDB_ERR_OPERATIONS_ERROR;
1962 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1963 if (ret != LDB_SUCCESS) {
1964 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1968 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1969 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1970 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1971 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1972 ldb_dn_get_linearized(msg->dn)));
1973 return LDB_ERR_OPERATIONS_ERROR;
1976 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1977 if (ret != LDB_SUCCESS) {
1978 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1979 ldb_dn_get_linearized(msg->dn)));
1984 el->values = md_value;
1990 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1992 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1994 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1995 &pdn2->dsdb_dn->extra_part);
2001 get a series of message element values as an array of DNs and GUIDs
2002 the result is sorted by GUID
2004 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2005 struct ldb_message_element *el, struct parsed_dn **pdn,
2006 const char *ldap_oid, struct ldb_request *parent)
2009 bool values_are_sorted = true;
2010 struct ldb_context *ldb = ldb_module_get_ctx(module);
2017 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2019 ldb_module_oom(module);
2020 return LDB_ERR_OPERATIONS_ERROR;
2023 for (i=0; i<el->num_values; i++) {
2024 struct ldb_val *v = &el->values[i];
2027 struct parsed_dn *p;
2031 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2032 if (p->dsdb_dn == NULL) {
2033 return LDB_ERR_INVALID_DN_SYNTAX;
2036 dn = p->dsdb_dn->dn;
2038 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2039 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2040 unlikely(GUID_all_zero(&p->guid))) {
2041 /* we got a DN without a GUID - go find the GUID */
2042 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2043 if (ret != LDB_SUCCESS) {
2044 char *dn_str = NULL;
2045 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2047 ldb_asprintf_errstring(ldb,
2048 "Unable to find GUID for DN %s\n",
2050 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2051 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2052 ldb_attr_cmp(el->name, "member") == 0) {
2053 return LDB_ERR_UNWILLING_TO_PERFORM;
2057 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2058 if (ret != LDB_SUCCESS) {
2061 } else if (!NT_STATUS_IS_OK(status)) {
2062 return LDB_ERR_OPERATIONS_ERROR;
2064 if (i > 0 && values_are_sorted) {
2065 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2067 values_are_sorted = false;
2070 /* keep a pointer to the original ldb_val */
2073 if (! values_are_sorted) {
2074 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2080 * Get a series of trusted message element values. The result is sorted by
2081 * GUID, even though the GUIDs might not be known. That works because we trust
2082 * the database to give us the elements like that if the
2083 * replmd_private->sorted_links flag is set.
2085 * We also ensure that the links are in the Functional Level 2003
2086 * linked attributes format.
2088 static int get_parsed_dns_trusted(struct ldb_module *module,
2089 struct replmd_private *replmd_private,
2090 TALLOC_CTX *mem_ctx,
2091 struct ldb_message_element *el,
2092 struct parsed_dn **pdn,
2093 const char *ldap_oid,
2094 struct ldb_request *parent)
2103 if (!replmd_private->sorted_links) {
2104 /* We need to sort the list. This is the slow old path we want
2107 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2109 if (ret != LDB_SUCCESS) {
2113 /* Here we get a list of 'struct parsed_dns' without the parsing */
2114 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2117 ldb_module_oom(module);
2118 return LDB_ERR_OPERATIONS_ERROR;
2121 for (i = 0; i < el->num_values; i++) {
2122 (*pdn)[i].v = &el->values[i];
2127 * This upgrades links to FL2003 style, and sorts the result
2128 * if that was needed.
2130 * TODO: Add a database feature that asserts we have no FL2000
2131 * style links to avoid this check or add a feature that
2132 * uses a similar check to find sorted/unsorted links
2133 * for an on-the-fly upgrade.
2136 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2137 *pdn, el->num_values,
2140 if (ret != LDB_SUCCESS) {
2148 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2149 otherwise an error code. For compatibility the error code differs depending
2150 on whether or not the attribute is "member".
2152 As always, the parsed_dn list is assumed to be sorted.
2154 static int check_parsed_dn_duplicates(struct ldb_module *module,
2155 struct ldb_message_element *el,
2156 struct parsed_dn *pdn)
2159 struct ldb_context *ldb = ldb_module_get_ctx(module);
2161 for (i = 1; i < el->num_values; i++) {
2162 struct parsed_dn *p = &pdn[i];
2163 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2164 ldb_asprintf_errstring(ldb,
2165 "Linked attribute %s has "
2166 "multiple identical values",
2168 if (ldb_attr_cmp(el->name, "member") == 0) {
2169 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2171 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2179 build a new extended DN, including all meta data fields
2181 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2182 RMD_ADDTIME = originating_add_time
2183 RMD_INVOCID = originating_invocation_id
2184 RMD_CHANGETIME = originating_change_time
2185 RMD_ORIGINATING_USN = originating_usn
2186 RMD_LOCAL_USN = local_usn
2187 RMD_VERSION = version
2189 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2190 struct dsdb_dn *dsdb_dn,
2191 const struct GUID *invocation_id,
2192 uint64_t local_usn, NTTIME nttime)
2194 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2195 local_usn, local_usn, nttime,
2196 RMD_VERSION_INITIAL, false);
2199 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2200 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2201 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2205 check if any links need upgrading from w2k format
2207 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2208 struct parsed_dn *dns, uint32_t count,
2209 struct ldb_message_element *el,
2210 const char *ldap_oid)
2213 const struct GUID *invocation_id = NULL;
2214 for (i=0; i<count; i++) {
2218 if (dns[i].dsdb_dn == NULL) {
2219 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2221 if (ret != LDB_SUCCESS) {
2222 return LDB_ERR_INVALID_DN_SYNTAX;
2226 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2227 &version, "RMD_VERSION");
2228 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2230 * We optimistically assume they are all the same; if
2231 * the first one is fixed, they are all fixed.
2233 * If the first one was *not* fixed and we find a
2234 * later one that is, that is an occasion to shout
2240 DEBUG(0, ("Mixed w2k and fixed format "
2241 "linked attributes\n"));
2245 if (invocation_id == NULL) {
2246 invocation_id = samdb_ntds_invocation_id(ldb);
2247 if (invocation_id == NULL) {
2248 return LDB_ERR_OPERATIONS_ERROR;
2253 /* it's an old one that needs upgrading */
2254 ret = replmd_update_la_val(el->values, dns[i].v,
2255 dns[i].dsdb_dn, dns[i].dsdb_dn,
2256 invocation_id, 1, 1, 0, false);
2257 if (ret != LDB_SUCCESS) {
2263 * This sort() is critical for the operation of
2264 * get_parsed_dns_trusted() because callers of this function
2265 * expect a sorted list, and FL2000 style links are not
2266 * sorted. In particular, as well as the upgrade case,
2267 * get_parsed_dns_trusted() is called from
2268 * replmd_delete_remove_link() even in FL2000 mode
2270 * We do not normally pay the cost of the qsort() due to the
2271 * early return in the RMD_VERSION found case.
2273 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2278 Sets the value for a linked attribute, including all meta data fields
2280 see replmd_build_la_val for value names
2282 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2283 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2284 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2285 uint32_t version, bool deleted)
2287 struct ldb_dn *dn = dsdb_dn->dn;
2288 const char *tstring, *usn_string, *flags_string;
2289 struct ldb_val tval;
2291 struct ldb_val usnv, local_usnv;
2292 struct ldb_val vers, flagsv;
2293 const struct ldb_val *old_addtime = NULL;
2296 const char *dnstring;
2298 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2300 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2302 return LDB_ERR_OPERATIONS_ERROR;
2304 tval = data_blob_string_const(tstring);
2306 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2308 return LDB_ERR_OPERATIONS_ERROR;
2310 usnv = data_blob_string_const(usn_string);
2312 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2314 return LDB_ERR_OPERATIONS_ERROR;
2316 local_usnv = data_blob_string_const(usn_string);
2318 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2319 if (!NT_STATUS_IS_OK(status)) {
2320 return LDB_ERR_OPERATIONS_ERROR;
2323 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2324 if (!flags_string) {
2325 return LDB_ERR_OPERATIONS_ERROR;
2327 flagsv = data_blob_string_const(flags_string);
2329 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2330 if (ret != LDB_SUCCESS) return ret;
2332 /* get the ADDTIME from the original */
2333 if (old_dsdb_dn != NULL) {
2334 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2337 if (old_addtime == NULL) {
2338 old_addtime = &tval;
2340 if (dsdb_dn != old_dsdb_dn ||
2341 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2342 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2343 if (ret != LDB_SUCCESS) return ret;
2346 /* use our invocation id */
2347 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2348 if (ret != LDB_SUCCESS) return ret;
2350 /* changetime is the current time */
2351 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2352 if (ret != LDB_SUCCESS) return ret;
2354 /* update the USN */
2355 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2356 if (ret != LDB_SUCCESS) return ret;
2358 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2359 if (ret != LDB_SUCCESS) return ret;
2361 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2362 vers = data_blob_string_const(vstring);
2363 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2364 if (ret != LDB_SUCCESS) return ret;
2366 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2367 if (dnstring == NULL) {
2368 return LDB_ERR_OPERATIONS_ERROR;
2370 *v = data_blob_string_const(dnstring);
2376 * Updates the value for a linked attribute, including all meta data fields
2378 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2379 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2380 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2383 uint32_t old_version;
2384 uint32_t version = RMD_VERSION_INITIAL;
2388 * We're updating the linked attribute locally, so increase the version
2389 * by 1 so that other DCs will see the change when it gets replicated out
2391 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2394 if (NT_STATUS_IS_OK(status)) {
2395 version = old_version + 1;
2398 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2399 usn, local_usn, nttime, version, deleted);
2403 handle adding a linked attribute
2405 static int replmd_modify_la_add(struct ldb_module *module,
2406 struct replmd_private *replmd_private,
2407 struct replmd_replicated_request *ac,
2408 struct ldb_message *msg,
2409 struct ldb_message_element *el,
2410 struct ldb_message_element *old_el,
2411 const struct dsdb_attribute *schema_attr,
2413 struct ldb_dn *msg_dn,
2414 struct ldb_request *parent)
2417 struct parsed_dn *dns, *old_dns;
2418 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2420 struct ldb_val *new_values = NULL;
2421 unsigned old_num_values = old_el ? old_el->num_values : 0;
2422 unsigned num_values = 0;
2423 unsigned max_num_values;
2424 struct ldb_context *ldb = ldb_module_get_ctx(module);
2426 unix_to_nt_time(&now, t);
2428 /* get the DNs to be added, fully parsed.
2430 * We need full parsing because they came off the wire and we don't
2431 * trust them, besides which we need their details to know where to put
2434 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2435 schema_attr->syntax->ldap_oid, parent);
2436 if (ret != LDB_SUCCESS) {
2437 talloc_free(tmp_ctx);
2441 /* get the existing DNs, lazily parsed */
2442 ret = get_parsed_dns_trusted(module, replmd_private,
2443 tmp_ctx, old_el, &old_dns,
2444 schema_attr->syntax->ldap_oid, parent);
2446 if (ret != LDB_SUCCESS) {
2447 talloc_free(tmp_ctx);
2451 max_num_values = old_num_values + el->num_values;
2452 if (max_num_values < old_num_values) {
2453 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2454 "old values: %u, new values: %u, sum: %u\n",
2455 old_num_values, el->num_values, max_num_values));
2456 talloc_free(tmp_ctx);
2457 return LDB_ERR_OPERATIONS_ERROR;
2460 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2462 if (new_values == NULL) {
2463 ldb_module_oom(module);
2464 talloc_free(tmp_ctx);
2465 return LDB_ERR_OPERATIONS_ERROR;
2469 * For each new value, find where it would go in the list. If there is
2470 * a matching GUID there, we update the existing value; otherwise we
2474 for (i = 0; i < el->num_values; i++) {
2475 struct parsed_dn *exact;
2476 struct parsed_dn *next;
2478 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2481 dns[i].dsdb_dn->extra_part, 0,
2483 schema_attr->syntax->ldap_oid,
2485 if (err != LDB_SUCCESS) {
2486 talloc_free(tmp_ctx);
2490 if (ac->fix_link_sid) {
2491 char *fixed_dnstring = NULL;
2492 struct dom_sid tmp_sid = { 0, };
2493 DATA_BLOB sid_blob = data_blob_null;
2494 enum ndr_err_code ndr_err;
2498 if (exact == NULL) {
2499 talloc_free(tmp_ctx);
2500 return ldb_operr(ldb);
2503 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2504 talloc_free(tmp_ctx);
2505 return ldb_operr(ldb);
2509 * Only "<GUID=...><SID=...>" is allowed.
2511 * We get the GUID to just to find the old
2512 * value and the SID in order to add it
2513 * to the found value.
2516 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2518 talloc_free(tmp_ctx);
2519 return ldb_operr(ldb);
2522 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2524 talloc_free(tmp_ctx);
2525 return ldb_operr(ldb);
2528 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2530 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2531 /* this is what we expect */
2532 } else if (NT_STATUS_IS_OK(status)) {
2533 struct GUID_txt_buf guid_str;
2534 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2535 "i[%u] SID NOT MISSING... Attribute %s already "
2536 "exists for target GUID %s, SID %s, DN: %s",
2538 GUID_buf_string(&exact->guid,
2540 dom_sid_string(tmp_ctx, &tmp_sid),
2541 dsdb_dn_get_extended_linearized(tmp_ctx,
2542 exact->dsdb_dn, 1));
2543 talloc_free(tmp_ctx);
2544 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2546 talloc_free(tmp_ctx);
2547 return ldb_operr(ldb);
2550 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2552 if (!NT_STATUS_IS_OK(status)) {
2553 struct GUID_txt_buf guid_str;
2554 ldb_asprintf_errstring(ldb,
2555 "NO SID PROVIDED... Attribute %s already "
2556 "exists for target GUID %s",
2558 GUID_buf_string(&exact->guid,
2560 talloc_free(tmp_ctx);
2561 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2564 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2565 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2566 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2567 talloc_free(tmp_ctx);
2568 return ldb_operr(ldb);
2571 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2572 data_blob_free(&sid_blob);
2573 if (ret != LDB_SUCCESS) {
2574 talloc_free(tmp_ctx);
2578 fixed_dnstring = dsdb_dn_get_extended_linearized(
2579 new_values, exact->dsdb_dn, 1);
2580 if (fixed_dnstring == NULL) {
2581 talloc_free(tmp_ctx);
2582 return ldb_operr(ldb);
2586 * We just replace the existing value...
2588 *exact->v = data_blob_string_const(fixed_dnstring);
2593 if (exact != NULL) {
2595 * We are trying to add one that exists, which is only
2596 * allowed if it was previously deleted.
2598 * When we do undelete a link we change it in place.
2599 * It will be copied across into the right spot in due
2603 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2605 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2606 struct GUID_txt_buf guid_str;
2607 ldb_asprintf_errstring(ldb,
2608 "Attribute %s already "
2609 "exists for target GUID %s",
2611 GUID_buf_string(&exact->guid,
2613 talloc_free(tmp_ctx);
2614 /* error codes for 'member' need to be
2616 if (ldb_attr_cmp(el->name, "member") == 0) {
2617 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2619 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2623 ret = replmd_update_la_val(new_values, exact->v,
2626 &ac->our_invocation_id,
2627 ac->seq_num, ac->seq_num,
2629 if (ret != LDB_SUCCESS) {
2630 talloc_free(tmp_ctx);
2634 ret = replmd_add_backlink(module, replmd_private,
2641 if (ret != LDB_SUCCESS) {
2642 talloc_free(tmp_ctx);
2648 * Here we don't have an exact match.
2650 * If next is NULL, this one goes beyond the end of the
2651 * existing list, so we need to add all of those ones first.
2653 * If next is not NULL, we need to add all the ones before
2657 offset = old_num_values;
2659 /* next should have been parsed, but let's make sure */
2660 if (next->dsdb_dn == NULL) {
2661 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2662 schema_attr->syntax->ldap_oid);
2663 if (ret != LDB_SUCCESS) {
2667 offset = MIN(next - old_dns, old_num_values);
2670 /* put all the old ones before next on the list */
2671 for (; j < offset; j++) {
2672 new_values[num_values] = *old_dns[j].v;
2676 ret = replmd_add_backlink(module, replmd_private,
2681 /* Make the new linked attribute ldb_val. */
2682 ret = replmd_build_la_val(new_values, &new_values[num_values],
2683 dns[i].dsdb_dn, &ac->our_invocation_id,
2685 if (ret != LDB_SUCCESS) {
2686 talloc_free(tmp_ctx);
2690 if (ret != LDB_SUCCESS) {
2691 talloc_free(tmp_ctx);
2695 /* copy the rest of the old ones (if any) */
2696 for (; j < old_num_values; j++) {
2697 new_values[num_values] = *old_dns[j].v;
2701 talloc_steal(msg->elements, new_values);
2702 if (old_el != NULL) {
2703 talloc_steal(msg->elements, old_el->values);
2705 el->values = new_values;
2706 el->num_values = num_values;
2708 talloc_free(tmp_ctx);
2710 /* we now tell the backend to replace all existing values
2711 with the one we have constructed */
2712 el->flags = LDB_FLAG_MOD_REPLACE;
2719 handle deleting all active linked attributes
2721 static int replmd_modify_la_delete(struct ldb_module *module,
2722 struct replmd_private *replmd_private,
2723 struct replmd_replicated_request *ac,
2724 struct ldb_message *msg,
2725 struct ldb_message_element *el,
2726 struct ldb_message_element *old_el,
2727 const struct dsdb_attribute *schema_attr,
2729 struct ldb_dn *msg_dn,
2730 struct ldb_request *parent)
2733 struct parsed_dn *dns, *old_dns;
2734 TALLOC_CTX *tmp_ctx = NULL;
2736 struct ldb_context *ldb = ldb_module_get_ctx(module);
2737 struct ldb_control *vanish_links_ctrl = NULL;
2738 bool vanish_links = false;
2739 unsigned int num_to_delete = el->num_values;
2743 unix_to_nt_time(&now, t);
2745 if (old_el == NULL || old_el->num_values == 0) {
2746 /* there is nothing to delete... */
2747 if (num_to_delete == 0) {
2748 /* and we're deleting nothing, so that's OK */
2751 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2754 tmp_ctx = talloc_new(msg);
2755 if (tmp_ctx == NULL) {
2756 return LDB_ERR_OPERATIONS_ERROR;
2759 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2760 schema_attr->syntax->ldap_oid, parent);
2761 if (ret != LDB_SUCCESS) {
2762 talloc_free(tmp_ctx);
2766 ret = get_parsed_dns_trusted(module, replmd_private,
2767 tmp_ctx, old_el, &old_dns,
2768 schema_attr->syntax->ldap_oid, parent);
2770 if (ret != LDB_SUCCESS) {
2771 talloc_free(tmp_ctx);
2776 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2777 if (vanish_links_ctrl) {
2778 vanish_links = true;
2779 vanish_links_ctrl->critical = false;
2783 /* we empty out el->values here to avoid damage if we return early. */
2788 * If vanish links is set, we are actually removing members of
2789 * old_el->values; otherwise we are just marking them deleted.
2791 * There is a special case when no values are given: we remove them
2792 * all. When we have the vanish_links control we just have to remove
2793 * the backlinks and change our element to replace the existing values
2794 * with the empty list.
2797 if (num_to_delete == 0) {
2798 for (i = 0; i < old_el->num_values; i++) {
2799 struct parsed_dn *p = &old_dns[i];
2800 if (p->dsdb_dn == NULL) {
2801 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2802 schema_attr->syntax->ldap_oid);
2803 if (ret != LDB_SUCCESS) {
2807 ret = replmd_add_backlink(module, replmd_private,
2808 ac->schema, msg_dn, &p->guid,
2811 if (ret != LDB_SUCCESS) {
2812 talloc_free(tmp_ctx);
2819 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2820 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2824 ret = replmd_update_la_val(old_el->values, p->v,
2825 p->dsdb_dn, p->dsdb_dn,
2826 &ac->our_invocation_id,
2827 ac->seq_num, ac->seq_num,
2829 if (ret != LDB_SUCCESS) {
2830 talloc_free(tmp_ctx);
2836 el->flags = LDB_FLAG_MOD_REPLACE;
2837 talloc_free(tmp_ctx);
2843 for (i = 0; i < num_to_delete; i++) {
2844 struct parsed_dn *p = &dns[i];
2845 struct parsed_dn *exact = NULL;
2846 struct parsed_dn *next = NULL;
2847 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2850 p->dsdb_dn->extra_part, 0,
2852 schema_attr->syntax->ldap_oid,
2854 if (ret != LDB_SUCCESS) {
2855 talloc_free(tmp_ctx);
2858 if (exact == NULL) {
2859 struct GUID_txt_buf buf;
2860 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2861 "exist for target GUID %s",
2863 GUID_buf_string(&p->guid, &buf));
2864 if (ldb_attr_cmp(el->name, "member") == 0) {
2865 talloc_free(tmp_ctx);
2866 return LDB_ERR_UNWILLING_TO_PERFORM;
2868 talloc_free(tmp_ctx);
2869 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2874 if (CHECK_DEBUGLVL(5)) {
2875 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2876 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2877 struct GUID_txt_buf buf;
2878 const char *guid_str = \
2879 GUID_buf_string(&p->guid, &buf);
2880 DEBUG(5, ("Deleting deleted linked "
2881 "attribute %s to %s, because "
2882 "vanish_links control is set\n",
2883 el->name, guid_str));
2887 /* remove the backlink */
2888 ret = replmd_add_backlink(module,
2895 if (ret != LDB_SUCCESS) {
2896 talloc_free(tmp_ctx);
2900 /* We flag the deletion and tidy it up later. */
2905 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2907 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2908 struct GUID_txt_buf buf;
2909 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2910 ldb_asprintf_errstring(ldb, "Attribute %s already "
2911 "deleted for target GUID %s",
2912 el->name, guid_str);
2913 if (ldb_attr_cmp(el->name, "member") == 0) {
2914 talloc_free(tmp_ctx);
2915 return LDB_ERR_UNWILLING_TO_PERFORM;
2917 talloc_free(tmp_ctx);
2918 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2922 ret = replmd_update_la_val(old_el->values, exact->v,
2923 exact->dsdb_dn, exact->dsdb_dn,
2924 &ac->our_invocation_id,
2925 ac->seq_num, ac->seq_num,
2927 if (ret != LDB_SUCCESS) {
2928 talloc_free(tmp_ctx);
2931 ret = replmd_add_backlink(module, replmd_private,
2936 if (ret != LDB_SUCCESS) {
2937 talloc_free(tmp_ctx);
2944 struct ldb_val *tmp_vals = NULL;
2946 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2947 old_el->num_values);
2948 if (tmp_vals == NULL) {
2949 talloc_free(tmp_ctx);
2950 return ldb_module_oom(module);
2952 for (i = 0; i < old_el->num_values; i++) {
2953 if (old_dns[i].v == NULL) {
2956 tmp_vals[j] = *old_dns[i].v;
2959 for (i = 0; i < j; i++) {
2960 old_el->values[i] = tmp_vals[i];
2962 old_el->num_values = j;
2965 el->values = talloc_steal(msg->elements, old_el->values);
2966 el->num_values = old_el->num_values;
2968 talloc_free(tmp_ctx);
2970 /* we now tell the backend to replace all existing values
2971 with the one we have constructed */
2972 el->flags = LDB_FLAG_MOD_REPLACE;
2978 handle replacing a linked attribute
2980 static int replmd_modify_la_replace(struct ldb_module *module,
2981 struct replmd_private *replmd_private,
2982 struct replmd_replicated_request *ac,
2983 struct ldb_message *msg,
2984 struct ldb_message_element *el,
2985 struct ldb_message_element *old_el,
2986 const struct dsdb_attribute *schema_attr,
2988 struct ldb_dn *msg_dn,
2989 struct ldb_request *parent)
2991 unsigned int i, old_i, new_i;
2992 struct parsed_dn *dns, *old_dns;
2993 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2995 struct ldb_context *ldb = ldb_module_get_ctx(module);
2996 struct ldb_val *new_values = NULL;
2997 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2998 unsigned int old_num_values;
2999 unsigned int repl_num_values;
3000 unsigned int max_num_values;
3003 unix_to_nt_time(&now, t);
3006 * The replace operation is unlike the replace and delete cases in that
3007 * we need to look at every existing link to see whether it is being
3008 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3010 * As we are trying to combine two sorted lists, the algorithm we use
3011 * is akin to the merge phase of a merge sort. We interleave the two
3012 * lists, doing different things depending on which side the current
3015 * There are three main cases, with some sub-cases.
3017 * - a DN is in the old list but not the new one. It needs to be
3018 * marked as deleted (but left in the list).
3019 * - maybe it is already deleted, and we have less to do.
3021 * - a DN is in both lists. The old data gets replaced by the new,
3022 * and the list doesn't grow. The old link may have been marked as
3023 * deleted, in which case we undelete it.
3025 * - a DN is in the new list only. We add it in the right place.
3028 old_num_values = old_el ? old_el->num_values : 0;
3029 repl_num_values = el->num_values;
3030 max_num_values = old_num_values + repl_num_values;
3032 if (max_num_values == 0) {
3033 /* There is nothing to do! */
3037 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3038 if (ret != LDB_SUCCESS) {
3039 talloc_free(tmp_ctx);
3043 ret = check_parsed_dn_duplicates(module, el, dns);
3044 if (ret != LDB_SUCCESS) {
3045 talloc_free(tmp_ctx);
3049 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3051 if (ret != LDB_SUCCESS) {
3052 talloc_free(tmp_ctx);
3056 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3058 if (ret != LDB_SUCCESS) {
3059 talloc_free(tmp_ctx);
3063 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3064 if (new_values == NULL) {
3065 ldb_module_oom(module);
3066 talloc_free(tmp_ctx);
3067 return LDB_ERR_OPERATIONS_ERROR;
3072 for (i = 0; i < max_num_values; i++) {
3074 struct parsed_dn *old_p, *new_p;
3075 if (old_i < old_num_values && new_i < repl_num_values) {
3076 old_p = &old_dns[old_i];
3077 new_p = &dns[new_i];
3078 cmp = parsed_dn_compare(old_p, new_p);
3079 } else if (old_i < old_num_values) {
3080 /* the new list is empty, read the old list */
3081 old_p = &old_dns[old_i];
3084 } else if (new_i < repl_num_values) {
3085 /* the old list is empty, read new list */
3087 new_p = &dns[new_i];
3095 * An old ones that come before the next replacement
3096 * (if any). We mark it as deleted and add it to the
3099 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3100 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3101 ret = replmd_update_la_val(new_values, old_p->v,
3104 &ac->our_invocation_id,
3105 ac->seq_num, ac->seq_num,
3107 if (ret != LDB_SUCCESS) {
3108 talloc_free(tmp_ctx);
3112 ret = replmd_add_backlink(module, replmd_private,
3115 &old_p->guid, false,
3118 if (ret != LDB_SUCCESS) {
3119 talloc_free(tmp_ctx);
3123 new_values[i] = *old_p->v;
3125 } else if (cmp == 0) {
3127 * We are overwriting one. If it was previously
3128 * deleted, we need to add a backlink.
3130 * Note that if any RMD_FLAGs in an extended new DN
3135 ret = replmd_update_la_val(new_values, old_p->v,
3138 &ac->our_invocation_id,
3139 ac->seq_num, ac->seq_num,
3141 if (ret != LDB_SUCCESS) {
3142 talloc_free(tmp_ctx);
3146 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3147 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3148 ret = replmd_add_backlink(module, replmd_private,
3154 if (ret != LDB_SUCCESS) {
3155 talloc_free(tmp_ctx);
3160 new_values[i] = *old_p->v;
3165 * Replacements that don't match an existing one. We
3166 * just add them to the final list.
3168 ret = replmd_build_la_val(new_values,
3171 &ac->our_invocation_id,
3173 if (ret != LDB_SUCCESS) {
3174 talloc_free(tmp_ctx);
3177 ret = replmd_add_backlink(module, replmd_private,
3183 if (ret != LDB_SUCCESS) {
3184 talloc_free(tmp_ctx);
3187 new_values[i] = *new_p->v;
3191 if (old_el != NULL) {
3192 talloc_steal(msg->elements, old_el->values);
3194 el->values = talloc_steal(msg->elements, new_values);
3196 talloc_free(tmp_ctx);
3198 el->flags = LDB_FLAG_MOD_REPLACE;
3205 handle linked attributes in modify requests
3207 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3208 struct replmd_private *replmd_private,
3209 struct replmd_replicated_request *ac,
3210 struct ldb_message *msg,
3212 struct ldb_request *parent)
3214 struct ldb_result *res;
3217 struct ldb_context *ldb = ldb_module_get_ctx(module);
3218 struct ldb_message *old_msg;
3220 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3222 * Nothing special is required for modifying or vanishing links
3223 * in fl2000 since they are just strings in a multi-valued
3226 struct ldb_control *ctrl = ldb_request_get_control(parent,
3227 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3229 ctrl->critical = false;
3237 * We should restrict this to the intersection of the list of
3238 * linked attributes in the schema and the list of attributes
3241 * This will help performance a little, as otherwise we have
3242 * to allocate the entire object value-by-value.
3244 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3245 DSDB_FLAG_NEXT_MODULE |
3246 DSDB_SEARCH_SHOW_RECYCLED |
3247 DSDB_SEARCH_REVEAL_INTERNALS |
3248 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3250 if (ret != LDB_SUCCESS) {
3254 old_msg = res->msgs[0];
3256 for (i=0; i<msg->num_elements; i++) {
3257 struct ldb_message_element *el = &msg->elements[i];
3258 struct ldb_message_element *old_el, *new_el;
3259 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3260 const struct dsdb_attribute *schema_attr
3261 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3263 ldb_asprintf_errstring(ldb,
3264 "%s: attribute %s is not a valid attribute in schema",
3265 __FUNCTION__, el->name);
3266 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3268 if (schema_attr->linkID == 0) {
3271 if ((schema_attr->linkID & 1) == 1) {
3273 struct ldb_control *ctrl;
3275 ctrl = ldb_request_get_control(parent,
3276 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3278 ctrl->critical = false;
3281 ctrl = ldb_request_get_control(parent,
3282 DSDB_CONTROL_DBCHECK);
3288 /* Odd is for the target. Illegal to modify */
3289 ldb_asprintf_errstring(ldb,
3290 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3291 return LDB_ERR_UNWILLING_TO_PERFORM;
3293 old_el = ldb_msg_find_element(old_msg, el->name);
3295 case LDB_FLAG_MOD_REPLACE:
3296 ret = replmd_modify_la_replace(module, replmd_private,
3297 ac, msg, el, old_el,
3302 case LDB_FLAG_MOD_DELETE:
3303 ret = replmd_modify_la_delete(module, replmd_private,
3304 ac, msg, el, old_el,
3309 case LDB_FLAG_MOD_ADD:
3310 ret = replmd_modify_la_add(module, replmd_private,
3311 ac, msg, el, old_el,
3317 ldb_asprintf_errstring(ldb,
3318 "invalid flags 0x%x for %s linked attribute",
3319 el->flags, el->name);
3320 return LDB_ERR_UNWILLING_TO_PERFORM;
3322 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3323 ldb_asprintf_errstring(ldb,
3324 "Attribute %s is single valued but more than one value has been supplied",
3326 /* Return codes as found on Windows 2012r2 */
3327 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3328 return LDB_ERR_CONSTRAINT_VIOLATION;
3330 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3333 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3336 if (ret != LDB_SUCCESS) {
3340 ldb_msg_remove_attr(old_msg, el->name);
3342 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3343 new_el->num_values = el->num_values;
3344 new_el->values = talloc_steal(msg->elements, el->values);
3346 /* TODO: this relises a bit too heavily on the exact
3347 behaviour of ldb_msg_find_element and
3348 ldb_msg_remove_element */
3349 old_el = ldb_msg_find_element(msg, el->name);
3351 ldb_msg_remove_element(msg, old_el);
3361 static int send_rodc_referral(struct ldb_request *req,
3362 struct ldb_context *ldb,
3365 char *referral = NULL;
3366 struct loadparm_context *lp_ctx = NULL;
3367 struct ldb_dn *fsmo_role_dn = NULL;
3368 struct ldb_dn *role_owner_dn = NULL;
3369 const char *domain = NULL;
3372 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3373 struct loadparm_context);
3375 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3376 &fsmo_role_dn, &role_owner_dn);
3378 if (W_ERROR_IS_OK(werr)) {
3379 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3380 if (server_dn != NULL) {
3381 ldb_dn_remove_child_components(server_dn, 1);
3382 domain = samdb_dn_to_dnshostname(ldb, req,
3387 if (domain == NULL) {
3388 domain = lpcfg_dnsdomain(lp_ctx);
3391 referral = talloc_asprintf(req, "ldap://%s/%s",
3393 ldb_dn_get_linearized(dn));
3394 if (referral == NULL) {
3396 return LDB_ERR_OPERATIONS_ERROR;
3399 return ldb_module_send_referral(req, referral);
3403 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3405 struct ldb_context *ldb;
3406 struct replmd_replicated_request *ac;
3407 struct ldb_request *down_req;
3408 struct ldb_message *msg;
3409 time_t t = time(NULL);
3411 bool is_urgent = false, rodc = false;
3412 bool is_schema_nc = false;
3413 unsigned int functional_level;
3414 const struct ldb_message_element *guid_el = NULL;
3415 struct ldb_control *sd_propagation_control;
3416 struct ldb_control *fix_links_control = NULL;
3417 struct ldb_control *fix_dn_name_control = NULL;
3418 struct ldb_control *fix_dn_sid_control = NULL;
3419 struct replmd_private *replmd_private =
3420 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3422 /* do not manipulate our control entries */
3423 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3424 return ldb_next_request(module, req);
3427 sd_propagation_control = ldb_request_get_control(req,
3428 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3429 if (sd_propagation_control != NULL) {
3430 if (req->op.mod.message->num_elements != 1) {
3431 return ldb_module_operr(module);
3433 ret = strcmp(req->op.mod.message->elements[0].name,
3434 "nTSecurityDescriptor");
3436 return ldb_module_operr(module);
3439 return ldb_next_request(module, req);
3442 ldb = ldb_module_get_ctx(module);
3444 fix_links_control = ldb_request_get_control(req,
3445 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3446 if (fix_links_control != NULL) {
3447 struct dsdb_schema *schema = NULL;
3448 const struct dsdb_attribute *sa = NULL;
3450 if (req->op.mod.message->num_elements != 1) {
3451 return ldb_module_operr(module);
3454 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3455 return ldb_module_operr(module);
3458 schema = dsdb_get_schema(ldb, req);
3459 if (schema == NULL) {
3460 return ldb_module_operr(module);
3463 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3464 req->op.mod.message->elements[0].name);
3466 return ldb_module_operr(module);
3469 if (sa->linkID == 0) {
3470 return ldb_module_operr(module);
3473 fix_links_control->critical = false;
3474 return ldb_next_request(module, req);
3477 fix_dn_name_control = ldb_request_get_control(req,
3478 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3479 if (fix_dn_name_control != NULL) {
3480 struct dsdb_schema *schema = NULL;
3481 const struct dsdb_attribute *sa = NULL;
3483 if (req->op.mod.message->num_elements != 2) {
3484 return ldb_module_operr(module);
3487 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3488 return ldb_module_operr(module);
3491 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3492 return ldb_module_operr(module);
3495 if (req->op.mod.message->elements[0].num_values != 1) {
3496 return ldb_module_operr(module);
3499 if (req->op.mod.message->elements[1].num_values != 1) {
3500 return ldb_module_operr(module);
3503 schema = dsdb_get_schema(ldb, req);
3504 if (schema == NULL) {
3505 return ldb_module_operr(module);
3508 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3509 req->op.mod.message->elements[1].name) != 0) {
3510 return ldb_module_operr(module);
3513 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3514 req->op.mod.message->elements[0].name);
3516 return ldb_module_operr(module);
3519 if (sa->dn_format == DSDB_INVALID_DN) {
3520 return ldb_module_operr(module);
3523 if (sa->linkID != 0) {
3524 return ldb_module_operr(module);
3528 * If we are run from dbcheck and we are not updating
3529 * a link (as these would need to be sorted and so
3530 * can't go via such a simple update, then do not
3531 * trigger replicated updates and a new USN from this
3532 * change, it wasn't a real change, just a new
3533 * (correct) string DN
3536 fix_dn_name_control->critical = false;
3537 return ldb_next_request(module, req);
3540 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3542 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3543 if (guid_el != NULL) {
3544 ldb_set_errstring(ldb,
3545 "replmd_modify: it's not allowed to change the objectGUID!");
3546 return LDB_ERR_CONSTRAINT_VIOLATION;
3549 ac = replmd_ctx_init(module, req);
3551 return ldb_module_oom(module);
3554 functional_level = dsdb_functional_level(ldb);
3556 /* we have to copy the message as the caller might have it as a const */
3557 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3561 return LDB_ERR_OPERATIONS_ERROR;
3564 fix_dn_sid_control = ldb_request_get_control(req,
3565 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3566 if (fix_dn_sid_control != NULL) {
3567 const struct dsdb_attribute *sa = NULL;
3569 if (msg->num_elements != 1) {
3571 return ldb_module_operr(module);
3574 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3576 return ldb_module_operr(module);
3579 if (msg->elements[0].num_values != 1) {
3581 return ldb_module_operr(module);
3584 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3585 msg->elements[0].name);
3588 return ldb_module_operr(module);
3591 if (sa->dn_format != DSDB_NORMAL_DN) {
3593 return ldb_module_operr(module);
3596 fix_dn_sid_control->critical = false;
3597 ac->fix_link_sid = true;
3599 goto handle_linked_attribs;
3602 ldb_msg_remove_attr(msg, "whenChanged");
3603 ldb_msg_remove_attr(msg, "uSNChanged");
3605 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3607 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3608 msg, &ac->seq_num, t, is_schema_nc,
3610 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3611 ret = send_rodc_referral(req, ldb, msg->dn);
3617 if (ret != LDB_SUCCESS) {
3622 handle_linked_attribs:
3623 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3625 if (ret != LDB_SUCCESS) {
3631 * - replace the old object with the newly constructed one
3634 ac->is_urgent = is_urgent;
3636 ret = ldb_build_mod_req(&down_req, ldb, ac,
3639 ac, replmd_op_callback,
3641 LDB_REQ_SET_LOCATION(down_req);
3642 if (ret != LDB_SUCCESS) {
3647 /* current partition control is needed by "replmd_op_callback" */
3648 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3649 ret = ldb_request_add_control(down_req,
3650 DSDB_CONTROL_CURRENT_PARTITION_OID,
3652 if (ret != LDB_SUCCESS) {
3658 /* If we are in functional level 2000, then
3659 * replmd_modify_handle_linked_attribs will have done
3661 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3662 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3663 if (ret != LDB_SUCCESS) {
3669 talloc_steal(down_req, msg);
3671 /* we only change whenChanged and uSNChanged if the seq_num
3673 if (ac->seq_num != 0) {
3674 ret = add_time_element(msg, "whenChanged", t);
3675 if (ret != LDB_SUCCESS) {
3681 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3682 if (ret != LDB_SUCCESS) {
3689 /* go on with the call chain */
3690 return ldb_next_request(module, down_req);
3693 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3696 handle a rename request
3698 On a rename we need to do an extra ldb_modify which sets the
3699 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3701 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3703 struct ldb_context *ldb;
3704 struct replmd_replicated_request *ac;
3706 struct ldb_request *down_req;
3708 /* do not manipulate our control entries */
3709 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3710 return ldb_next_request(module, req);
3713 ldb = ldb_module_get_ctx(module);
3715 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3717 ac = replmd_ctx_init(module, req);
3719 return ldb_module_oom(module);
3722 ret = ldb_build_rename_req(&down_req, ldb, ac,
3723 ac->req->op.rename.olddn,
3724 ac->req->op.rename.newdn,
3726 ac, replmd_rename_callback,
3728 LDB_REQ_SET_LOCATION(down_req);
3729 if (ret != LDB_SUCCESS) {
3734 /* go on with the call chain */
3735 return ldb_next_request(module, down_req);
3738 /* After the rename is compleated, update the whenchanged etc */
3739 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3741 struct ldb_context *ldb;
3742 struct ldb_request *down_req;
3743 struct ldb_message *msg;
3744 const struct dsdb_attribute *rdn_attr;
3745 const char *rdn_name;
3746 const struct ldb_val *rdn_val;
3747 const char *attrs[5] = { NULL, };
3748 time_t t = time(NULL);
3750 bool is_urgent = false, rodc = false;
3752 struct replmd_replicated_request *ac =
3753 talloc_get_type(req->context, struct replmd_replicated_request);
3754 struct replmd_private *replmd_private =
3755 talloc_get_type(ldb_module_get_private(ac->module),
3756 struct replmd_private);
3758 ldb = ldb_module_get_ctx(ac->module);
3760 if (ares->error != LDB_SUCCESS) {
3761 return ldb_module_done(ac->req, ares->controls,
3762 ares->response, ares->error);
3765 if (ares->type != LDB_REPLY_DONE) {
3766 ldb_set_errstring(ldb,
3767 "invalid ldb_reply_type in callback");
3769 return ldb_module_done(ac->req, NULL, NULL,
3770 LDB_ERR_OPERATIONS_ERROR);
3774 * - replace the old object with the newly constructed one
3777 msg = ldb_msg_new(ac);
3780 return LDB_ERR_OPERATIONS_ERROR;
3783 msg->dn = ac->req->op.rename.newdn;
3785 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3787 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3788 if (rdn_name == NULL) {
3790 return ldb_module_done(ac->req, NULL, NULL,
3794 /* normalize the rdn attribute name */
3795 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3796 if (rdn_attr == NULL) {
3798 return ldb_module_done(ac->req, NULL, NULL,
3801 rdn_name = rdn_attr->lDAPDisplayName;
3803 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3804 if (rdn_val == NULL) {
3806 return ldb_module_done(ac->req, NULL, NULL,
3810 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3812 return ldb_module_done(ac->req, NULL, NULL,
3815 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3817 return ldb_module_done(ac->req, NULL, NULL,
3820 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3822 return ldb_module_done(ac->req, NULL, NULL,
3825 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3827 return ldb_module_done(ac->req, NULL, NULL,
3832 * here we let replmd_update_rpmd() only search for
3833 * the existing "replPropertyMetaData" and rdn_name attributes.
3835 * We do not want the existing "name" attribute as
3836 * the "name" attribute needs to get the version
3837 * updated on rename even if the rdn value hasn't changed.
3839 * This is the diff of the meta data, for a moved user
3840 * on a w2k8r2 server:
3843 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3844 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3845 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3846 * version : 0x00000001 (1)
3847 * reserved : 0x00000000 (0)
3848 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3849 * local_usn : 0x00000000000037a5 (14245)
3850 * array: struct replPropertyMetaData1
3851 * attid : DRSUAPI_ATTID_name (0x90001)
3852 * - version : 0x00000001 (1)
3853 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3854 * + version : 0x00000002 (2)
3855 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3856 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3857 * - originating_usn : 0x00000000000037a5 (14245)
3858 * - local_usn : 0x00000000000037a5 (14245)
3859 * + originating_usn : 0x0000000000003834 (14388)
3860 * + local_usn : 0x0000000000003834 (14388)
3861 * array: struct replPropertyMetaData1
3862 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3863 * version : 0x00000004 (4)
3865 attrs[0] = "replPropertyMetaData";
3866 attrs[1] = "objectClass";
3867 attrs[2] = "instanceType";
3868 attrs[3] = rdn_name;
3871 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3872 msg, &ac->seq_num, t,
3873 is_schema_nc, &is_urgent, &rodc);
3874 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3875 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3877 return ldb_module_done(req, NULL, NULL, ret);
3880 if (ret != LDB_SUCCESS) {
3882 return ldb_module_done(ac->req, NULL, NULL, ret);
3885 if (ac->seq_num == 0) {
3887 return ldb_module_done(ac->req, NULL, NULL,
3889 "internal error seq_num == 0"));
3891 ac->is_urgent = is_urgent;
3893 ret = ldb_build_mod_req(&down_req, ldb, ac,
3896 ac, replmd_op_callback,
3898 LDB_REQ_SET_LOCATION(down_req);
3899 if (ret != LDB_SUCCESS) {
3904 /* current partition control is needed by "replmd_op_callback" */
3905 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3906 ret = ldb_request_add_control(down_req,
3907 DSDB_CONTROL_CURRENT_PARTITION_OID,
3909 if (ret != LDB_SUCCESS) {
3915 talloc_steal(down_req, msg);
3917 ret = add_time_element(msg, "whenChanged", t);
3918 if (ret != LDB_SUCCESS) {
3924 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3925 if (ret != LDB_SUCCESS) {
3931 /* go on with the call chain - do the modify after the rename */
3932 return ldb_next_request(ac->module, down_req);
3936 * remove links from objects that point at this object when an object
3937 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3938 * RemoveObj which states that link removal due to the object being
3939 * deleted is NOT an originating update - they just go away!
3942 static int replmd_delete_remove_link(struct ldb_module *module,
3943 const struct dsdb_schema *schema,
3944 struct replmd_private *replmd_private,
3947 struct ldb_message_element *el,
3948 const struct dsdb_attribute *sa,
3949 struct ldb_request *parent)
3952 TALLOC_CTX *tmp_ctx = talloc_new(module);
3953 struct ldb_context *ldb = ldb_module_get_ctx(module);
3955 for (i=0; i<el->num_values; i++) {
3956 struct dsdb_dn *dsdb_dn;
3958 struct ldb_message *msg;
3959 const struct dsdb_attribute *target_attr;
3960 struct ldb_message_element *el2;
3962 struct ldb_val dn_val;
3963 uint32_t dsdb_flags = 0;
3964 const char *attrs[] = { NULL, NULL };
3965 struct ldb_result *link_res;
3966 struct ldb_message *link_msg;
3967 struct ldb_message_element *link_el;
3968 struct parsed_dn *link_dns;
3969 struct parsed_dn *p = NULL, *unused = NULL;
3971 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3975 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3977 talloc_free(tmp_ctx);
3978 return LDB_ERR_OPERATIONS_ERROR;
3981 /* remove the link */
3982 msg = ldb_msg_new(tmp_ctx);
3984 ldb_module_oom(module);
3985 talloc_free(tmp_ctx);
3986 return LDB_ERR_OPERATIONS_ERROR;
3990 msg->dn = dsdb_dn->dn;
3992 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3993 if (target_attr == NULL) {
3996 attrs[0] = target_attr->lDAPDisplayName;
3998 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3999 LDB_FLAG_MOD_DELETE, &el2);
4000 if (ret != LDB_SUCCESS) {
4001 ldb_module_oom(module);
4002 talloc_free(tmp_ctx);
4003 return LDB_ERR_OPERATIONS_ERROR;
4006 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4008 DSDB_FLAG_NEXT_MODULE |
4009 DSDB_SEARCH_SHOW_EXTENDED_DN |
4010 DSDB_SEARCH_SHOW_RECYCLED,
4013 if (ret != LDB_SUCCESS) {
4014 talloc_free(tmp_ctx);
4018 link_msg = link_res->msgs[0];
4019 link_el = ldb_msg_find_element(link_msg,
4020 target_attr->lDAPDisplayName);
4021 if (link_el == NULL) {
4022 talloc_free(tmp_ctx);
4023 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4027 * This call 'upgrades' the links in link_dns, but we
4028 * do not commit the result back into the database, so
4029 * this is safe to call in FL2000 or on databases that
4030 * have been run at that level in the past.
4032 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
4034 target_attr->syntax->ldap_oid, parent);
4035 if (ret != LDB_SUCCESS) {
4036 talloc_free(tmp_ctx);
4040 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4044 target_attr->syntax->ldap_oid, false);
4045 if (ret != LDB_SUCCESS) {
4046 talloc_free(tmp_ctx);
4051 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4052 "Failed to find forward link on %s "
4053 "as %s to remove backlink %s on %s",
4054 ldb_dn_get_linearized(msg->dn),
4055 target_attr->lDAPDisplayName,
4056 sa->lDAPDisplayName,
4057 ldb_dn_get_linearized(dn));
4058 talloc_free(tmp_ctx);
4059 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4063 /* This needs to get the Binary DN, by first searching */
4064 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4067 dn_val = data_blob_string_const(dn_str);
4068 el2->values = &dn_val;
4069 el2->num_values = 1;
4072 * Ensure that we tell the modification to vanish any linked
4073 * attributes (not simply mark them as isDeleted = TRUE)
4075 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4077 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4078 if (ret != LDB_SUCCESS) {
4079 talloc_free(tmp_ctx);
4083 talloc_free(tmp_ctx);
4089 handle update of replication meta data for deletion of objects
4091 This also handles the mapping of delete to a rename operation
4092 to allow deletes to be replicated.
4094 It also handles the incoming deleted objects, to ensure they are
4095 fully deleted here. In that case re_delete is true, and we do not
4096 use this as a signal to change the deleted state, just reinforce it.
4099 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4101 int ret = LDB_ERR_OTHER;
4102 bool retb, disallow_move_on_delete;
4103 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4104 const char *rdn_name;
4105 const struct ldb_val *rdn_value, *new_rdn_value;
4107 struct ldb_context *ldb = ldb_module_get_ctx(module);
4108 const struct dsdb_schema *schema;
4109 struct ldb_message *msg, *old_msg;
4110 struct ldb_message_element *el;
4111 TALLOC_CTX *tmp_ctx;
4112 struct ldb_result *res, *parent_res;
4113 static const char * const preserved_attrs[] = {
4114 /* yes, this really is a hard coded list. See MS-ADTS
4115 section 3.1.1.5.5.1.1 */
4118 "dNReferenceUpdate",
4129 "msDS-LastKnownRDN",
4135 "distinguishedName",
4139 "proxiedObjectName",
4141 "nTSecurityDescriptor",
4142 "replPropertyMetaData",
4144 "securityIdentifier",
4152 "userAccountControl",
4159 static const char * const all_attrs[] = {
4160 DSDB_SECRET_ATTRIBUTES,
4164 static const struct ldb_val true_val = {
4165 .data = discard_const_p(uint8_t, "TRUE"),
4170 uint32_t dsdb_flags = 0;
4171 struct replmd_private *replmd_private;
4172 enum deletion_state deletion_state, next_deletion_state;
4174 if (ldb_dn_is_special(req->op.del.dn)) {
4175 return ldb_next_request(module, req);
4179 * We have to allow dbcheck to remove an object that
4180 * is beyond repair, and to do so totally. This could
4181 * mean we we can get a partial object from the other
4182 * DC, causing havoc, so dbcheck suggests
4183 * re-replication first. dbcheck sets both DBCHECK
4184 * and RELAX in this situation.
4186 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4187 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4188 /* really, really remove it */
4189 return ldb_next_request(module, req);
4192 tmp_ctx = talloc_new(ldb);
4195 return LDB_ERR_OPERATIONS_ERROR;
4198 schema = dsdb_get_schema(ldb, tmp_ctx);
4200 talloc_free(tmp_ctx);
4201 return LDB_ERR_OPERATIONS_ERROR;
4204 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4206 /* we need the complete msg off disk, so we can work out which
4207 attributes need to be removed */
4208 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4209 DSDB_FLAG_NEXT_MODULE |
4210 DSDB_SEARCH_SHOW_RECYCLED |
4211 DSDB_SEARCH_REVEAL_INTERNALS |
4212 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4213 if (ret != LDB_SUCCESS) {
4214 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4215 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4216 re_delete ? "re-delete" : "delete",
4217 ldb_dn_get_linearized(old_dn),
4218 ldb_errstring(ldb_module_get_ctx(module)));
4219 talloc_free(tmp_ctx);
4222 old_msg = res->msgs[0];
4224 replmd_deletion_state(module, old_msg,
4226 &next_deletion_state);
4228 /* This supports us noticing an incoming isDeleted and acting on it */
4230 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4231 next_deletion_state = deletion_state;
4234 if (next_deletion_state == OBJECT_REMOVED) {
4236 * We have to prevent objects being deleted, even if
4237 * the administrator really wants them gone, as
4238 * without the tombstone, we can get a partial object
4239 * from the other DC, causing havoc.
4241 * The only other valid case is when the 180 day
4242 * timeout has expired, when relax is specified.
4244 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4245 /* it is already deleted - really remove it this time */
4246 talloc_free(tmp_ctx);
4247 return ldb_next_request(module, req);
4250 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4251 "This check is to prevent corruption of the replicated state.",
4252 ldb_dn_get_linearized(old_msg->dn));
4253 return LDB_ERR_UNWILLING_TO_PERFORM;
4256 rdn_name = ldb_dn_get_rdn_name(old_dn);
4257 rdn_value = ldb_dn_get_rdn_val(old_dn);
4258 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4259 talloc_free(tmp_ctx);
4260 return ldb_operr(ldb);
4263 msg = ldb_msg_new(tmp_ctx);
4265 ldb_module_oom(module);
4266 talloc_free(tmp_ctx);
4267 return LDB_ERR_OPERATIONS_ERROR;
4272 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4273 disallow_move_on_delete =
4274 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4275 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4277 /* work out where we will be renaming this object to */
4278 if (!disallow_move_on_delete) {
4279 struct ldb_dn *deleted_objects_dn;
4280 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4281 &deleted_objects_dn);
4284 * We should not move objects if we can't find the
4285 * deleted objects DN. Not moving (or otherwise
4286 * harming) the Deleted Objects DN itself is handled
4289 if (re_delete && (ret != LDB_SUCCESS)) {
4290 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4291 if (new_dn == NULL) {
4292 ldb_module_oom(module);
4293 talloc_free(tmp_ctx);
4294 return LDB_ERR_OPERATIONS_ERROR;
4296 } else if (ret != LDB_SUCCESS) {
4297 /* this is probably an attempted delete on a partition
4298 * that doesn't allow delete operations, such as the
4299 * schema partition */
4300 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4301 ldb_dn_get_linearized(old_dn));
4302 talloc_free(tmp_ctx);
4303 return LDB_ERR_UNWILLING_TO_PERFORM;
4305 new_dn = deleted_objects_dn;
4308 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4309 if (new_dn == NULL) {
4310 ldb_module_oom(module);
4311 talloc_free(tmp_ctx);
4312 return LDB_ERR_OPERATIONS_ERROR;
4316 /* get the objects GUID from the search we just did */
4317 guid = samdb_result_guid(old_msg, "objectGUID");
4319 if (deletion_state == OBJECT_NOT_DELETED) {
4320 struct ldb_message_element *is_deleted_el;
4322 ret = replmd_make_deleted_child_dn(tmp_ctx,
4325 rdn_name, rdn_value,
4328 if (ret != LDB_SUCCESS) {
4329 talloc_free(tmp_ctx);
4333 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4335 if (ret != LDB_SUCCESS) {
4336 ldb_asprintf_errstring(ldb, __location__
4337 ": Failed to add isDeleted string to the msg");
4338 talloc_free(tmp_ctx);
4341 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4344 * No matter what has happened with other renames etc, try again to
4345 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4348 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4349 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4351 ldb_asprintf_errstring(ldb, __location__
4352 ": Unable to add a prepare rdn of %s",
4353 ldb_dn_get_linearized(rdn));
4354 talloc_free(tmp_ctx);
4355 return LDB_ERR_OPERATIONS_ERROR;
4357 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4359 retb = ldb_dn_add_child(new_dn, rdn);
4361 ldb_asprintf_errstring(ldb, __location__
4362 ": Unable to add rdn %s to base dn: %s",
4363 ldb_dn_get_linearized(rdn),
4364 ldb_dn_get_linearized(new_dn));
4365 talloc_free(tmp_ctx);
4366 return LDB_ERR_OPERATIONS_ERROR;
4371 now we need to modify the object in the following ways:
4373 - add isDeleted=TRUE
4374 - update rDN and name, with new rDN
4375 - remove linked attributes
4376 - remove objectCategory and sAMAccountType
4377 - remove attribs not on the preserved list
4378 - preserved if in above list, or is rDN
4379 - remove all linked attribs from this object
4380 - remove all links from other objects to this object
4381 (note we use the backlinks to do this, so we won't find one-way
4382 links that still point to this object, or deactivated two-way
4383 links, i.e. 'member' after the user has been removed from the
4385 - add lastKnownParent
4386 - update replPropertyMetaData?
4388 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4391 if (deletion_state == OBJECT_NOT_DELETED) {
4392 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4393 char *parent_dn_str = NULL;
4394 struct ldb_message_element *p_el;
4396 /* we need the storage form of the parent GUID */
4397 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4399 DSDB_FLAG_NEXT_MODULE |
4400 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4401 DSDB_SEARCH_REVEAL_INTERNALS|
4402 DSDB_SEARCH_SHOW_RECYCLED, req);
4403 if (ret != LDB_SUCCESS) {
4404 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4405 "repmd_delete: Failed to %s %s, "
4406 "because we failed to find it's parent (%s): %s",
4407 re_delete ? "re-delete" : "delete",
4408 ldb_dn_get_linearized(old_dn),
4409 ldb_dn_get_linearized(parent_dn),
4410 ldb_errstring(ldb_module_get_ctx(module)));
4411 talloc_free(tmp_ctx);
4416 * Now we can use the DB version,
4417 * it will have the extended DN info in it
4419 parent_dn = parent_res->msgs[0]->dn;
4420 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4423 if (parent_dn_str == NULL) {
4424 talloc_free(tmp_ctx);
4425 return ldb_module_oom(module);
4428 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4430 if (ret != LDB_SUCCESS) {
4431 ldb_asprintf_errstring(ldb, __location__
4432 ": Failed to add lastKnownParent "
4433 "string when deleting %s",
4434 ldb_dn_get_linearized(old_dn));
4435 talloc_free(tmp_ctx);
4438 p_el = ldb_msg_find_element(msg,
4441 talloc_free(tmp_ctx);
4442 return ldb_module_operr(module);
4444 p_el->flags = LDB_FLAG_MOD_REPLACE;
4446 if (next_deletion_state == OBJECT_DELETED) {
4447 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4448 if (ret != LDB_SUCCESS) {
4449 ldb_asprintf_errstring(ldb, __location__
4450 ": Failed to add msDS-LastKnownRDN "
4451 "string when deleting %s",
4452 ldb_dn_get_linearized(old_dn));
4453 talloc_free(tmp_ctx);
4456 p_el = ldb_msg_find_element(msg,
4457 "msDS-LastKnownRDN");
4459 talloc_free(tmp_ctx);
4460 return ldb_module_operr(module);
4462 p_el->flags = LDB_FLAG_MOD_ADD;
4466 switch (next_deletion_state) {
4468 case OBJECT_RECYCLED:
4469 case OBJECT_TOMBSTONE:
4472 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4473 * describes what must be removed from a tombstone
4476 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4477 * describes what must be removed from a recycled
4483 * we also mark it as recycled, meaning this object can't be
4484 * recovered (we are stripping its attributes).
4485 * This is done only if we have this schema object of course ...
4486 * This behavior is identical to the one of Windows 2008R2 which
4487 * always set the isRecycled attribute, even if the recycle-bin is
4488 * not activated and what ever the forest level is.
4490 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4491 struct ldb_message_element *is_recycled_el;
4493 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4495 if (ret != LDB_SUCCESS) {
4496 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4497 ldb_module_oom(module);
4498 talloc_free(tmp_ctx);
4501 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4504 replmd_private = talloc_get_type(ldb_module_get_private(module),
4505 struct replmd_private);
4506 /* work out which of the old attributes we will be removing */
4507 for (i=0; i<old_msg->num_elements; i++) {
4508 const struct dsdb_attribute *sa;
4509 el = &old_msg->elements[i];
4510 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4512 talloc_free(tmp_ctx);
4513 return LDB_ERR_OPERATIONS_ERROR;
4515 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4516 /* don't remove the rDN */
4520 if (sa->linkID & 1) {
4522 * we have a backlink in this object
4523 * that needs to be removed. We're not
4524 * allowed to remove it directly
4525 * however, so we instead setup a
4526 * modify to delete the corresponding
4529 ret = replmd_delete_remove_link(module, schema,
4533 if (ret == LDB_SUCCESS) {
4535 * now we continue, which means we
4536 * won't remove this backlink
4542 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4543 const char *old_dn_str
4544 = ldb_dn_get_linearized(old_dn);
4545 ldb_asprintf_errstring(ldb,
4547 ": Failed to remove backlink of "
4548 "%s when deleting %s: %s",
4551 ldb_errstring(ldb));
4552 talloc_free(tmp_ctx);
4553 return LDB_ERR_OPERATIONS_ERROR;
4557 * Otherwise vanish the link, we are
4558 * out of sync and the controlling
4559 * object does not have the source
4563 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4565 } else if (sa->linkID == 0) {
4566 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4569 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4574 * Ensure that we tell the modification to vanish any linked
4575 * attributes (not simply mark them as isDeleted = TRUE)
4577 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4579 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4580 if (ret != LDB_SUCCESS) {
4581 talloc_free(tmp_ctx);
4582 ldb_module_oom(module);
4589 case OBJECT_DELETED:
4591 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4592 * describes what must be removed from a deleted
4596 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4597 if (ret != LDB_SUCCESS) {
4598 talloc_free(tmp_ctx);
4599 ldb_module_oom(module);
4603 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4604 if (ret != LDB_SUCCESS) {
4605 talloc_free(tmp_ctx);
4606 ldb_module_oom(module);
4616 if (deletion_state == OBJECT_NOT_DELETED) {
4617 const struct dsdb_attribute *sa;
4619 /* work out what the new rdn value is, for updating the
4620 rDN and name fields */
4621 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4622 if (new_rdn_value == NULL) {
4623 talloc_free(tmp_ctx);
4624 return ldb_operr(ldb);
4627 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4629 talloc_free(tmp_ctx);
4630 return LDB_ERR_OPERATIONS_ERROR;
4633 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4635 if (ret != LDB_SUCCESS) {
4636 talloc_free(tmp_ctx);
4639 el->flags = LDB_FLAG_MOD_REPLACE;
4641 el = ldb_msg_find_element(old_msg, "name");
4643 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4644 if (ret != LDB_SUCCESS) {
4645 talloc_free(tmp_ctx);
4648 el->flags = LDB_FLAG_MOD_REPLACE;
4653 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4658 * No matter what has happned with other renames, try again to
4659 * get this to be under the deleted DN.
4661 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4662 /* now rename onto the new DN */
4663 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4664 if (ret != LDB_SUCCESS){
4665 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4666 ldb_dn_get_linearized(old_dn),
4667 ldb_dn_get_linearized(new_dn),
4668 ldb_errstring(ldb)));
4669 talloc_free(tmp_ctx);
4675 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4676 if (ret != LDB_SUCCESS) {
4677 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4678 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4679 talloc_free(tmp_ctx);
4683 talloc_free(tmp_ctx);
4685 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4688 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4690 return replmd_delete_internals(module, req, false);
4694 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4699 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4701 int ret = LDB_ERR_OTHER;
4702 /* TODO: do some error mapping */
4704 /* Let the caller know the full WERROR */
4705 ar->objs->error = status;
4711 static struct replPropertyMetaData1 *
4712 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4713 enum drsuapi_DsAttributeId attid)
4716 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4718 for (i = 0; i < rpmd_ctr->count; i++) {
4719 if (rpmd_ctr->array[i].attid == attid) {
4720 return &rpmd_ctr->array[i];
4728 return true if an update is newer than an existing entry
4729 see section 5.11 of MS-ADTS
4731 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4732 const struct GUID *update_invocation_id,
4733 uint32_t current_version,
4734 uint32_t update_version,
4735 NTTIME current_change_time,
4736 NTTIME update_change_time)
4738 if (update_version != current_version) {
4739 return update_version > current_version;
4741 if (update_change_time != current_change_time) {
4742 return update_change_time > current_change_time;
4744 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4747 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4748 struct replPropertyMetaData1 *new_m)
4750 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4751 &new_m->originating_invocation_id,
4754 cur_m->originating_change_time,
4755 new_m->originating_change_time);
4758 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4759 struct replPropertyMetaData1 *cur_m,
4760 struct replPropertyMetaData1 *new_m)
4765 * If the new replPropertyMetaData entry for this attribute is
4766 * not provided (this happens in the case where we look for
4767 * ATTID_name, but the name was not changed), then the local
4768 * state is clearly still current, as the remote
4769 * server didn't send it due to being older the high watermark
4772 if (new_m == NULL) {
4776 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4778 * if we compare equal then do an
4779 * update. This is used when a client
4780 * asks for a FULL_SYNC, and can be
4781 * used to recover a corrupt
4784 * This call is a bit tricky, what we
4785 * are doing it turning the 'is_newer'
4786 * call into a 'not is older' by
4787 * swapping cur_m and new_m, and negating the
4790 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4793 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4801 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4803 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4804 struct ldb_context *ldb,
4806 const char *four_char_prefix,
4807 const char *rdn_name,
4808 const struct ldb_val *rdn_value,
4811 struct ldb_val deleted_child_rdn_val;
4812 struct GUID_txt_buf guid_str;
4816 GUID_buf_string(&guid, &guid_str);
4818 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4820 ldb_asprintf_errstring(ldb, __location__
4821 ": Unable to add a formatted child to dn: %s",
4822 ldb_dn_get_linearized(dn));
4823 return LDB_ERR_OPERATIONS_ERROR;
4827 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4828 * we should truncate this value to ensure the RDN is not more than 255 chars.
4830 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4832 * "Naming constraints are not enforced for replicated
4833 * updates." so this is safe and we don't have to work out not
4834 * splitting a UTF8 char right now.
4836 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4839 * sizeof(guid_str.buf) will always be longer than
4840 * strlen(guid_str.buf) but we allocate using this and
4841 * waste the trailing bytes to avoid scaring folks
4842 * with memcpy() using strlen() below
4845 deleted_child_rdn_val.data
4846 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4848 rdn_value->length + 5
4849 + sizeof(guid_str.buf));
4850 if (!deleted_child_rdn_val.data) {
4851 ldb_asprintf_errstring(ldb, __location__
4852 ": Unable to add a formatted child to dn: %s",
4853 ldb_dn_get_linearized(dn));
4854 return LDB_ERR_OPERATIONS_ERROR;
4857 deleted_child_rdn_val.length =
4858 rdn_value->length + 5
4859 + strlen(guid_str.buf);
4861 SMB_ASSERT(deleted_child_rdn_val.length <
4862 talloc_get_size(deleted_child_rdn_val.data));
4865 * talloc won't allocate more than 256MB so we can't
4866 * overflow but just to be sure
4868 if (deleted_child_rdn_val.length < rdn_value->length) {
4869 return LDB_ERR_OPERATIONS_ERROR;
4872 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4873 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4874 four_char_prefix, 4);
4875 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4877 sizeof(guid_str.buf));
4879 /* Now set the value into the RDN, without parsing it */
4880 ret = ldb_dn_set_component(
4884 deleted_child_rdn_val);
4893 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4894 struct ldb_context *ldb,
4898 const struct ldb_val *rdn_val;
4899 const char *rdn_name;
4900 struct ldb_dn *new_dn;
4903 rdn_val = ldb_dn_get_rdn_val(dn);
4904 rdn_name = ldb_dn_get_rdn_name(dn);
4905 if (!rdn_val || !rdn_name) {
4909 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4914 ret = replmd_make_prefix_child_dn(mem_ctx,
4920 if (ret != LDB_SUCCESS) {
4929 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4930 struct ldb_context *ldb,
4932 const char *rdn_name,
4933 const struct ldb_val *rdn_value,
4936 return replmd_make_prefix_child_dn(tmp_ctx,
4946 perform a modify operation which sets the rDN and name attributes to
4947 their current values. This has the effect of changing these
4948 attributes to have been last updated by the current DC. This is
4949 needed to ensure that renames performed as part of conflict
4950 resolution are propagated to other DCs
4952 static int replmd_name_modify(struct replmd_replicated_request *ar,
4953 struct ldb_request *req, struct ldb_dn *dn)
4955 struct ldb_message *msg;
4956 const char *rdn_name;
4957 const struct ldb_val *rdn_val;
4958 const struct dsdb_attribute *rdn_attr;
4961 msg = ldb_msg_new(req);
4967 rdn_name = ldb_dn_get_rdn_name(dn);
4968 if (rdn_name == NULL) {
4972 /* normalize the rdn attribute name */
4973 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4974 if (rdn_attr == NULL) {
4977 rdn_name = rdn_attr->lDAPDisplayName;
4979 rdn_val = ldb_dn_get_rdn_val(dn);
4980 if (rdn_val == NULL) {
4984 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4987 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4990 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4993 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4998 * We have to mark this as a replicated update otherwise
4999 * schema_data may reject a rename in the schema partition
5002 ret = dsdb_module_modify(ar->module, msg,
5003 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5005 if (ret != LDB_SUCCESS) {
5006 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5007 ldb_dn_get_linearized(dn),
5008 ldb_errstring(ldb_module_get_ctx(ar->module))));
5018 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5019 ldb_dn_get_linearized(dn)));
5020 return LDB_ERR_OPERATIONS_ERROR;
5025 callback for conflict DN handling where we have renamed the incoming
5026 record. After renaming it, we need to ensure the change of name and
5027 rDN for the incoming record is seen as an originating update by this DC.
5029 This also handles updating lastKnownParent for entries sent to lostAndFound
5031 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5033 struct replmd_replicated_request *ar =
5034 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5035 struct ldb_dn *conflict_dn = NULL;
5038 if (ares->error != LDB_SUCCESS) {
5039 /* call the normal callback for everything except success */
5040 return replmd_op_callback(req, ares);
5043 switch (req->operation) {
5045 conflict_dn = req->op.add.message->dn;
5048 conflict_dn = req->op.mod.message->dn;
5051 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5054 /* perform a modify of the rDN and name of the record */
5055 ret = replmd_name_modify(ar, req, conflict_dn);
5056 if (ret != LDB_SUCCESS) {
5058 return replmd_op_callback(req, ares);
5061 if (ar->objs->objects[ar->index_current].last_known_parent) {
5062 struct ldb_message *msg = ldb_msg_new(req);
5064 ldb_module_oom(ar->module);
5065 return LDB_ERR_OPERATIONS_ERROR;
5068 msg->dn = req->op.add.message->dn;
5070 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5071 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5072 if (ret != LDB_SUCCESS) {
5073 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5074 ldb_module_oom(ar->module);
5077 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5079 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5080 if (ret != LDB_SUCCESS) {
5081 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5082 ldb_dn_get_linearized(msg->dn),
5083 ldb_errstring(ldb_module_get_ctx(ar->module))));
5089 return replmd_op_callback(req, ares);
5093 callback for replmd_replicated_apply_add()
5094 This copes with the creation of conflict records in the case where
5095 the DN exists, but with a different objectGUID
5097 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))
5099 struct ldb_dn *conflict_dn;
5100 struct replmd_replicated_request *ar =
5101 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5102 struct ldb_result *res;
5103 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5105 const struct ldb_val *omd_value;
5106 struct replPropertyMetaDataBlob omd, *rmd;
5107 enum ndr_err_code ndr_err;
5108 bool rename_incoming_record, rodc;
5109 struct replPropertyMetaData1 *rmd_name, *omd_name;
5110 struct ldb_message *msg;
5111 struct ldb_request *down_req = NULL;
5113 /* call the normal callback for success */
5114 if (ares->error == LDB_SUCCESS) {
5115 return callback(req, ares);
5119 * we have a conflict, and need to decide if we will keep the
5120 * new record or the old record
5123 msg = ar->objs->objects[ar->index_current].msg;
5124 conflict_dn = msg->dn;
5126 /* For failures other than conflicts, fail the whole operation here */
5127 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5128 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5129 ldb_dn_get_linearized(conflict_dn),
5130 ldb_errstring(ldb_module_get_ctx(ar->module)));
5132 return ldb_module_done(ar->req, NULL, NULL,
5133 LDB_ERR_OPERATIONS_ERROR);
5136 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5137 if (ret != LDB_SUCCESS) {
5138 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)));
5139 return ldb_module_done(ar->req, NULL, NULL,
5140 LDB_ERR_OPERATIONS_ERROR);
5146 * We are on an RODC, or were a GC for this
5147 * partition, so we have to fail this until
5148 * someone who owns the partition sorts it
5151 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5152 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5153 " - We must fail the operation until a master for this partition resolves the conflict",
5154 ldb_dn_get_linearized(conflict_dn));
5155 ret = LDB_ERR_OPERATIONS_ERROR;
5160 * first we need the replPropertyMetaData attribute from the
5161 * local, conflicting record
5163 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5165 DSDB_FLAG_NEXT_MODULE |
5166 DSDB_SEARCH_SHOW_DELETED |
5167 DSDB_SEARCH_SHOW_RECYCLED, req);
5168 if (ret != LDB_SUCCESS) {
5169 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5170 ldb_dn_get_linearized(conflict_dn)));
5174 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5175 if (omd_value == NULL) {
5176 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5177 ldb_dn_get_linearized(conflict_dn)));
5181 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5182 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5183 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5184 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5185 ldb_dn_get_linearized(conflict_dn)));
5189 rmd = ar->objs->objects[ar->index_current].meta_data;
5192 * we decide which is newer based on the RPMD on the name
5193 * attribute. See [MS-DRSR] ResolveNameConflict.
5195 * We expect omd_name to be present, as this is from a local
5196 * search, but while rmd_name should have been given to us by
5197 * the remote server, if it is missing we just prefer the
5199 * replmd_replPropertyMetaData1_new_should_be_taken()
5201 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5202 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5204 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5205 ldb_dn_get_linearized(conflict_dn)));
5210 * Should we preserve the current record, and so rename the
5211 * incoming record to be a conflict?
5213 rename_incoming_record
5214 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5215 omd_name, rmd_name);
5217 if (rename_incoming_record) {
5219 struct ldb_dn *new_dn;
5221 guid = samdb_result_guid(msg, "objectGUID");
5222 if (GUID_all_zero(&guid)) {
5223 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5224 ldb_dn_get_linearized(conflict_dn)));
5227 new_dn = replmd_conflict_dn(req,
5228 ldb_module_get_ctx(ar->module),
5229 conflict_dn, &guid);
5230 if (new_dn == NULL) {
5231 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5232 ldb_dn_get_linearized(conflict_dn)));
5236 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5237 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5239 /* re-submit the request, but with the new DN */
5240 callback = replmd_op_name_modify_callback;
5243 /* we are renaming the existing record */
5245 struct ldb_dn *new_dn;
5247 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5248 if (GUID_all_zero(&guid)) {
5249 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5250 ldb_dn_get_linearized(conflict_dn)));
5254 new_dn = replmd_conflict_dn(req,
5255 ldb_module_get_ctx(ar->module),
5256 conflict_dn, &guid);
5257 if (new_dn == NULL) {
5258 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5259 ldb_dn_get_linearized(conflict_dn)));
5263 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5264 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5266 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5267 DSDB_FLAG_OWN_MODULE, req);
5268 if (ret != LDB_SUCCESS) {
5269 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5270 ldb_dn_get_linearized(conflict_dn),
5271 ldb_dn_get_linearized(new_dn),
5272 ldb_errstring(ldb_module_get_ctx(ar->module))));
5277 * now we need to ensure that the rename is seen as an
5278 * originating update. We do that with a modify.
5280 ret = replmd_name_modify(ar, req, new_dn);
5281 if (ret != LDB_SUCCESS) {
5285 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5286 ldb_dn_get_linearized(req->op.add.message->dn)));
5289 ret = ldb_build_add_req(&down_req,
5290 ldb_module_get_ctx(ar->module),
5297 if (ret != LDB_SUCCESS) {
5300 LDB_REQ_SET_LOCATION(down_req);
5302 /* current partition control needed by "repmd_op_callback" */
5303 ret = ldb_request_add_control(down_req,
5304 DSDB_CONTROL_CURRENT_PARTITION_OID,
5306 if (ret != LDB_SUCCESS) {
5307 return replmd_replicated_request_error(ar, ret);
5310 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5311 /* this tells the partition module to make it a
5312 partial replica if creating an NC */
5313 ret = ldb_request_add_control(down_req,
5314 DSDB_CONTROL_PARTIAL_REPLICA,
5316 if (ret != LDB_SUCCESS) {
5317 return replmd_replicated_request_error(ar, ret);
5322 * Finally we re-run the add, otherwise the new record won't
5323 * exist, as we are here because of that exact failure!
5325 return ldb_next_request(ar->module, down_req);
5328 /* on failure make the caller get the error. This means
5329 * replication will stop with an error, but there is not much
5332 if (ret == LDB_SUCCESS) {
5333 ret = LDB_ERR_OPERATIONS_ERROR;
5335 return ldb_module_done(ar->req, NULL, NULL,
5340 callback for replmd_replicated_apply_add()
5341 This copes with the creation of conflict records in the case where
5342 the DN exists, but with a different objectGUID
5344 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5346 struct replmd_replicated_request *ar =
5347 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5349 if (ar->objs->objects[ar->index_current].last_known_parent) {
5350 /* This is like a conflict DN, where we put the object in LostAndFound
5351 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5352 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5355 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5359 this is called when a new object comes in over DRS
5361 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5363 struct ldb_context *ldb;
5364 struct ldb_request *change_req;
5365 enum ndr_err_code ndr_err;
5366 struct ldb_message *msg;
5367 struct replPropertyMetaDataBlob *md;
5368 struct ldb_val md_value;
5371 bool remote_isDeleted = false;
5374 time_t t = time(NULL);
5375 const struct ldb_val *rdn_val;
5376 struct replmd_private *replmd_private =
5377 talloc_get_type(ldb_module_get_private(ar->module),
5378 struct replmd_private);
5379 unix_to_nt_time(&now, t);
5381 ldb = ldb_module_get_ctx(ar->module);
5382 msg = ar->objs->objects[ar->index_current].msg;
5383 md = ar->objs->objects[ar->index_current].meta_data;
5384 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5386 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5387 if (ret != LDB_SUCCESS) {
5388 return replmd_replicated_request_error(ar, ret);
5391 ret = dsdb_msg_add_guid(msg,
5392 &ar->objs->objects[ar->index_current].object_guid,
5394 if (ret != LDB_SUCCESS) {
5395 return replmd_replicated_request_error(ar, ret);
5398 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5399 if (ret != LDB_SUCCESS) {
5400 return replmd_replicated_request_error(ar, ret);
5403 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5404 if (ret != LDB_SUCCESS) {
5405 return replmd_replicated_request_error(ar, ret);
5408 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5409 if (ret != LDB_SUCCESS) {
5410 return replmd_replicated_request_error(ar, ret);
5413 /* remove any message elements that have zero values */
5414 for (i=0; i<msg->num_elements; i++) {
5415 struct ldb_message_element *el = &msg->elements[i];
5417 if (el->num_values == 0) {
5418 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5419 ldb_asprintf_errstring(ldb, __location__
5420 ": empty objectClass sent on %s, aborting replication\n",
5421 ldb_dn_get_linearized(msg->dn));
5422 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5425 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5427 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5428 msg->num_elements--;
5435 struct GUID_txt_buf guid_txt;
5437 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5440 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5441 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5444 } else if (DEBUGLVL(4)) {
5445 struct GUID_txt_buf guid_txt;
5446 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5447 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5448 ldb_dn_get_linearized(msg->dn)));
5450 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5451 "isDeleted", false);
5454 * the meta data array is already sorted by the caller, except
5455 * for the RDN, which needs to be added.
5459 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5460 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5461 md, ar, now, is_schema_nc,
5463 if (ret != LDB_SUCCESS) {
5464 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5465 return replmd_replicated_request_error(ar, ret);
5468 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5469 if (ret != LDB_SUCCESS) {
5470 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5471 return replmd_replicated_request_error(ar, ret);
5474 for (i=0; i < md->ctr.ctr1.count; i++) {
5475 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5477 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5478 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5479 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5480 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5481 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5483 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5484 if (ret != LDB_SUCCESS) {
5485 return replmd_replicated_request_error(ar, ret);
5488 replmd_ldb_message_sort(msg, ar->schema);
5490 if (!remote_isDeleted) {
5491 ret = dsdb_module_schedule_sd_propagation(ar->module,
5492 ar->objs->partition_dn,
5494 if (ret != LDB_SUCCESS) {
5495 return replmd_replicated_request_error(ar, ret);
5499 ar->isDeleted = remote_isDeleted;
5501 ret = ldb_build_add_req(&change_req,
5507 replmd_op_add_callback,
5509 LDB_REQ_SET_LOCATION(change_req);
5510 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5512 /* current partition control needed by "repmd_op_callback" */
5513 ret = ldb_request_add_control(change_req,
5514 DSDB_CONTROL_CURRENT_PARTITION_OID,
5516 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5518 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5519 /* this tells the partition module to make it a
5520 partial replica if creating an NC */
5521 ret = ldb_request_add_control(change_req,
5522 DSDB_CONTROL_PARTIAL_REPLICA,
5524 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5527 return ldb_next_request(ar->module, change_req);
5530 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5531 struct ldb_reply *ares)
5533 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5534 struct replmd_replicated_request);
5538 return ldb_module_done(ar->req, NULL, NULL,
5539 LDB_ERR_OPERATIONS_ERROR);
5543 * The error NO_SUCH_OBJECT is not expected, unless the search
5544 * base is the partition DN, and that case doesn't happen here
5545 * because then we wouldn't get a parent_guid_value in any
5548 if (ares->error != LDB_SUCCESS) {
5549 return ldb_module_done(ar->req, ares->controls,
5550 ares->response, ares->error);
5553 switch (ares->type) {
5554 case LDB_REPLY_ENTRY:
5556 struct ldb_message *parent_msg = ares->message;
5557 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5558 struct ldb_dn *parent_dn = NULL;
5561 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5562 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5563 /* Per MS-DRSR 4.1.10.6.10
5564 * FindBestParentObject we need to move this
5565 * new object under a deleted object to
5567 struct ldb_dn *nc_root;
5569 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5570 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5571 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5572 "No suitable NC root found for %s. "
5573 "We need to move this object because parent object %s "
5574 "is deleted, but this object is not.",
5575 ldb_dn_get_linearized(msg->dn),
5576 ldb_dn_get_linearized(parent_msg->dn));
5577 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5578 } else if (ret != LDB_SUCCESS) {
5579 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5580 "Unable to find NC root for %s: %s. "
5581 "We need to move this object because parent object %s "
5582 "is deleted, but this object is not.",
5583 ldb_dn_get_linearized(msg->dn),
5584 ldb_errstring(ldb_module_get_ctx(ar->module)),
5585 ldb_dn_get_linearized(parent_msg->dn));
5586 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5589 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5591 DS_GUID_LOSTANDFOUND_CONTAINER,
5593 if (ret != LDB_SUCCESS) {
5594 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5595 "Unable to find LostAndFound Container for %s "
5596 "in partition %s: %s. "
5597 "We need to move this object because parent object %s "
5598 "is deleted, but this object is not.",
5599 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5600 ldb_errstring(ldb_module_get_ctx(ar->module)),
5601 ldb_dn_get_linearized(parent_msg->dn));
5602 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5604 ar->objs->objects[ar->index_current].last_known_parent
5605 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5609 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5612 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5614 comp_num = ldb_dn_get_comp_num(msg->dn);
5616 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5618 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5621 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5623 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5627 case LDB_REPLY_REFERRAL:
5628 /* we ignore referrals */
5631 case LDB_REPLY_DONE:
5633 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5634 struct GUID_txt_buf str_buf;
5635 if (ar->search_msg != NULL) {
5636 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5637 "No parent with GUID %s found for object locally known as %s",
5638 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5639 ldb_dn_get_linearized(ar->search_msg->dn));
5641 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5642 "No parent with GUID %s found for object remotely known as %s",
5643 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5644 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5648 * This error code is really important, as it
5649 * is the flag back to the callers to retry
5650 * this with DRSUAPI_DRS_GET_ANC, and so get
5651 * the parent objects before the child
5654 return ldb_module_done(ar->req, NULL, NULL,
5655 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5658 if (ar->search_msg != NULL) {
5659 ret = replmd_replicated_apply_merge(ar);
5661 ret = replmd_replicated_apply_add(ar);
5663 if (ret != LDB_SUCCESS) {
5664 return ldb_module_done(ar->req, NULL, NULL, ret);
5673 * Look for the parent object, so we put the new object in the right
5674 * place This is akin to NameObject in MS-DRSR - this routine and the
5675 * callbacks find the right parent name, and correct name for this
5679 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5681 struct ldb_context *ldb;
5685 struct ldb_request *search_req;
5686 static const char *attrs[] = {"isDeleted", NULL};
5687 struct GUID_txt_buf guid_str_buf;
5689 ldb = ldb_module_get_ctx(ar->module);
5691 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5692 if (ar->search_msg != NULL) {
5693 return replmd_replicated_apply_merge(ar);
5695 return replmd_replicated_apply_add(ar);
5699 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5702 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5703 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5705 ret = ldb_build_search_req(&search_req,
5708 ar->objs->partition_dn,
5714 replmd_replicated_apply_search_for_parent_callback,
5716 LDB_REQ_SET_LOCATION(search_req);
5718 ret = dsdb_request_add_controls(search_req,
5719 DSDB_SEARCH_SHOW_RECYCLED|
5720 DSDB_SEARCH_SHOW_DELETED|
5721 DSDB_SEARCH_SHOW_EXTENDED_DN);
5722 if (ret != LDB_SUCCESS) {
5726 return ldb_next_request(ar->module, search_req);
5730 handle renames that come in over DRS replication
5732 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5733 struct ldb_message *msg,
5734 struct ldb_request *parent,
5738 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5739 struct ldb_result *res;
5740 struct ldb_dn *conflict_dn;
5741 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5742 const struct ldb_val *omd_value;
5743 struct replPropertyMetaDataBlob omd, *rmd;
5744 enum ndr_err_code ndr_err;
5745 bool rename_incoming_record, rodc;
5746 struct replPropertyMetaData1 *rmd_name, *omd_name;
5747 struct ldb_dn *new_dn;
5750 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5751 ldb_dn_get_linearized(ar->search_msg->dn),
5752 ldb_dn_get_linearized(msg->dn)));
5755 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5756 DSDB_FLAG_NEXT_MODULE, ar->req);
5757 if (ret == LDB_SUCCESS) {
5758 talloc_free(tmp_ctx);
5763 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5764 talloc_free(tmp_ctx);
5765 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5766 ldb_dn_get_linearized(ar->search_msg->dn),
5767 ldb_dn_get_linearized(msg->dn),
5768 ldb_errstring(ldb_module_get_ctx(ar->module)));
5772 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5773 if (ret != LDB_SUCCESS) {
5774 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5775 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5776 ldb_errstring(ldb_module_get_ctx(ar->module)));
5777 return LDB_ERR_OPERATIONS_ERROR;
5780 * we have a conflict, and need to decide if we will keep the
5781 * new record or the old record
5784 conflict_dn = msg->dn;
5788 * We are on an RODC, or were a GC for this
5789 * partition, so we have to fail this until
5790 * someone who owns the partition sorts it
5793 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5794 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5795 " - We must fail the operation until a master for this partition resolves the conflict",
5796 ldb_dn_get_linearized(conflict_dn));
5797 ret = LDB_ERR_OPERATIONS_ERROR;
5802 * first we need the replPropertyMetaData attribute from the
5805 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5807 DSDB_FLAG_NEXT_MODULE |
5808 DSDB_SEARCH_SHOW_DELETED |
5809 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5810 if (ret != LDB_SUCCESS) {
5811 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5812 ldb_dn_get_linearized(conflict_dn)));
5816 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5817 if (omd_value == NULL) {
5818 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5819 ldb_dn_get_linearized(conflict_dn)));
5823 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5824 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5825 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5826 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5827 ldb_dn_get_linearized(conflict_dn)));
5831 rmd = ar->objs->objects[ar->index_current].meta_data;
5834 * we decide which is newer based on the RPMD on the name
5835 * attribute. See [MS-DRSR] ResolveNameConflict.
5837 * We expect omd_name to be present, as this is from a local
5838 * search, but while rmd_name should have been given to us by
5839 * the remote server, if it is missing we just prefer the
5841 * replmd_replPropertyMetaData1_new_should_be_taken()
5843 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5844 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5846 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5847 ldb_dn_get_linearized(conflict_dn)));
5852 * Should we preserve the current record, and so rename the
5853 * incoming record to be a conflict?
5855 rename_incoming_record =
5856 !replmd_replPropertyMetaData1_new_should_be_taken(
5857 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5858 omd_name, rmd_name);
5860 if (rename_incoming_record) {
5862 new_dn = replmd_conflict_dn(msg,
5863 ldb_module_get_ctx(ar->module),
5865 &ar->objs->objects[ar->index_current].object_guid);
5866 if (new_dn == NULL) {
5867 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5868 "Failed to form conflict DN for %s\n",
5869 ldb_dn_get_linearized(msg->dn));
5871 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5874 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5875 DSDB_FLAG_NEXT_MODULE, ar->req);
5876 if (ret != LDB_SUCCESS) {
5877 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5878 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5879 ldb_dn_get_linearized(conflict_dn),
5880 ldb_dn_get_linearized(ar->search_msg->dn),
5881 ldb_dn_get_linearized(new_dn),
5882 ldb_errstring(ldb_module_get_ctx(ar->module)));
5883 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5891 /* we are renaming the existing record */
5893 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5894 if (GUID_all_zero(&guid)) {
5895 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5896 ldb_dn_get_linearized(conflict_dn)));
5900 new_dn = replmd_conflict_dn(tmp_ctx,
5901 ldb_module_get_ctx(ar->module),
5902 conflict_dn, &guid);
5903 if (new_dn == NULL) {
5904 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5905 ldb_dn_get_linearized(conflict_dn)));
5909 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5910 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5912 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5913 DSDB_FLAG_OWN_MODULE, ar->req);
5914 if (ret != LDB_SUCCESS) {
5915 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5916 ldb_dn_get_linearized(conflict_dn),
5917 ldb_dn_get_linearized(new_dn),
5918 ldb_errstring(ldb_module_get_ctx(ar->module))));
5923 * now we need to ensure that the rename is seen as an
5924 * originating update. We do that with a modify.
5926 ret = replmd_name_modify(ar, ar->req, new_dn);
5927 if (ret != LDB_SUCCESS) {
5931 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5932 ldb_dn_get_linearized(ar->search_msg->dn),
5933 ldb_dn_get_linearized(msg->dn)));
5936 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5937 DSDB_FLAG_NEXT_MODULE, ar->req);
5938 if (ret != LDB_SUCCESS) {
5939 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5940 ldb_dn_get_linearized(ar->search_msg->dn),
5941 ldb_dn_get_linearized(msg->dn),
5942 ldb_errstring(ldb_module_get_ctx(ar->module))));
5946 talloc_free(tmp_ctx);
5950 * On failure make the caller get the error
5951 * This means replication will stop with an error,
5952 * but there is not much else we can do. In the
5953 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5956 if (ret == LDB_SUCCESS) {
5957 ret = LDB_ERR_OPERATIONS_ERROR;
5960 talloc_free(tmp_ctx);
5965 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5967 struct ldb_context *ldb;
5968 struct ldb_request *change_req;
5969 enum ndr_err_code ndr_err;
5970 struct ldb_message *msg;
5971 struct replPropertyMetaDataBlob *rmd;
5972 struct replPropertyMetaDataBlob omd;
5973 const struct ldb_val *omd_value;
5974 struct replPropertyMetaDataBlob nmd;
5975 struct ldb_val nmd_value;
5976 struct GUID remote_parent_guid;
5979 unsigned int removed_attrs = 0;
5981 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5982 bool isDeleted = false;
5983 bool local_isDeleted = false;
5984 bool remote_isDeleted = false;
5985 bool take_remote_isDeleted = false;
5986 bool sd_updated = false;
5987 bool renamed = false;
5988 bool is_schema_nc = false;
5990 const struct ldb_val *old_rdn, *new_rdn;
5991 struct replmd_private *replmd_private =
5992 talloc_get_type(ldb_module_get_private(ar->module),
5993 struct replmd_private);
5995 time_t t = time(NULL);
5996 unix_to_nt_time(&now, t);
5998 ldb = ldb_module_get_ctx(ar->module);
5999 msg = ar->objs->objects[ar->index_current].msg;
6001 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6003 rmd = ar->objs->objects[ar->index_current].meta_data;
6007 /* find existing meta data */
6008 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6010 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6011 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6012 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6013 nt_status = ndr_map_error2ntstatus(ndr_err);
6014 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6017 if (omd.version != 1) {
6018 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6023 struct GUID_txt_buf guid_txt;
6025 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6026 LDB_CHANGETYPE_MODIFY, msg);
6027 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6030 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6032 ndr_print_struct_string(s,
6033 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6034 "existing replPropertyMetaData",
6036 ndr_print_struct_string(s,
6037 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6038 "incoming replPropertyMetaData",
6041 } else if (DEBUGLVL(4)) {
6042 struct GUID_txt_buf guid_txt;
6044 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6045 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6047 ldb_dn_get_linearized(msg->dn)));
6050 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6051 "isDeleted", false);
6052 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6053 "isDeleted", false);
6056 * Fill in the remote_parent_guid with the GUID or an all-zero
6059 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6060 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6062 remote_parent_guid = GUID_zero();
6066 * To ensure we follow a complex rename chain around, we have
6067 * to confirm that the DN is the same (mostly to confirm the
6068 * RDN) and the parentGUID is the same.
6070 * This ensures we keep things under the correct parent, which
6071 * replmd_replicated_handle_rename() will do.
6074 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6075 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6079 * handle renames, even just by case that come in over
6080 * DRS. Changes in the parent DN don't hit us here,
6081 * because the search for a parent will clean up those
6084 * We also have already filtered out the case where
6085 * the peer has an older name to what we have (see
6086 * replmd_replicated_apply_search_callback())
6088 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6091 if (ret != LDB_SUCCESS) {
6092 ldb_debug(ldb, LDB_DEBUG_FATAL,
6093 "replmd_replicated_request rename %s => %s failed - %s\n",
6094 ldb_dn_get_linearized(ar->search_msg->dn),
6095 ldb_dn_get_linearized(msg->dn),
6096 ldb_errstring(ldb));
6097 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6100 if (renamed == true) {
6102 * Set the callback to one that will fix up the name
6103 * metadata on the new conflict DN
6105 callback = replmd_op_name_modify_callback;
6110 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6111 nmd.ctr.ctr1.array = talloc_array(ar,
6112 struct replPropertyMetaData1,
6113 nmd.ctr.ctr1.count);
6114 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6116 /* first copy the old meta data */
6117 for (i=0; i < omd.ctr.ctr1.count; i++) {
6118 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6123 /* now merge in the new meta data */
6124 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6127 for (j=0; j < ni; j++) {
6130 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6134 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6135 ar->objs->dsdb_repl_flags,
6136 &nmd.ctr.ctr1.array[j],
6137 &rmd->ctr.ctr1.array[i]);
6139 /* replace the entry */
6140 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6141 if (ar->seq_num == 0) {
6142 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6143 if (ret != LDB_SUCCESS) {
6144 return replmd_replicated_request_error(ar, ret);
6147 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6148 switch (nmd.ctr.ctr1.array[j].attid) {
6149 case DRSUAPI_ATTID_ntSecurityDescriptor:
6152 case DRSUAPI_ATTID_isDeleted:
6153 take_remote_isDeleted = true;
6162 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6163 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6164 msg->elements[i-removed_attrs].name,
6165 ldb_dn_get_linearized(msg->dn),
6166 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6169 /* we don't want to apply this change so remove the attribute */
6170 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6177 if (found) continue;
6179 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6180 if (ar->seq_num == 0) {
6181 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6182 if (ret != LDB_SUCCESS) {
6183 return replmd_replicated_request_error(ar, ret);
6186 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6187 switch (nmd.ctr.ctr1.array[ni].attid) {
6188 case DRSUAPI_ATTID_ntSecurityDescriptor:
6191 case DRSUAPI_ATTID_isDeleted:
6192 take_remote_isDeleted = true;
6201 * finally correct the size of the meta_data array
6203 nmd.ctr.ctr1.count = ni;
6205 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6206 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6209 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6210 &nmd, ar, now, is_schema_nc,
6212 if (ret != LDB_SUCCESS) {
6213 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6214 return replmd_replicated_request_error(ar, ret);
6218 * sort the new meta data array
6220 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6221 if (ret != LDB_SUCCESS) {
6222 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6227 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6230 * This also controls SD propagation below
6232 if (take_remote_isDeleted) {
6233 isDeleted = remote_isDeleted;
6235 isDeleted = local_isDeleted;
6238 ar->isDeleted = isDeleted;
6241 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6243 if (msg->num_elements == 0) {
6244 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6247 return replmd_replicated_apply_isDeleted(ar);
6250 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6251 ar->index_current, msg->num_elements);
6257 if (sd_updated && !isDeleted) {
6258 ret = dsdb_module_schedule_sd_propagation(ar->module,
6259 ar->objs->partition_dn,
6261 if (ret != LDB_SUCCESS) {
6262 return ldb_operr(ldb);
6266 /* create the meta data value */
6267 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6268 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6269 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6270 nt_status = ndr_map_error2ntstatus(ndr_err);
6271 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6275 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6276 * and replPopertyMetaData attributes
6278 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6279 if (ret != LDB_SUCCESS) {
6280 return replmd_replicated_request_error(ar, ret);
6282 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6283 if (ret != LDB_SUCCESS) {
6284 return replmd_replicated_request_error(ar, ret);
6286 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6287 if (ret != LDB_SUCCESS) {
6288 return replmd_replicated_request_error(ar, ret);
6291 replmd_ldb_message_sort(msg, ar->schema);
6293 /* we want to replace the old values */
6294 for (i=0; i < msg->num_elements; i++) {
6295 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6296 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6297 if (msg->elements[i].num_values == 0) {
6298 ldb_asprintf_errstring(ldb, __location__
6299 ": objectClass removed on %s, aborting replication\n",
6300 ldb_dn_get_linearized(msg->dn));
6301 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6307 struct GUID_txt_buf guid_txt;
6309 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6310 LDB_CHANGETYPE_MODIFY,
6312 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6313 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6317 } else if (DEBUGLVL(4)) {
6318 struct GUID_txt_buf guid_txt;
6320 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6321 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6323 ldb_dn_get_linearized(msg->dn)));
6326 ret = ldb_build_mod_req(&change_req,
6334 LDB_REQ_SET_LOCATION(change_req);
6335 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6337 /* current partition control needed by "repmd_op_callback" */
6338 ret = ldb_request_add_control(change_req,
6339 DSDB_CONTROL_CURRENT_PARTITION_OID,
6341 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6343 return ldb_next_request(ar->module, change_req);
6346 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6347 struct ldb_reply *ares)
6349 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6350 struct replmd_replicated_request);
6354 return ldb_module_done(ar->req, NULL, NULL,
6355 LDB_ERR_OPERATIONS_ERROR);
6357 if (ares->error != LDB_SUCCESS &&
6358 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6359 return ldb_module_done(ar->req, ares->controls,
6360 ares->response, ares->error);
6363 switch (ares->type) {
6364 case LDB_REPLY_ENTRY:
6365 ar->search_msg = talloc_steal(ar, ares->message);
6368 case LDB_REPLY_REFERRAL:
6369 /* we ignore referrals */
6372 case LDB_REPLY_DONE:
6374 struct replPropertyMetaData1 *md_remote;
6375 struct replPropertyMetaData1 *md_local;
6377 struct replPropertyMetaDataBlob omd;
6378 const struct ldb_val *omd_value;
6379 struct replPropertyMetaDataBlob *rmd;
6380 struct ldb_message *msg;
6382 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6383 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6386 * This is the ADD case, find the appropriate parent,
6387 * as this object doesn't exist locally:
6389 if (ar->search_msg == NULL) {
6390 ret = replmd_replicated_apply_search_for_parent(ar);
6391 if (ret != LDB_SUCCESS) {
6392 return ldb_module_done(ar->req, NULL, NULL, ret);
6399 * Otherwise, in the MERGE case, work out if we are
6400 * attempting a rename, and if so find the parent the
6401 * newly renamed object wants to belong under (which
6402 * may not be the parent in it's attached string DN
6404 rmd = ar->objs->objects[ar->index_current].meta_data;
6408 /* find existing meta data */
6409 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6411 enum ndr_err_code ndr_err;
6412 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6413 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6414 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6415 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6416 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6419 if (omd.version != 1) {
6420 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6424 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6426 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6427 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6428 && GUID_all_zero(&ar->local_parent_guid)) {
6429 DEBUG(0, ("Refusing to replicate new version of %s "
6430 "as local object has an all-zero parentGUID attribute, "
6431 "despite not being an NC root\n",
6432 ldb_dn_get_linearized(ar->search_msg->dn)));
6433 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6437 * now we need to check for double renames. We could have a
6438 * local rename pending which our replication partner hasn't
6439 * received yet. We choose which one wins by looking at the
6440 * attribute stamps on the two objects, the newer one wins.
6442 * This also simply applies the correct algorithms for
6443 * determining if a change was made to name at all, or
6444 * if the object has just been renamed under the same
6447 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6448 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6450 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6451 ldb_dn_get_linearized(ar->search_msg->dn)));
6452 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6456 * if there is no name attribute given then we have to assume the
6457 * object we've received has the older name
6459 if (replmd_replPropertyMetaData1_new_should_be_taken(
6460 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6461 md_local, md_remote)) {
6462 struct GUID_txt_buf p_guid_local;
6463 struct GUID_txt_buf p_guid_remote;
6464 msg = ar->objs->objects[ar->index_current].msg;
6466 /* Merge on the existing object, with rename */
6468 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6469 "as incoming object changing to %s under %s\n",
6470 ldb_dn_get_linearized(ar->search_msg->dn),
6471 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6472 ldb_dn_get_linearized(msg->dn),
6473 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6475 ret = replmd_replicated_apply_search_for_parent(ar);
6477 struct GUID_txt_buf p_guid_local;
6478 struct GUID_txt_buf p_guid_remote;
6479 msg = ar->objs->objects[ar->index_current].msg;
6482 * Merge on the existing object, force no
6483 * rename (code below just to explain why in
6487 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6488 ldb_dn_get_linearized(msg->dn)) == 0) {
6489 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6490 GUID_equal(&ar->local_parent_guid,
6491 ar->objs->objects[ar->index_current].parent_guid)
6493 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6494 "despite incoming object changing parent to %s\n",
6495 ldb_dn_get_linearized(ar->search_msg->dn),
6496 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6497 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6501 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6502 " and rejecting older rename to %s under %s\n",
6503 ldb_dn_get_linearized(ar->search_msg->dn),
6504 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6505 ldb_dn_get_linearized(msg->dn),
6506 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6510 * This assignment ensures that the strcmp()
6511 * and GUID_equal() calls in
6512 * replmd_replicated_apply_merge() avoids the
6515 ar->objs->objects[ar->index_current].parent_guid =
6516 &ar->local_parent_guid;
6518 msg->dn = ar->search_msg->dn;
6519 ret = replmd_replicated_apply_merge(ar);
6521 if (ret != LDB_SUCCESS) {
6522 return ldb_module_done(ar->req, NULL, NULL, ret);
6532 * Stores the linked attributes received in the replication chunk - these get
6533 * applied at the end of the transaction. We also check that each linked
6534 * attribute is valid, i.e. source and target objects are known.
6536 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6538 int ret = LDB_SUCCESS;
6540 struct ldb_module *module = ar->module;
6541 struct replmd_private *replmd_private =
6542 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6543 struct ldb_context *ldb;
6545 ldb = ldb_module_get_ctx(module);
6547 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6549 /* save away the linked attributes for the end of the transaction */
6550 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6551 struct la_entry *la_entry;
6553 if (replmd_private->la_ctx == NULL) {
6554 replmd_private->la_ctx = talloc_new(replmd_private);
6556 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6557 if (la_entry == NULL) {
6559 return LDB_ERR_OPERATIONS_ERROR;
6561 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6562 if (la_entry->la == NULL) {
6563 talloc_free(la_entry);
6565 return LDB_ERR_OPERATIONS_ERROR;
6567 *la_entry->la = ar->objs->linked_attributes[i];
6568 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6570 /* we need to steal the non-scalars so they stay
6571 around until the end of the transaction */
6572 talloc_steal(la_entry->la, la_entry->la->identifier);
6573 talloc_steal(la_entry->la, la_entry->la->value.blob);
6575 ret = replmd_verify_linked_attribute(ar, la_entry);
6577 if (ret != LDB_SUCCESS) {
6581 DLIST_ADD(replmd_private->la_list, la_entry);
6587 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6589 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6591 struct ldb_context *ldb;
6595 struct ldb_request *search_req;
6596 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6597 "parentGUID", "instanceType",
6598 "replPropertyMetaData", "nTSecurityDescriptor",
6599 "isDeleted", NULL };
6600 struct GUID_txt_buf guid_str_buf;
6602 if (ar->index_current >= ar->objs->num_objects) {
6605 * Now that we've applied all the objects, check the new linked
6606 * attributes and store them (we apply them in .prepare_commit)
6608 ret = replmd_store_linked_attributes(ar);
6610 if (ret != LDB_SUCCESS) {
6614 /* done applying objects, move on to the next stage */
6615 return replmd_replicated_uptodate_vector(ar);
6618 ldb = ldb_module_get_ctx(ar->module);
6619 ar->search_msg = NULL;
6620 ar->isDeleted = false;
6622 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6625 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6626 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6628 ret = ldb_build_search_req(&search_req,
6631 ar->objs->partition_dn,
6637 replmd_replicated_apply_search_callback,
6639 LDB_REQ_SET_LOCATION(search_req);
6641 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6643 if (ret != LDB_SUCCESS) {
6647 return ldb_next_request(ar->module, search_req);
6651 * This is essentially a wrapper for replmd_replicated_apply_next()
6653 * This is needed to ensure that both codepaths call this handler.
6655 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6657 struct ldb_dn *deleted_objects_dn;
6658 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6659 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6660 &deleted_objects_dn);
6661 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6663 * Do a delete here again, so that if there is
6664 * anything local that conflicts with this
6665 * object being deleted, it is removed. This
6666 * includes links. See MS-DRSR 4.1.10.6.9
6669 * If the object is already deleted, and there
6670 * is no more work required, it doesn't do
6674 /* This has been updated to point to the DN we eventually did the modify on */
6676 struct ldb_request *del_req;
6677 struct ldb_result *res;
6679 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6681 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6685 res = talloc_zero(tmp_ctx, struct ldb_result);
6687 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6688 talloc_free(tmp_ctx);
6692 /* Build a delete request, which hopefully will artually turn into nothing */
6693 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6697 ldb_modify_default_callback,
6699 LDB_REQ_SET_LOCATION(del_req);
6700 if (ret != LDB_SUCCESS) {
6701 talloc_free(tmp_ctx);
6706 * This is the guts of the call, call back
6707 * into our delete code, but setting the
6708 * re_delete flag so we delete anything that
6709 * shouldn't be there on a deleted or recycled
6712 ret = replmd_delete_internals(ar->module, del_req, true);
6713 if (ret == LDB_SUCCESS) {
6714 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6717 talloc_free(tmp_ctx);
6718 if (ret != LDB_SUCCESS) {
6723 ar->index_current++;
6724 return replmd_replicated_apply_next(ar);
6727 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6728 struct ldb_reply *ares)
6730 struct ldb_context *ldb;
6731 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6732 struct replmd_replicated_request);
6733 ldb = ldb_module_get_ctx(ar->module);
6736 return ldb_module_done(ar->req, NULL, NULL,
6737 LDB_ERR_OPERATIONS_ERROR);
6739 if (ares->error != LDB_SUCCESS) {
6740 return ldb_module_done(ar->req, ares->controls,
6741 ares->response, ares->error);
6744 if (ares->type != LDB_REPLY_DONE) {
6745 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6746 return ldb_module_done(ar->req, NULL, NULL,
6747 LDB_ERR_OPERATIONS_ERROR);
6752 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6755 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6757 struct ldb_context *ldb;
6758 struct ldb_request *change_req;
6759 enum ndr_err_code ndr_err;
6760 struct ldb_message *msg;
6761 struct replUpToDateVectorBlob ouv;
6762 const struct ldb_val *ouv_value;
6763 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6764 struct replUpToDateVectorBlob nuv;
6765 struct ldb_val nuv_value;
6766 struct ldb_message_element *nuv_el = NULL;
6767 struct ldb_message_element *orf_el = NULL;
6768 struct repsFromToBlob nrf;
6769 struct ldb_val *nrf_value = NULL;
6770 struct ldb_message_element *nrf_el = NULL;
6774 time_t t = time(NULL);
6777 uint32_t instanceType;
6779 ldb = ldb_module_get_ctx(ar->module);
6780 ruv = ar->objs->uptodateness_vector;
6786 unix_to_nt_time(&now, t);
6788 if (ar->search_msg == NULL) {
6789 /* this happens for a REPL_OBJ call where we are
6790 creating the target object by replicating it. The
6791 subdomain join code does this for the partition DN
6793 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6794 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6797 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6798 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6799 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6800 ldb_dn_get_linearized(ar->search_msg->dn)));
6801 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6805 * first create the new replUpToDateVector
6807 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6809 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6810 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6811 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6812 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6813 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6816 if (ouv.version != 2) {
6817 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6822 * the new uptodateness vector will at least
6823 * contain 1 entry, one for the source_dsa
6825 * plus optional values from our old vector and the one from the source_dsa
6827 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6828 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6829 nuv.ctr.ctr2.cursors = talloc_array(ar,
6830 struct drsuapi_DsReplicaCursor2,
6831 nuv.ctr.ctr2.count);
6832 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6834 /* first copy the old vector */
6835 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6836 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6840 /* merge in the source_dsa vector is available */
6841 for (i=0; (ruv && i < ruv->count); i++) {
6844 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6845 &ar->our_invocation_id)) {
6849 for (j=0; j < ni; j++) {
6850 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6851 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6857 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6858 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6863 if (found) continue;
6865 /* if it's not there yet, add it */
6866 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6871 * finally correct the size of the cursors array
6873 nuv.ctr.ctr2.count = ni;
6878 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6881 * create the change ldb_message
6883 msg = ldb_msg_new(ar);
6884 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6885 msg->dn = ar->search_msg->dn;
6887 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6888 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6889 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6890 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6891 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6893 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6894 if (ret != LDB_SUCCESS) {
6895 return replmd_replicated_request_error(ar, ret);
6897 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6900 * now create the new repsFrom value from the given repsFromTo1 structure
6904 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6905 nrf.ctr.ctr1.last_attempt = now;
6906 nrf.ctr.ctr1.last_success = now;
6907 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6910 * first see if we already have a repsFrom value for the current source dsa
6911 * if so we'll later replace this value
6913 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6915 for (i=0; i < orf_el->num_values; i++) {
6916 struct repsFromToBlob *trf;
6918 trf = talloc(ar, struct repsFromToBlob);
6919 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6921 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6922 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6923 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6924 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6925 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6928 if (trf->version != 1) {
6929 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6933 * we compare the source dsa objectGUID not the invocation_id
6934 * because we want only one repsFrom value per source dsa
6935 * and when the invocation_id of the source dsa has changed we don't need
6936 * the old repsFrom with the old invocation_id
6938 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6939 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6945 nrf_value = &orf_el->values[i];
6950 * copy over all old values to the new ldb_message
6952 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6953 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6958 * if we haven't found an old repsFrom value for the current source dsa
6959 * we'll add a new value
6962 struct ldb_val zero_value;
6963 ZERO_STRUCT(zero_value);
6964 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6965 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6967 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6970 /* we now fill the value which is already attached to ldb_message */
6971 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6973 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6974 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6975 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6976 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6980 * the ldb_message_element for the attribute, has all the old values and the new one
6981 * so we'll replace the whole attribute with all values
6983 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6985 if (CHECK_DEBUGLVL(4)) {
6986 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6987 LDB_CHANGETYPE_MODIFY,
6989 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6993 /* prepare the ldb_modify() request */
6994 ret = ldb_build_mod_req(&change_req,
7000 replmd_replicated_uptodate_modify_callback,
7002 LDB_REQ_SET_LOCATION(change_req);
7003 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7005 return ldb_next_request(ar->module, change_req);
7008 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7009 struct ldb_reply *ares)
7011 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7012 struct replmd_replicated_request);
7016 return ldb_module_done(ar->req, NULL, NULL,
7017 LDB_ERR_OPERATIONS_ERROR);
7019 if (ares->error != LDB_SUCCESS &&
7020 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7021 return ldb_module_done(ar->req, ares->controls,
7022 ares->response, ares->error);
7025 switch (ares->type) {
7026 case LDB_REPLY_ENTRY:
7027 ar->search_msg = talloc_steal(ar, ares->message);
7030 case LDB_REPLY_REFERRAL:
7031 /* we ignore referrals */
7034 case LDB_REPLY_DONE:
7035 ret = replmd_replicated_uptodate_modify(ar);
7036 if (ret != LDB_SUCCESS) {
7037 return ldb_module_done(ar->req, NULL, NULL, ret);
7046 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7048 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7049 struct replmd_private *replmd_private =
7050 talloc_get_type_abort(ldb_module_get_private(ar->module),
7051 struct replmd_private);
7053 static const char *attrs[] = {
7054 "replUpToDateVector",
7059 struct ldb_request *search_req;
7061 ar->search_msg = NULL;
7064 * Let the caller know that we did an originating updates
7066 ar->objs->originating_updates = replmd_private->originating_updates;
7068 ret = ldb_build_search_req(&search_req,
7071 ar->objs->partition_dn,
7077 replmd_replicated_uptodate_search_callback,
7079 LDB_REQ_SET_LOCATION(search_req);
7080 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7082 return ldb_next_request(ar->module, search_req);
7087 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7089 struct ldb_context *ldb;
7090 struct dsdb_extended_replicated_objects *objs;
7091 struct replmd_replicated_request *ar;
7092 struct ldb_control **ctrls;
7095 ldb = ldb_module_get_ctx(module);
7097 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7099 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7101 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7102 return LDB_ERR_PROTOCOL_ERROR;
7105 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7106 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7107 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7108 return LDB_ERR_PROTOCOL_ERROR;
7111 ar = replmd_ctx_init(module, req);
7113 return LDB_ERR_OPERATIONS_ERROR;
7115 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7116 ar->apply_mode = true;
7118 ar->schema = dsdb_get_schema(ldb, ar);
7120 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7122 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7123 return LDB_ERR_CONSTRAINT_VIOLATION;
7126 ctrls = req->controls;
7128 if (req->controls) {
7129 req->controls = talloc_memdup(ar, req->controls,
7130 talloc_get_size(req->controls));
7131 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7134 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7135 if (ret != LDB_SUCCESS) {
7139 /* If this change contained linked attributes in the body
7140 * (rather than in the links section) we need to update
7141 * backlinks in linked_attributes */
7142 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7143 if (ret != LDB_SUCCESS) {
7147 ar->controls = req->controls;
7148 req->controls = ctrls;
7150 return replmd_replicated_apply_next(ar);
7154 * Checks how to handle an missing target - either we need to fail the
7155 * replication and retry with GET_TGT, ignore the link and continue, or try to
7156 * add a partial link to an unknown target.
7158 static int replmd_allow_missing_target(struct ldb_module *module,
7159 TALLOC_CTX *mem_ctx,
7160 struct ldb_dn *target_dn,
7161 struct ldb_dn *source_dn,
7164 uint32_t dsdb_repl_flags,
7166 const char * missing_str)
7168 struct ldb_context *ldb = ldb_module_get_ctx(module);
7172 * we may not be able to resolve link targets properly when
7173 * dealing with subsets of objects, e.g. the source is a
7174 * critical object and the target isn't
7177 * When we implement Trusted Domains we need to consider
7178 * whether they get treated as an incomplete replica here or not
7180 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7183 * Ignore the link. We don't increase the highwater-mark in
7184 * the object subset cases, so subsequent replications should
7185 * resolve any missing links
7187 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7188 ldb_dn_get_linearized(target_dn),
7189 ldb_dn_get_linearized(source_dn)));
7190 *ignore_link = true;
7194 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7197 * target should already be up-to-date so there's no point in
7198 * retrying. This could be due to bad timing, or if a target
7199 * on a one-way link was deleted. We ignore the link rather
7200 * than failing the replication cycle completely
7202 *ignore_link = true;
7203 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7204 ldb_dn_get_linearized(target_dn), missing_str,
7205 ldb_dn_get_linearized(source_dn));
7209 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7213 if (is_in_same_nc) {
7214 /* fail the replication and retry with GET_TGT */
7215 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7217 ldb_dn_get_linearized(target_dn),
7218 GUID_string(mem_ctx, guid),
7219 ldb_dn_get_linearized(source_dn));
7220 return LDB_ERR_NO_SUCH_OBJECT;
7224 * The target of the cross-partition link is missing. Continue
7225 * and try to at least add the forward-link. This isn't great,
7226 * but a partial link can be fixed by dbcheck, so it's better
7227 * than dropping the link completely.
7229 *ignore_link = false;
7231 if (is_obj_commit) {
7234 * Only log this when we're actually committing the objects.
7235 * This avoids spurious logs, i.e. if we're just verifying the
7236 * received link during a join.
7238 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7239 missing_str, ldb_dn_get_linearized(target_dn),
7240 ldb_dn_get_linearized(source_dn));
7247 * Checks that the target object for a linked attribute exists.
7248 * @param guid returns the target object's GUID (is returned)if it exists)
7249 * @param ignore_link set to true if the linked attribute should be ignored
7250 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7252 static int replmd_check_target_exists(struct ldb_module *module,
7253 struct dsdb_dn *dsdb_dn,
7254 struct la_entry *la_entry,
7255 struct ldb_dn *source_dn,
7260 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7261 struct ldb_context *ldb = ldb_module_get_ctx(module);
7262 struct ldb_result *target_res;
7263 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7264 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7267 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7268 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7270 *ignore_link = false;
7271 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7273 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7276 * This strange behaviour (allowing a NULL/missing
7277 * GUID) originally comes from:
7279 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7280 * Author: Andrew Tridgell <tridge@samba.org>
7281 * Date: Mon Dec 21 21:21:55 2009 +1100
7283 * s4-drs: cope better with NULL GUIDS from DRS
7285 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7286 * need to match by DN if possible when seeing if we should update an
7289 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7291 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7293 DSDB_FLAG_NEXT_MODULE |
7294 DSDB_SEARCH_SHOW_RECYCLED |
7295 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7296 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7298 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7299 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7301 ldb_dn_get_linearized(dsdb_dn->dn),
7302 ldb_dn_get_linearized(source_dn));
7303 talloc_free(tmp_ctx);
7304 return LDB_ERR_OPERATIONS_ERROR;
7306 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7307 NULL, LDB_SCOPE_SUBTREE,
7309 DSDB_FLAG_NEXT_MODULE |
7310 DSDB_SEARCH_SHOW_RECYCLED |
7311 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7312 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7315 GUID_string(tmp_ctx, guid));
7318 if (ret != LDB_SUCCESS) {
7319 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7320 GUID_string(tmp_ctx, guid),
7321 ldb_errstring(ldb));
7322 talloc_free(tmp_ctx);
7326 if (target_res->count == 0) {
7329 * target object is unknown. Check whether to ignore the link,
7330 * fail the replication, or add a partial link
7332 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7333 source_dn, is_obj_commit, guid,
7334 la_entry->dsdb_repl_flags,
7335 ignore_link, "Unknown");
7337 } else if (target_res->count != 1) {
7338 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7339 GUID_string(tmp_ctx, guid));
7340 ret = LDB_ERR_OPERATIONS_ERROR;
7342 struct ldb_message *target_msg = target_res->msgs[0];
7344 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7346 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7347 replmd_deletion_state(module, target_msg,
7348 &target_deletion_state, NULL);
7351 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7352 * ProcessLinkValue(). Link updates should not be sent for
7353 * recycled and tombstone objects (deleting the links should
7354 * happen when we delete the object). This probably means our
7355 * copy of the target object isn't up to date.
7357 if (target_deletion_state >= OBJECT_RECYCLED) {
7360 * target object is deleted. Check whether to ignore the
7361 * link, fail the replication, or add a partial link
7363 ret = replmd_allow_missing_target(module, tmp_ctx,
7364 dsdb_dn->dn, source_dn,
7365 is_obj_commit, guid,
7366 la_entry->dsdb_repl_flags,
7367 ignore_link, "Deleted");
7371 talloc_free(tmp_ctx);
7376 * Extracts the key details about the source/target object for a
7377 * linked-attribute entry.
7378 * This returns the following details:
7379 * @param ret_attr the schema details for the linked attribute
7380 * @param source_msg the search result for the source object
7381 * @param target_dsdb_dn the unpacked DN info for the target object
7383 static int replmd_extract_la_entry_details(struct ldb_module *module,
7384 struct la_entry *la_entry,
7385 TALLOC_CTX *mem_ctx,
7386 const struct dsdb_attribute **ret_attr,
7387 struct ldb_message **source_msg,
7388 struct dsdb_dn **target_dsdb_dn)
7390 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7391 struct ldb_context *ldb = ldb_module_get_ctx(module);
7392 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7394 const struct dsdb_attribute *attr;
7396 struct ldb_result *res;
7397 const char *attrs[4];
7400 linked_attributes[0]:
7401 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7403 identifier: struct drsuapi_DsReplicaObjectIdentifier
7404 __ndr_size : 0x0000003a (58)
7405 __ndr_size_sid : 0x00000000 (0)
7406 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7408 __ndr_size_dn : 0x00000000 (0)
7410 attid : DRSUAPI_ATTID_member (0x1F)
7411 value: struct drsuapi_DsAttributeValue
7412 __ndr_size : 0x0000007e (126)
7414 blob : DATA_BLOB length=126
7415 flags : 0x00000001 (1)
7416 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7417 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7418 meta_data: struct drsuapi_DsReplicaMetaData
7419 version : 0x00000015 (21)
7420 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7421 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7422 originating_usn : 0x000000000001e19c (123292)
7424 (for cases where the link is to a normal DN)
7425 &target: struct drsuapi_DsReplicaObjectIdentifier3
7426 __ndr_size : 0x0000007e (126)
7427 __ndr_size_sid : 0x0000001c (28)
7428 guid : 7639e594-db75-4086-b0d4-67890ae46031
7429 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7430 __ndr_size_dn : 0x00000022 (34)
7431 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7434 /* find the attribute being modified */
7435 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7437 struct GUID_txt_buf guid_str;
7438 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7440 GUID_buf_string(&la->identifier->guid,
7442 return LDB_ERR_OPERATIONS_ERROR;
7446 * All attributes listed here must be dealt with in some way
7447 * by replmd_process_linked_attribute() otherwise in the case
7448 * of isDeleted: FALSE the modify will fail with:
7450 * Failed to apply linked attribute change 'attribute 'isDeleted':
7451 * invalid modify flags on
7452 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7455 * This is becaue isDeleted is a Boolean, so FALSE is a
7456 * legitimate value (set by Samba's deletetest.py)
7458 attrs[0] = attr->lDAPDisplayName;
7459 attrs[1] = "isDeleted";
7460 attrs[2] = "isRecycled";
7464 * get the existing message from the db for the object with
7465 * this GUID, returning attribute being modified. We will then
7466 * use this msg as the basis for a modify call
7468 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7469 DSDB_FLAG_NEXT_MODULE |
7470 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7471 DSDB_SEARCH_SHOW_RECYCLED |
7472 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7473 DSDB_SEARCH_REVEAL_INTERNALS,
7475 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7476 if (ret != LDB_SUCCESS) {
7479 if (res->count != 1) {
7480 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7481 GUID_string(mem_ctx, &la->identifier->guid));
7482 return LDB_ERR_NO_SUCH_OBJECT;
7485 *source_msg = res->msgs[0];
7487 /* the value blob for the attribute holds the target object DN */
7488 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7489 if (!W_ERROR_IS_OK(status)) {
7490 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7491 attr->lDAPDisplayName,
7492 ldb_dn_get_linearized(res->msgs[0]->dn),
7493 win_errstr(status));
7494 return LDB_ERR_OPERATIONS_ERROR;
7503 * Verifies the source and target objects are known for a linked attribute
7505 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7506 struct la_entry *la)
7508 int ret = LDB_SUCCESS;
7509 TALLOC_CTX *tmp_ctx = talloc_new(la);
7510 struct ldb_module *module = ar->module;
7511 struct ldb_message *src_msg;
7512 const struct dsdb_attribute *attr;
7513 struct dsdb_dn *tgt_dsdb_dn;
7514 struct GUID guid = GUID_zero();
7517 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7518 &src_msg, &tgt_dsdb_dn);
7521 * When we fail to find the source object, the error code we pass
7522 * back here is really important. It flags back to the callers to
7523 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7524 * never happen if we're replicating from a Samba DC, but it is
7525 * needed to talk to a Windows DC
7527 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7528 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7531 if (ret != LDB_SUCCESS) {
7532 talloc_free(tmp_ctx);
7537 * We can skip the target object checks if we're only syncing critical
7538 * objects, or we know the target is up-to-date. If either case, we
7539 * still continue even if the target doesn't exist
7541 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7542 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7544 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7545 src_msg->dn, false, &guid,
7550 * When we fail to find the target object, the error code we pass
7551 * back here is really important. It flags back to the callers to
7552 * retry this request with DRSUAPI_DRS_GET_TGT
7554 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7555 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7558 talloc_free(tmp_ctx);
7563 * Finds the current active Parsed-DN value for a single-valued linked
7564 * attribute, if one exists.
7565 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7566 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7569 static int replmd_get_active_singleval_link(struct ldb_module *module,
7570 TALLOC_CTX *mem_ctx,
7571 struct parsed_dn pdn_list[],
7573 const struct dsdb_attribute *attr,
7574 struct parsed_dn **ret_pdn)
7580 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7582 /* nothing to do for multi-valued linked attributes */
7586 for (i = 0; i < count; i++) {
7587 int ret = LDB_SUCCESS;
7588 struct parsed_dn *pdn = &pdn_list[i];
7590 /* skip any inactive links */
7591 if (dsdb_dn_is_deleted_val(pdn->v)) {
7595 /* we've found an active value for this attribute */
7598 if (pdn->dsdb_dn == NULL) {
7599 struct ldb_context *ldb = ldb_module_get_ctx(module);
7601 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7602 attr->syntax->ldap_oid);
7608 /* no active link found */
7613 * @returns true if the replication linked attribute info is newer than we
7614 * already have in our DB
7615 * @param pdn the existing linked attribute info in our DB
7616 * @param la the new linked attribute info received during replication
7618 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7619 struct drsuapi_DsReplicaLinkedAttribute *la)
7621 /* see if this update is newer than what we have already */
7622 struct GUID invocation_id = GUID_zero();
7623 uint32_t version = 0;
7624 NTTIME change_time = 0;
7628 /* no existing info so update is newer */
7632 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7633 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7634 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7636 return replmd_update_is_newer(&invocation_id,
7637 &la->meta_data.originating_invocation_id,
7639 la->meta_data.version,
7641 la->meta_data.originating_change_time);
7645 * Marks an existing linked attribute value as deleted in the DB
7646 * @param pdn the parsed-DN of the target-value to delete
7648 static int replmd_delete_link_value(struct ldb_module *module,
7649 struct replmd_private *replmd_private,
7650 TALLOC_CTX *mem_ctx,
7651 struct ldb_dn *src_obj_dn,
7652 const struct dsdb_schema *schema,
7653 const struct dsdb_attribute *attr,
7656 struct GUID *target_guid,
7657 struct dsdb_dn *target_dsdb_dn,
7658 struct ldb_val *output_val)
7660 struct ldb_context *ldb = ldb_module_get_ctx(module);
7663 const struct GUID *invocation_id = NULL;
7667 unix_to_nt_time(&now, t);
7669 invocation_id = samdb_ntds_invocation_id(ldb);
7670 if (invocation_id == NULL) {
7671 return LDB_ERR_OPERATIONS_ERROR;
7674 /* if the existing link is active, remove its backlink */
7678 * NOTE WELL: After this we will never (at runtime) be
7679 * able to find this forward link (for instant
7680 * removal) if/when the link target is deleted.
7682 * We have dbcheck rules to cover this and cope otherwise
7683 * by filtering at runtime (i.e. in the extended_dn module).
7685 ret = replmd_add_backlink(module, replmd_private, schema,
7686 src_obj_dn, target_guid, false,
7688 if (ret != LDB_SUCCESS) {
7693 /* mark the existing value as deleted */
7694 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7695 target_dsdb_dn, invocation_id, seq_num,
7696 seq_num, now, true);
7701 * Checks for a conflict in single-valued link attributes, and tries to
7702 * resolve the problem if possible.
7704 * Single-valued links should only ever have one active value. If we already
7705 * have an active link value, and during replication we receive an active link
7706 * value for a different target DN, then we need to resolve this inconsistency
7707 * and determine which value should be active. If the received info is better/
7708 * newer than the existing link attribute, then we need to set our existing
7709 * link as deleted. If the received info is worse/older, then we should continue
7710 * to add it, but set it as an inactive link.
7712 * Note that this is a corner-case that is unlikely to happen (but if it does
7713 * happen, we don't want it to break replication completely).
7715 * @param pdn_being_modified the parsed DN corresponding to the received link
7716 * target (note this is NULL if the link does not already exist in our DB)
7717 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7718 * any existing active or inactive values for the attribute in our DB.
7719 * @param dsdb_dn the target DN for the received link attribute
7720 * @param add_as_inactive gets set to true if the received link is worse than
7721 * the existing link - it should still be added, but as an inactive link.
7723 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7724 struct replmd_private *replmd_private,
7725 TALLOC_CTX *mem_ctx,
7726 struct ldb_dn *src_obj_dn,
7727 struct drsuapi_DsReplicaLinkedAttribute *la,
7728 struct dsdb_dn *dsdb_dn,
7729 struct parsed_dn *pdn_being_modified,
7730 struct parsed_dn *pdn_list,
7731 struct ldb_message_element *old_el,
7732 const struct dsdb_schema *schema,
7733 const struct dsdb_attribute *attr,
7735 bool *add_as_inactive)
7737 struct parsed_dn *active_pdn = NULL;
7738 bool update_is_newer = false;
7742 * check if there's a conflict for single-valued links, i.e. an active
7743 * linked attribute already exists, but it has a different target value
7745 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7746 old_el->num_values, attr,
7749 if (ret != LDB_SUCCESS) {
7754 * If no active value exists (or the received info is for the currently
7755 * active value), then no conflict exists
7757 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7761 DBG_WARNING("Link conflict for %s attribute on %s\n",
7762 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7764 /* Work out how to resolve the conflict based on which info is better */
7765 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7767 if (update_is_newer) {
7768 DBG_WARNING("Using received value %s, over existing target %s\n",
7769 ldb_dn_get_linearized(dsdb_dn->dn),
7770 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7773 * Delete our existing active link. The received info will then
7774 * be added (through normal link processing) as the active value
7776 ret = replmd_delete_link_value(module, replmd_private, old_el,
7777 src_obj_dn, schema, attr,
7778 seq_num, true, &active_pdn->guid,
7779 active_pdn->dsdb_dn,
7782 if (ret != LDB_SUCCESS) {
7786 DBG_WARNING("Using existing target %s, over received value %s\n",
7787 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7788 ldb_dn_get_linearized(dsdb_dn->dn));
7791 * we want to keep our existing active link and add the
7792 * received link as inactive
7794 *add_as_inactive = true;
7801 process one linked attribute structure
7803 static int replmd_process_linked_attribute(struct ldb_module *module,
7804 struct replmd_private *replmd_private,
7805 struct la_entry *la_entry,
7806 struct ldb_request *parent)
7808 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7809 struct ldb_context *ldb = ldb_module_get_ctx(module);
7810 struct ldb_message *msg;
7811 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7812 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7814 const struct dsdb_attribute *attr;
7815 struct dsdb_dn *dsdb_dn;
7816 uint64_t seq_num = 0;
7817 struct ldb_message_element *old_el;
7818 time_t t = time(NULL);
7819 struct parsed_dn *pdn_list, *pdn, *next;
7820 struct GUID guid = GUID_zero();
7821 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7823 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7824 struct dsdb_dn *old_dsdb_dn = NULL;
7825 struct ldb_val *val_to_update = NULL;
7826 bool add_as_inactive = false;
7829 * get the attribute being modified, the search result for the source object,
7830 * and the target object's DN details
7832 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7835 if (ret != LDB_SUCCESS) {
7836 talloc_free(tmp_ctx);
7841 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7842 * ProcessLinkValue, because link updates are not applied to
7843 * recycled and tombstone objects. We don't have to delete
7844 * any existing link, that should have happened when the
7845 * object deletion was replicated or initiated.
7847 * This needs isDeleted and isRecycled to be included as
7848 * attributes in the search and so in msg if set.
7850 replmd_deletion_state(module, msg, &deletion_state, NULL);
7852 if (deletion_state >= OBJECT_RECYCLED) {
7853 talloc_free(tmp_ctx);
7858 * Now that we know the deletion_state, remove the extra
7859 * attributes added for that purpose. We need to do this
7860 * otherwise in the case of isDeleted: FALSE the modify will
7863 * Failed to apply linked attribute change 'attribute 'isDeleted':
7864 * invalid modify flags on
7865 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7868 * This is becaue isDeleted is a Boolean, so FALSE is a
7869 * legitimate value (set by Samba's deletetest.py)
7872 ldb_msg_remove_attr(msg, "isDeleted");
7873 ldb_msg_remove_attr(msg, "isRecycled");
7875 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7876 if (old_el == NULL) {
7877 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7878 if (ret != LDB_SUCCESS) {
7879 ldb_module_oom(module);
7880 talloc_free(tmp_ctx);
7881 return LDB_ERR_OPERATIONS_ERROR;
7884 old_el->flags = LDB_FLAG_MOD_REPLACE;
7887 /* parse the existing links */
7888 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7889 attr->syntax->ldap_oid, parent);
7891 if (ret != LDB_SUCCESS) {
7892 talloc_free(tmp_ctx);
7896 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7897 true, &guid, &ignore_link);
7899 if (ret != LDB_SUCCESS) {
7900 talloc_free(tmp_ctx);
7905 * there are some cases where the target object doesn't exist, but it's
7906 * OK to ignore the linked attribute
7909 talloc_free(tmp_ctx);
7913 /* see if this link already exists */
7914 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7917 dsdb_dn->extra_part, 0,
7919 attr->syntax->ldap_oid,
7921 if (ret != LDB_SUCCESS) {
7922 talloc_free(tmp_ctx);
7926 if (!replmd_link_update_is_newer(pdn, la)) {
7927 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7928 old_el->name, ldb_dn_get_linearized(msg->dn),
7929 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7930 talloc_free(tmp_ctx);
7934 /* get a seq_num for this change */
7935 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7936 if (ret != LDB_SUCCESS) {
7937 talloc_free(tmp_ctx);
7942 * check for single-valued link conflicts, i.e. an active linked
7943 * attribute already exists, but it has a different target value
7946 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7947 tmp_ctx, msg->dn, la,
7948 dsdb_dn, pdn, pdn_list,
7949 old_el, schema, attr,
7952 if (ret != LDB_SUCCESS) {
7953 talloc_free(tmp_ctx);
7959 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7961 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7962 /* remove the existing backlink */
7963 ret = replmd_add_backlink(module, replmd_private,
7966 &pdn->guid, false, attr,
7968 if (ret != LDB_SUCCESS) {
7969 talloc_free(tmp_ctx);
7974 val_to_update = pdn->v;
7975 old_dsdb_dn = pdn->dsdb_dn;
7981 * We know where the new one needs to be, from the *next
7982 * pointer into pdn_list.
7985 offset = old_el->num_values;
7987 if (next->dsdb_dn == NULL) {
7988 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7989 attr->syntax->ldap_oid);
7990 if (ret != LDB_SUCCESS) {
7994 offset = next - pdn_list;
7995 if (offset > old_el->num_values) {
7996 talloc_free(tmp_ctx);
7997 return LDB_ERR_OPERATIONS_ERROR;
8001 old_el->values = talloc_realloc(msg->elements, old_el->values,
8002 struct ldb_val, old_el->num_values+1);
8003 if (!old_el->values) {
8004 ldb_module_oom(module);
8005 return LDB_ERR_OPERATIONS_ERROR;
8008 if (offset != old_el->num_values) {
8009 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8010 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8013 old_el->num_values++;
8015 val_to_update = &old_el->values[offset];
8019 /* set the link attribute's value to the info that was received */
8020 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8021 &la->meta_data.originating_invocation_id,
8022 la->meta_data.originating_usn, seq_num,
8023 la->meta_data.originating_change_time,
8024 la->meta_data.version,
8026 if (ret != LDB_SUCCESS) {
8027 talloc_free(tmp_ctx);
8031 if (add_as_inactive) {
8033 /* Set the new link as inactive/deleted to avoid conflicts */
8034 ret = replmd_delete_link_value(module, replmd_private, old_el,
8035 msg->dn, schema, attr, seq_num,
8036 false, &guid, dsdb_dn,
8039 if (ret != LDB_SUCCESS) {
8040 talloc_free(tmp_ctx);
8044 } else if (active) {
8046 /* if the new link is active, then add the new backlink */
8047 ret = replmd_add_backlink(module, replmd_private,
8052 if (ret != LDB_SUCCESS) {
8053 talloc_free(tmp_ctx);
8058 /* we only change whenChanged and uSNChanged if the seq_num
8060 ret = add_time_element(msg, "whenChanged", t);
8061 if (ret != LDB_SUCCESS) {
8062 talloc_free(tmp_ctx);
8067 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8068 if (ret != LDB_SUCCESS) {
8069 talloc_free(tmp_ctx);
8074 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8075 if (old_el == NULL) {
8076 talloc_free(tmp_ctx);
8077 return ldb_operr(ldb);
8080 ret = dsdb_check_single_valued_link(attr, old_el);
8081 if (ret != LDB_SUCCESS) {
8082 talloc_free(tmp_ctx);
8086 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8088 ret = linked_attr_modify(module, msg, parent);
8089 if (ret != LDB_SUCCESS) {
8090 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
8092 ldb_ldif_message_redacted_string(ldb,
8094 LDB_CHANGETYPE_MODIFY,
8096 talloc_free(tmp_ctx);
8100 talloc_free(tmp_ctx);
8105 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8107 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8108 return replmd_extended_replicated_objects(module, req);
8111 return ldb_next_request(module, req);
8116 we hook into the transaction operations to allow us to
8117 perform the linked attribute updates at the end of the whole
8118 transaction. This allows a forward linked attribute to be created
8119 before the object is created. During a vampire, w2k8 sends us linked
8120 attributes before the objects they are part of.
8122 static int replmd_start_transaction(struct ldb_module *module)
8124 /* create our private structure for this transaction */
8125 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8126 struct replmd_private);
8127 replmd_txn_cleanup(replmd_private);
8129 /* free any leftover mod_usn records from cancelled
8131 while (replmd_private->ncs) {
8132 struct nc_entry *e = replmd_private->ncs;
8133 DLIST_REMOVE(replmd_private->ncs, e);
8137 replmd_private->originating_updates = false;
8139 return ldb_next_start_trans(module);
8143 on prepare commit we loop over our queued la_context structures and
8146 static int replmd_prepare_commit(struct ldb_module *module)
8148 struct replmd_private *replmd_private =
8149 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8150 struct la_entry *la, *prev;
8154 * Walk the list of linked attributes from DRS replication.
8156 * We walk backwards, to do the first entry first, as we
8157 * added the entries with DLIST_ADD() which puts them at the
8160 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
8161 prev = DLIST_PREV(la);
8162 DLIST_REMOVE(replmd_private->la_list, la);
8163 ret = replmd_process_linked_attribute(module, replmd_private,
8165 if (ret != LDB_SUCCESS) {
8166 replmd_txn_cleanup(replmd_private);
8171 replmd_txn_cleanup(replmd_private);
8173 /* possibly change @REPLCHANGED */
8174 ret = replmd_notify_store(module, NULL);
8175 if (ret != LDB_SUCCESS) {
8179 return ldb_next_prepare_commit(module);
8182 static int replmd_del_transaction(struct ldb_module *module)
8184 struct replmd_private *replmd_private =
8185 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8186 replmd_txn_cleanup(replmd_private);
8188 return ldb_next_del_trans(module);
8192 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8193 .name = "repl_meta_data",
8194 .init_context = replmd_init,
8196 .modify = replmd_modify,
8197 .rename = replmd_rename,
8198 .del = replmd_delete,
8199 .extended = replmd_extended,
8200 .start_transaction = replmd_start_transaction,
8201 .prepare_commit = replmd_prepare_commit,
8202 .del_transaction = replmd_del_transaction,
8205 int ldb_repl_meta_data_module_init(const char *version)
8207 LDB_MODULE_CHECK_VERSION(version);
8208 return ldb_register_module(&ldb_repl_meta_data_module_ops);