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_group *la_list;
70 struct nc_entry *prev, *next;
73 uint64_t mod_usn_urgent;
75 struct ldb_dn *schema_dn;
76 bool originating_updates;
79 uint32_t num_processed;
80 bool recyclebin_enabled;
81 bool recyclebin_state_known;
85 * groups link attributes together by source-object and attribute-ID,
86 * to improve processing efficiency (i.e. for 'member' attribute, which
87 * could have 100s or 1000s of links).
88 * Note this grouping is best effort - the same source object could still
89 * correspond to several la_groups (a lot depends on the order DRS sends
90 * the links in). The groups currently don't span replication chunks (which
91 * caps the size to ~1500 links by default).
94 struct la_group *next, *prev;
95 struct la_entry *la_entries;
99 struct la_entry *next, *prev;
100 struct drsuapi_DsReplicaLinkedAttribute *la;
101 uint32_t dsdb_repl_flags;
104 struct replmd_replicated_request {
105 struct ldb_module *module;
106 struct ldb_request *req;
108 const struct dsdb_schema *schema;
109 struct GUID our_invocation_id;
111 /* the controls we pass down */
112 struct ldb_control **controls;
115 * Backlinks for the replmd_add() case (we want to create
116 * backlinks after creating the user, but before the end of
119 struct la_backlink *la_backlinks;
121 /* details for the mode where we apply a bunch of inbound replication meessages */
123 uint32_t index_current;
124 struct dsdb_extended_replicated_objects *objs;
126 struct ldb_message *search_msg;
127 struct GUID local_parent_guid;
138 * the result of replmd_process_linked_attribute(): either there was no change
139 * (update was ignored), a new link was added (either inactive or active), or
140 * an existing link was modified (active/inactive status may have changed).
145 LINK_CHANGE_MODIFIED,
146 } replmd_link_changed;
148 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
149 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
150 static int replmd_check_upgrade_links(struct ldb_context *ldb,
151 struct parsed_dn *dns, uint32_t count,
152 struct ldb_message_element *el,
153 const char *ldap_oid);
154 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
156 struct la_entry *la_entry,
157 struct ldb_dn *src_dn,
158 const struct dsdb_attribute *attr);
159 static int replmd_get_la_entry_source(struct ldb_module *module,
160 struct la_entry *la_entry,
162 const struct dsdb_attribute **ret_attr,
163 struct ldb_message **source_msg);
164 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
165 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
166 uint64_t usn, uint64_t local_usn, NTTIME nttime,
167 uint32_t version, bool deleted);
169 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
170 struct ldb_context *ldb,
172 const char *rdn_name,
173 const struct ldb_val *rdn_value,
176 enum urgent_situation {
177 REPL_URGENT_ON_CREATE = 1,
178 REPL_URGENT_ON_UPDATE = 2,
179 REPL_URGENT_ON_DELETE = 4
182 enum deletion_state {
183 OBJECT_NOT_DELETED=1,
190 static bool replmd_recyclebin_enabled(struct ldb_module *module)
192 bool enabled = false;
193 struct replmd_private *replmd_private =
194 talloc_get_type_abort(ldb_module_get_private(module),
195 struct replmd_private);
198 * only lookup the recycle-bin state once per replication, then cache
199 * the result. This can save us 1000s of DB searches
201 if (!replmd_private->recyclebin_state_known) {
202 int ret = dsdb_recyclebin_enabled(module, &enabled);
203 if (ret != LDB_SUCCESS) {
207 replmd_private->recyclebin_enabled = enabled;
208 replmd_private->recyclebin_state_known = true;
211 return replmd_private->recyclebin_enabled;
214 static void replmd_deletion_state(struct ldb_module *module,
215 const struct ldb_message *msg,
216 enum deletion_state *current_state,
217 enum deletion_state *next_state)
219 bool enabled = false;
222 *current_state = OBJECT_REMOVED;
223 if (next_state != NULL) {
224 *next_state = OBJECT_REMOVED;
229 enabled = replmd_recyclebin_enabled(module);
231 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
233 *current_state = OBJECT_TOMBSTONE;
234 if (next_state != NULL) {
235 *next_state = OBJECT_REMOVED;
240 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
241 *current_state = OBJECT_RECYCLED;
242 if (next_state != NULL) {
243 *next_state = OBJECT_REMOVED;
248 *current_state = OBJECT_DELETED;
249 if (next_state != NULL) {
250 *next_state = OBJECT_RECYCLED;
255 *current_state = OBJECT_NOT_DELETED;
256 if (next_state == NULL) {
261 *next_state = OBJECT_DELETED;
263 *next_state = OBJECT_TOMBSTONE;
267 static const struct {
268 const char *update_name;
269 enum urgent_situation repl_situation;
270 } urgent_objects[] = {
271 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
272 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
273 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
274 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
275 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
276 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
280 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
281 static const char *urgent_attrs[] = {
284 "userAccountControl",
289 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
290 enum urgent_situation situation)
293 for (i=0; urgent_objects[i].update_name; i++) {
295 if ((situation & urgent_objects[i].repl_situation) == 0) {
299 for (j=0; j<objectclass_el->num_values; j++) {
300 const struct ldb_val *v = &objectclass_el->values[j];
301 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
309 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
311 if (ldb_attr_in_list(urgent_attrs, el->name)) {
317 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
320 initialise the module
321 allocate the private structure and build the list
322 of partition DNs for use by replmd_notify()
324 static int replmd_init(struct ldb_module *module)
326 struct replmd_private *replmd_private;
327 struct ldb_context *ldb = ldb_module_get_ctx(module);
328 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
329 struct ldb_dn *samba_dsdb_dn;
330 struct ldb_result *res;
332 TALLOC_CTX *frame = talloc_stackframe();
333 replmd_private = talloc_zero(module, struct replmd_private);
334 if (replmd_private == NULL) {
337 return LDB_ERR_OPERATIONS_ERROR;
339 ldb_module_set_private(module, replmd_private);
341 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
343 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
344 if (!samba_dsdb_dn) {
349 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
350 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
351 if (ret == LDB_SUCCESS) {
352 replmd_private->sorted_links
353 = ldb_msg_check_string_attribute(res->msgs[0],
354 SAMBA_COMPATIBLE_FEATURES_ATTR,
355 SAMBA_SORTED_LINKS_FEATURE);
359 return ldb_next_init(module);
363 cleanup our per-transaction contexts
365 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
367 talloc_free(replmd_private->la_ctx);
368 replmd_private->la_list = NULL;
369 replmd_private->la_ctx = NULL;
370 replmd_private->recyclebin_state_known = false;
375 struct la_backlink *next, *prev;
376 const char *attr_name;
377 struct ldb_dn *forward_dn;
378 struct GUID target_guid;
383 a ldb_modify request operating on modules below the
386 static int linked_attr_modify(struct ldb_module *module,
387 const struct ldb_message *message,
388 struct ldb_request *parent)
390 struct ldb_request *mod_req;
392 struct ldb_context *ldb = ldb_module_get_ctx(module);
393 TALLOC_CTX *tmp_ctx = talloc_new(module);
394 struct ldb_result *res;
396 res = talloc_zero(tmp_ctx, struct ldb_result);
398 talloc_free(tmp_ctx);
399 return ldb_oom(ldb_module_get_ctx(module));
402 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
406 ldb_modify_default_callback,
408 LDB_REQ_SET_LOCATION(mod_req);
409 if (ret != LDB_SUCCESS) {
410 talloc_free(tmp_ctx);
414 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
416 if (ret != LDB_SUCCESS) {
420 /* Run the new request */
421 ret = ldb_next_request(module, mod_req);
423 if (ret == LDB_SUCCESS) {
424 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
427 talloc_free(tmp_ctx);
432 process a backlinks we accumulated during a transaction, adding and
433 deleting the backlinks from the target objects
435 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
437 struct ldb_dn *target_dn, *source_dn;
439 struct ldb_context *ldb = ldb_module_get_ctx(module);
440 struct ldb_message *msg;
441 TALLOC_CTX *frame = talloc_stackframe();
447 - construct ldb_message
448 - either an add or a delete
450 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
451 if (ret != LDB_SUCCESS) {
452 struct GUID_txt_buf guid_str;
453 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
454 GUID_buf_string(&bl->target_guid, &guid_str));
455 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
460 msg = ldb_msg_new(frame);
462 ldb_module_oom(module);
464 return LDB_ERR_OPERATIONS_ERROR;
467 source_dn = ldb_dn_copy(frame, bl->forward_dn);
469 ldb_module_oom(module);
471 return LDB_ERR_OPERATIONS_ERROR;
473 /* Filter down to the attributes we want in the backlink */
474 const char *accept[] = { "GUID", "SID", NULL };
475 ldb_dn_extended_filter(source_dn, accept);
478 /* construct a ldb_message for adding/deleting the backlink */
480 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
482 ldb_module_oom(module);
484 return LDB_ERR_OPERATIONS_ERROR;
486 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
487 if (ret != LDB_SUCCESS) {
491 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
493 /* a backlink should never be single valued. Unfortunately the
494 exchange schema has a attribute
495 msExchBridgeheadedLocalConnectorsDNBL which is single
496 valued and a backlink. We need to cope with that by
497 ignoring the single value flag */
498 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
500 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
501 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
502 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
503 cope with possible corruption where the backlink has
504 already been removed */
505 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
506 ldb_dn_get_linearized(target_dn),
507 ldb_dn_get_linearized(source_dn),
508 ldb_errstring(ldb)));
510 } else if (ret != LDB_SUCCESS) {
511 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
512 bl->active?"add":"remove",
513 ldb_dn_get_linearized(source_dn),
514 ldb_dn_get_linearized(target_dn),
524 add a backlink to the list of backlinks to add/delete in the prepare
527 forward_dn is stolen onto the defereed context
529 static int replmd_defer_add_backlink(struct ldb_module *module,
530 struct replmd_private *replmd_private,
531 const struct dsdb_schema *schema,
532 struct replmd_replicated_request *ac,
533 struct ldb_dn *forward_dn,
534 struct GUID *target_guid, bool active,
535 const struct dsdb_attribute *schema_attr,
536 struct ldb_request *parent)
538 const struct dsdb_attribute *target_attr;
539 struct la_backlink *bl;
541 bl = talloc(ac, struct la_backlink);
543 ldb_module_oom(module);
544 return LDB_ERR_OPERATIONS_ERROR;
547 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
550 * windows 2003 has a broken schema where the
551 * definition of msDS-IsDomainFor is missing (which is
552 * supposed to be the backlink of the
553 * msDS-HasDomainNCs attribute
558 bl->attr_name = target_attr->lDAPDisplayName;
559 bl->forward_dn = talloc_steal(bl, forward_dn);
560 bl->target_guid = *target_guid;
563 DLIST_ADD(ac->la_backlinks, bl);
569 add a backlink to the list of backlinks to add/delete in the prepare
572 static int replmd_add_backlink(struct ldb_module *module,
573 struct replmd_private *replmd_private,
574 const struct dsdb_schema *schema,
575 struct ldb_dn *forward_dn,
576 struct GUID *target_guid, bool active,
577 const struct dsdb_attribute *schema_attr,
578 struct ldb_request *parent)
580 const struct dsdb_attribute *target_attr;
581 struct la_backlink bl;
584 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
587 * windows 2003 has a broken schema where the
588 * definition of msDS-IsDomainFor is missing (which is
589 * supposed to be the backlink of the
590 * msDS-HasDomainNCs attribute
595 bl.attr_name = target_attr->lDAPDisplayName;
596 bl.forward_dn = forward_dn;
597 bl.target_guid = *target_guid;
600 ret = replmd_process_backlink(module, &bl, parent);
606 * Callback for most write operations in this module:
608 * notify the repl task that a object has changed. The notifies are
609 * gathered up in the replmd_private structure then written to the
610 * @REPLCHANGED object in each partition during the prepare_commit
612 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
615 struct replmd_replicated_request *ac =
616 talloc_get_type_abort(req->context, struct replmd_replicated_request);
617 struct replmd_private *replmd_private =
618 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
619 struct nc_entry *modified_partition;
620 struct ldb_control *partition_ctrl;
621 const struct dsdb_control_current_partition *partition;
623 struct ldb_control **controls;
625 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
627 controls = ares->controls;
628 if (ldb_request_get_control(ac->req,
629 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
631 * Remove the current partition control from what we pass up
632 * the chain if it hasn't been requested manually.
634 controls = ldb_controls_except_specified(ares->controls, ares,
638 if (ares->error != LDB_SUCCESS) {
639 struct GUID_txt_buf guid_txt;
640 struct ldb_message *msg = NULL;
643 if (ac->apply_mode == false) {
644 DBG_NOTICE("Originating update failure. Error is: %s\n",
645 ldb_strerror(ares->error));
646 return ldb_module_done(ac->req, controls,
647 ares->response, ares->error);
650 msg = ac->objs->objects[ac->index_current].msg;
652 * Set at DBG_NOTICE as once these start to happe, they
653 * will happen a lot until resolved, due to repeated
654 * replication. The caller will probably print the
655 * ldb error string anyway.
657 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
658 ldb_dn_get_linearized(msg->dn),
659 ldb_strerror(ares->error));
661 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
666 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
667 ac->search_msg == NULL ? "ADD" : "MODIFY",
668 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
672 return ldb_module_done(ac->req, controls,
673 ares->response, ares->error);
676 if (ares->type != LDB_REPLY_DONE) {
677 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
678 return ldb_module_done(ac->req, NULL,
679 NULL, LDB_ERR_OPERATIONS_ERROR);
682 if (ac->apply_mode == false) {
683 struct la_backlink *bl;
685 * process our backlink list after an replmd_add(),
686 * creating and deleting backlinks as necessary (this
687 * code is sync). The other cases are handled inline
690 for (bl=ac->la_backlinks; bl; bl=bl->next) {
691 ret = replmd_process_backlink(ac->module, bl, ac->req);
692 if (ret != LDB_SUCCESS) {
693 return ldb_module_done(ac->req, NULL,
699 if (!partition_ctrl) {
700 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
701 return ldb_module_done(ac->req, NULL,
702 NULL, LDB_ERR_OPERATIONS_ERROR);
705 partition = talloc_get_type_abort(partition_ctrl->data,
706 struct dsdb_control_current_partition);
708 if (ac->seq_num > 0) {
709 for (modified_partition = replmd_private->ncs; modified_partition;
710 modified_partition = modified_partition->next) {
711 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
716 if (modified_partition == NULL) {
717 modified_partition = talloc_zero(replmd_private, struct nc_entry);
718 if (!modified_partition) {
719 ldb_oom(ldb_module_get_ctx(ac->module));
720 return ldb_module_done(ac->req, NULL,
721 NULL, LDB_ERR_OPERATIONS_ERROR);
723 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
724 if (!modified_partition->dn) {
725 ldb_oom(ldb_module_get_ctx(ac->module));
726 return ldb_module_done(ac->req, NULL,
727 NULL, LDB_ERR_OPERATIONS_ERROR);
729 DLIST_ADD(replmd_private->ncs, modified_partition);
732 if (ac->seq_num > modified_partition->mod_usn) {
733 modified_partition->mod_usn = ac->seq_num;
735 modified_partition->mod_usn_urgent = ac->seq_num;
738 if (!ac->apply_mode) {
739 replmd_private->originating_updates = true;
743 if (ac->apply_mode) {
744 ret = replmd_replicated_apply_isDeleted(ac);
745 if (ret != LDB_SUCCESS) {
746 return ldb_module_done(ac->req, NULL, NULL, ret);
750 /* free the partition control container here, for the
751 * common path. Other cases will have it cleaned up
752 * eventually with the ares */
753 talloc_free(partition_ctrl);
754 return ldb_module_done(ac->req, controls,
755 ares->response, LDB_SUCCESS);
761 * update a @REPLCHANGED record in each partition if there have been
762 * any writes of replicated data in the partition
764 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
766 struct replmd_private *replmd_private =
767 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
769 while (replmd_private->ncs) {
771 struct nc_entry *modified_partition = replmd_private->ncs;
773 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
774 modified_partition->mod_usn,
775 modified_partition->mod_usn_urgent, parent);
776 if (ret != LDB_SUCCESS) {
777 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
778 ldb_dn_get_linearized(modified_partition->dn)));
782 if (ldb_dn_compare(modified_partition->dn,
783 replmd_private->schema_dn) == 0) {
784 struct ldb_result *ext_res;
785 ret = dsdb_module_extended(module,
786 replmd_private->schema_dn,
788 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
790 DSDB_FLAG_NEXT_MODULE,
792 if (ret != LDB_SUCCESS) {
795 talloc_free(ext_res);
798 DLIST_REMOVE(replmd_private->ncs, modified_partition);
799 talloc_free(modified_partition);
807 created a replmd_replicated_request context
809 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
810 struct ldb_request *req)
812 struct ldb_context *ldb;
813 struct replmd_replicated_request *ac;
814 const struct GUID *our_invocation_id;
816 ldb = ldb_module_get_ctx(module);
818 ac = talloc_zero(req, struct replmd_replicated_request);
827 ac->schema = dsdb_get_schema(ldb, ac);
829 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
830 "replmd_modify: no dsdb_schema loaded");
831 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
836 /* get our invocationId */
837 our_invocation_id = samdb_ntds_invocation_id(ldb);
838 if (!our_invocation_id) {
839 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
840 "replmd_add: unable to find invocationId\n");
844 ac->our_invocation_id = *our_invocation_id;
850 add a time element to a record
852 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
854 struct ldb_message_element *el;
858 if (ldb_msg_find_element(msg, attr) != NULL) {
862 s = ldb_timestring(msg, t);
864 return LDB_ERR_OPERATIONS_ERROR;
867 ret = ldb_msg_add_string(msg, attr, s);
868 if (ret != LDB_SUCCESS) {
872 el = ldb_msg_find_element(msg, attr);
873 /* always set as replace. This works because on add ops, the flag
875 el->flags = LDB_FLAG_MOD_REPLACE;
881 add a uint64_t element to a record
883 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
884 const char *attr, uint64_t v)
886 struct ldb_message_element *el;
889 if (ldb_msg_find_element(msg, attr) != NULL) {
893 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
894 if (ret != LDB_SUCCESS) {
898 el = ldb_msg_find_element(msg, attr);
899 /* always set as replace. This works because on add ops, the flag
901 el->flags = LDB_FLAG_MOD_REPLACE;
906 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
907 const struct replPropertyMetaData1 *m2,
908 const uint32_t *rdn_attid)
911 * This assignment seems inoccous, but it is critical for the
912 * system, as we need to do the comparisons as a unsigned
913 * quantity, not signed (enums are signed integers)
915 uint32_t attid_1 = m1->attid;
916 uint32_t attid_2 = m2->attid;
918 if (attid_1 == attid_2) {
923 * See above regarding this being an unsigned comparison.
924 * Otherwise when the high bit is set on non-standard
925 * attributes, they would end up first, before objectClass
928 return attid_1 > attid_2 ? 1 : -1;
931 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
932 struct replPropertyMetaDataCtr1 *ctr1,
935 if (ctr1->count == 0) {
936 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
937 "No elements found in replPropertyMetaData for %s!\n",
938 ldb_dn_get_linearized(dn));
939 return LDB_ERR_CONSTRAINT_VIOLATION;
942 /* the objectClass attribute is value 0x00000000, so must be first */
943 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
944 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
945 "No objectClass found in replPropertyMetaData for %s!\n",
946 ldb_dn_get_linearized(dn));
947 return LDB_ERR_OBJECT_CLASS_VIOLATION;
953 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
954 struct replPropertyMetaDataCtr1 *ctr1,
957 /* Note this is O(n^2) for the almost-sorted case, which this is */
958 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
959 replmd_replPropertyMetaData1_attid_sort);
960 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
963 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
964 const struct ldb_message_element *e2,
965 const struct dsdb_schema *schema)
967 const struct dsdb_attribute *a1;
968 const struct dsdb_attribute *a2;
971 * TODO: make this faster by caching the dsdb_attribute pointer
972 * on the ldb_messag_element
975 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
976 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
979 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
983 return strcasecmp(e1->name, e2->name);
985 if (a1->attributeID_id == a2->attributeID_id) {
988 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
991 static void replmd_ldb_message_sort(struct ldb_message *msg,
992 const struct dsdb_schema *schema)
994 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
997 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
998 const struct GUID *invocation_id,
999 uint64_t local_usn, NTTIME nttime);
1001 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
1003 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1004 struct ldb_message_element *el, struct parsed_dn **pdn,
1005 const char *ldap_oid, struct ldb_request *parent);
1007 static int check_parsed_dn_duplicates(struct ldb_module *module,
1008 struct ldb_message_element *el,
1009 struct parsed_dn *pdn);
1012 fix up linked attributes in replmd_add.
1013 This involves setting up the right meta-data in extended DN
1014 components, and creating backlinks to the object
1016 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1017 struct replmd_private *replmd_private,
1018 struct ldb_message_element *el,
1019 struct replmd_replicated_request *ac,
1021 struct ldb_dn *forward_dn,
1022 const struct dsdb_attribute *sa,
1023 struct ldb_request *parent)
1026 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1027 struct ldb_context *ldb = ldb_module_get_ctx(module);
1028 struct parsed_dn *pdn;
1029 /* We will take a reference to the schema in replmd_add_backlink */
1030 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
1031 struct ldb_val *new_values = NULL;
1034 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1035 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1037 ldb_asprintf_errstring(ldb,
1038 "Attribute %s is single valued but "
1039 "more than one value has been supplied",
1041 talloc_free(tmp_ctx);
1042 return LDB_ERR_CONSTRAINT_VIOLATION;
1045 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1046 sa->syntax->ldap_oid, parent);
1047 if (ret != LDB_SUCCESS) {
1048 talloc_free(tmp_ctx);
1052 ret = check_parsed_dn_duplicates(module, el, pdn);
1053 if (ret != LDB_SUCCESS) {
1054 talloc_free(tmp_ctx);
1058 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1059 if (new_values == NULL) {
1060 ldb_module_oom(module);
1061 talloc_free(tmp_ctx);
1062 return LDB_ERR_OPERATIONS_ERROR;
1065 for (i = 0; i < el->num_values; i++) {
1066 struct parsed_dn *p = &pdn[i];
1067 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1068 &ac->our_invocation_id,
1070 if (ret != LDB_SUCCESS) {
1071 talloc_free(tmp_ctx);
1075 ret = replmd_defer_add_backlink(module, replmd_private,
1077 forward_dn, &p->guid, true, sa,
1079 if (ret != LDB_SUCCESS) {
1080 talloc_free(tmp_ctx);
1084 new_values[i] = *p->v;
1086 el->values = talloc_steal(mem_ctx, new_values);
1088 talloc_free(tmp_ctx);
1092 static int replmd_add_make_extended_dn(struct ldb_request *req,
1093 const DATA_BLOB *guid_blob,
1094 struct ldb_dn **_extended_dn)
1097 const DATA_BLOB *sid_blob;
1098 /* Calculate an extended DN for any linked attributes */
1099 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1101 return LDB_ERR_OPERATIONS_ERROR;
1103 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1104 if (ret != LDB_SUCCESS) {
1108 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1109 if (sid_blob != NULL) {
1110 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1111 if (ret != LDB_SUCCESS) {
1115 *_extended_dn = extended_dn;
1120 intercept add requests
1122 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1124 struct ldb_context *ldb;
1125 struct ldb_control *control;
1126 struct replmd_replicated_request *ac;
1127 enum ndr_err_code ndr_err;
1128 struct ldb_request *down_req;
1129 struct ldb_message *msg;
1130 const DATA_BLOB *guid_blob;
1131 DATA_BLOB guid_blob_stack;
1133 uint8_t guid_data[16];
1134 struct replPropertyMetaDataBlob nmd;
1135 struct ldb_val nmd_value;
1136 struct ldb_dn *extended_dn = NULL;
1139 * The use of a time_t here seems odd, but as the NTTIME
1140 * elements are actually declared as NTTIME_1sec in the IDL,
1141 * getting a higher resolution timestamp is not required.
1143 time_t t = time(NULL);
1148 unsigned int functional_level;
1150 bool allow_add_guid = false;
1151 bool remove_current_guid = false;
1152 bool is_urgent = false;
1153 bool is_schema_nc = false;
1154 struct ldb_message_element *objectclass_el;
1155 struct replmd_private *replmd_private =
1156 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1158 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1159 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1161 allow_add_guid = true;
1164 /* do not manipulate our control entries */
1165 if (ldb_dn_is_special(req->op.add.message->dn)) {
1166 return ldb_next_request(module, req);
1169 ldb = ldb_module_get_ctx(module);
1171 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1173 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1174 if (guid_blob != NULL) {
1175 if (!allow_add_guid) {
1176 ldb_set_errstring(ldb,
1177 "replmd_add: it's not allowed to add an object with objectGUID!");
1178 return LDB_ERR_UNWILLING_TO_PERFORM;
1180 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1181 if (!NT_STATUS_IS_OK(status)) {
1182 ldb_set_errstring(ldb,
1183 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1184 return LDB_ERR_UNWILLING_TO_PERFORM;
1186 /* we remove this attribute as it can be a string and
1187 * will not be treated correctly and then we will re-add
1188 * it later on in the good format */
1189 remove_current_guid = true;
1193 guid = GUID_random();
1195 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1197 /* This can't fail */
1198 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1199 (ndr_push_flags_fn_t)ndr_push_GUID);
1200 guid_blob = &guid_blob_stack;
1203 ac = replmd_ctx_init(module, req);
1205 return ldb_module_oom(module);
1208 functional_level = dsdb_functional_level(ldb);
1210 /* Get a sequence number from the backend */
1211 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1212 if (ret != LDB_SUCCESS) {
1217 /* we have to copy the message as the caller might have it as a const */
1218 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1222 return LDB_ERR_OPERATIONS_ERROR;
1225 /* generated times */
1226 unix_to_nt_time(&now, t);
1227 time_str = ldb_timestring(msg, t);
1231 return LDB_ERR_OPERATIONS_ERROR;
1233 if (remove_current_guid) {
1234 ldb_msg_remove_attr(msg,"objectGUID");
1238 * remove autogenerated attributes
1240 ldb_msg_remove_attr(msg, "whenCreated");
1241 ldb_msg_remove_attr(msg, "whenChanged");
1242 ldb_msg_remove_attr(msg, "uSNCreated");
1243 ldb_msg_remove_attr(msg, "uSNChanged");
1244 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1247 * readd replicated attributes
1249 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1250 if (ret != LDB_SUCCESS) {
1256 /* build the replication meta_data */
1259 nmd.ctr.ctr1.count = msg->num_elements;
1260 nmd.ctr.ctr1.array = talloc_array(msg,
1261 struct replPropertyMetaData1,
1262 nmd.ctr.ctr1.count);
1263 if (!nmd.ctr.ctr1.array) {
1266 return LDB_ERR_OPERATIONS_ERROR;
1269 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1271 for (i=0; i < msg->num_elements;) {
1272 struct ldb_message_element *e = &msg->elements[i];
1273 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1274 const struct dsdb_attribute *sa;
1276 if (e->name[0] == '@') {
1281 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1283 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1284 "replmd_add: attribute '%s' not defined in schema\n",
1287 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1290 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1291 /* if the attribute is not replicated (0x00000001)
1292 * or constructed (0x00000004) it has no metadata
1298 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1299 if (extended_dn == NULL) {
1300 ret = replmd_add_make_extended_dn(req,
1303 if (ret != LDB_SUCCESS) {
1310 * Prepare the context for the backlinks and
1311 * create metadata for the forward links. The
1312 * backlinks are created in
1313 * replmd_op_callback() after the successful
1314 * ADD of the object.
1316 ret = replmd_add_fix_la(module, msg->elements,
1321 if (ret != LDB_SUCCESS) {
1325 /* linked attributes are not stored in
1326 replPropertyMetaData in FL above w2k */
1331 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1333 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1334 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1337 if (rdn_val == NULL) {
1340 return LDB_ERR_OPERATIONS_ERROR;
1343 rdn = (const char*)rdn_val->data;
1344 if (strcmp(rdn, "Deleted Objects") == 0) {
1346 * Set the originating_change_time to 29/12/9999 at 23:59:59
1347 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1349 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1351 m->originating_change_time = now;
1354 m->originating_change_time = now;
1356 m->originating_invocation_id = ac->our_invocation_id;
1357 m->originating_usn = ac->seq_num;
1358 m->local_usn = ac->seq_num;
1361 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1366 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1368 if (e->num_values != 0) {
1373 ldb_msg_remove_element(msg, e);
1376 /* fix meta data count */
1377 nmd.ctr.ctr1.count = ni;
1380 * sort meta data array
1382 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1383 if (ret != LDB_SUCCESS) {
1384 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1389 /* generated NDR encoded values */
1390 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1392 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1393 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1396 return LDB_ERR_OPERATIONS_ERROR;
1400 * add the autogenerated values
1402 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1403 if (ret != LDB_SUCCESS) {
1408 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1409 if (ret != LDB_SUCCESS) {
1414 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1415 if (ret != LDB_SUCCESS) {
1420 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1421 if (ret != LDB_SUCCESS) {
1426 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1427 if (ret != LDB_SUCCESS) {
1434 * sort the attributes by attid before storing the object
1436 replmd_ldb_message_sort(msg, ac->schema);
1439 * Assert that we do have an objectClass
1441 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1442 if (objectclass_el == NULL) {
1443 ldb_asprintf_errstring(ldb, __location__
1444 ": objectClass missing on %s\n",
1445 ldb_dn_get_linearized(msg->dn));
1447 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1449 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1450 REPL_URGENT_ON_CREATE);
1452 ac->is_urgent = is_urgent;
1453 ret = ldb_build_add_req(&down_req, ldb, ac,
1456 ac, replmd_op_callback,
1459 LDB_REQ_SET_LOCATION(down_req);
1460 if (ret != LDB_SUCCESS) {
1465 /* current partition control is needed by "replmd_op_callback" */
1466 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1467 ret = ldb_request_add_control(down_req,
1468 DSDB_CONTROL_CURRENT_PARTITION_OID,
1470 if (ret != LDB_SUCCESS) {
1476 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1477 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1478 if (ret != LDB_SUCCESS) {
1484 /* mark the control done */
1486 control->critical = 0;
1488 /* go on with the call chain */
1489 return ldb_next_request(module, down_req);
1494 * update the replPropertyMetaData for one element
1496 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1497 struct ldb_message *msg,
1498 struct ldb_message_element *el,
1499 struct ldb_message_element *old_el,
1500 struct replPropertyMetaDataBlob *omd,
1501 const struct dsdb_schema *schema,
1503 const struct GUID *our_invocation_id,
1506 bool is_forced_rodc,
1507 struct ldb_request *req)
1510 const struct dsdb_attribute *a;
1511 struct replPropertyMetaData1 *md1;
1512 bool may_skip = false;
1515 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1517 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1518 /* allow this to make it possible for dbcheck
1519 to remove bad attributes */
1523 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1525 return LDB_ERR_OPERATIONS_ERROR;
1528 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1530 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1535 * if the attribute's value haven't changed, and this isn't
1536 * just a delete of everything then return LDB_SUCCESS Unless
1537 * we have the provision control or if the attribute is
1538 * interSiteTopologyGenerator as this page explain:
1539 * http://support.microsoft.com/kb/224815 this attribute is
1540 * periodicaly written by the DC responsible for the intersite
1541 * generation in a given site
1543 * Unchanged could be deleting or replacing an already-gone
1544 * thing with an unconstrained delete/empty replace or a
1545 * replace with the same value, but not an add with the same
1546 * value because that could be about adding a duplicate (which
1547 * is for someone else to error out on).
1549 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1550 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1553 } else if (old_el == NULL && el->num_values == 0) {
1554 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1556 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1559 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1560 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1562 * We intentionally skip the version bump when attempting to
1565 * The control is set by dbcheck and expunge-tombstones which
1566 * both attempt to be non-replicating. Otherwise, making an
1567 * alteration to the replication state would trigger a
1568 * broadcast of all expunged objects.
1573 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1575 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1579 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1580 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1582 * allow this to make it possible for dbcheck
1583 * to rebuild broken metadata
1589 for (i=0; i<omd->ctr.ctr1.count; i++) {
1591 * First check if we find it under the msDS-IntID,
1592 * then check if we find it under the OID and
1595 * This allows the administrator to simply re-write
1596 * the attributes and so restore replication, which is
1597 * likely what they will try to do.
1599 if (attid == omd->ctr.ctr1.array[i].attid) {
1603 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1608 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1609 /* linked attributes are not stored in
1610 replPropertyMetaData in FL above w2k, but we do
1611 raise the seqnum for the object */
1612 if (*seq_num == 0 &&
1613 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1614 return LDB_ERR_OPERATIONS_ERROR;
1619 if (i == omd->ctr.ctr1.count) {
1620 /* we need to add a new one */
1621 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1622 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1623 if (omd->ctr.ctr1.array == NULL) {
1625 return LDB_ERR_OPERATIONS_ERROR;
1627 omd->ctr.ctr1.count++;
1628 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1631 /* Get a new sequence number from the backend. We only do this
1632 * if we have a change that requires a new
1633 * replPropertyMetaData element
1635 if (*seq_num == 0) {
1636 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1637 if (ret != LDB_SUCCESS) {
1638 return LDB_ERR_OPERATIONS_ERROR;
1642 md1 = &omd->ctr.ctr1.array[i];
1646 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1647 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1650 if (rdn_val == NULL) {
1652 return LDB_ERR_OPERATIONS_ERROR;
1655 rdn = (const char*)rdn_val->data;
1656 if (strcmp(rdn, "Deleted Objects") == 0) {
1658 * Set the originating_change_time to 29/12/9999 at 23:59:59
1659 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1661 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1663 md1->originating_change_time = now;
1666 md1->originating_change_time = now;
1668 md1->originating_invocation_id = *our_invocation_id;
1669 md1->originating_usn = *seq_num;
1670 md1->local_usn = *seq_num;
1672 if (is_forced_rodc) {
1673 /* Force version to 0 to be overriden later via replication */
1681 * Bump the replPropertyMetaData version on an attribute, and if it
1682 * has changed (or forced by leaving rdn_old NULL), update the value
1685 * This is important, as calling a modify operation may not change the
1686 * version number if the values appear unchanged, but a rename between
1687 * parents bumps this value.
1690 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1691 struct ldb_message *msg,
1692 const struct ldb_val *rdn_new,
1693 const struct ldb_val *rdn_old,
1694 struct replPropertyMetaDataBlob *omd,
1695 struct replmd_replicated_request *ar,
1698 bool is_forced_rodc)
1700 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1701 const struct dsdb_attribute *rdn_attr =
1702 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1703 const char *attr_name = rdn_attr != NULL ?
1704 rdn_attr->lDAPDisplayName :
1706 struct ldb_message_element new_el = {
1707 .flags = LDB_FLAG_MOD_REPLACE,
1710 .values = discard_const_p(struct ldb_val, rdn_new)
1712 struct ldb_message_element old_el = {
1713 .flags = LDB_FLAG_MOD_REPLACE,
1715 .num_values = rdn_old ? 1 : 0,
1716 .values = discard_const_p(struct ldb_val, rdn_old)
1719 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1720 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1721 if (ret != LDB_SUCCESS) {
1722 return ldb_oom(ldb);
1726 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1727 omd, ar->schema, &ar->seq_num,
1728 &ar->our_invocation_id,
1729 now, is_schema_nc, is_forced_rodc,
1734 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1736 uint32_t count = omd.ctr.ctr1.count;
1739 for (i=0; i < count; i++) {
1740 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1741 if (max < m.local_usn) {
1749 * update the replPropertyMetaData object each time we modify an
1750 * object. This is needed for DRS replication, as the merge on the
1751 * client is based on this object
1753 static int replmd_update_rpmd(struct ldb_module *module,
1754 const struct dsdb_schema *schema,
1755 struct ldb_request *req,
1756 const char * const *rename_attrs,
1757 struct ldb_message *msg, uint64_t *seq_num,
1758 time_t t, bool is_schema_nc,
1759 bool *is_urgent, bool *rodc)
1761 const struct ldb_val *omd_value;
1762 enum ndr_err_code ndr_err;
1763 struct replPropertyMetaDataBlob omd;
1766 const struct GUID *our_invocation_id;
1768 const char * const *attrs = NULL;
1769 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1770 struct ldb_result *res;
1771 struct ldb_context *ldb;
1772 struct ldb_message_element *objectclass_el;
1773 enum urgent_situation situation;
1774 bool rmd_is_provided;
1775 bool rmd_is_just_resorted = false;
1776 const char *not_rename_attrs[4 + msg->num_elements];
1777 bool is_forced_rodc = false;
1780 attrs = rename_attrs;
1782 for (i = 0; i < msg->num_elements; i++) {
1783 not_rename_attrs[i] = msg->elements[i].name;
1785 not_rename_attrs[i] = "replPropertyMetaData";
1786 not_rename_attrs[i+1] = "objectClass";
1787 not_rename_attrs[i+2] = "instanceType";
1788 not_rename_attrs[i+3] = NULL;
1789 attrs = not_rename_attrs;
1792 ldb = ldb_module_get_ctx(module);
1794 ret = samdb_rodc(ldb, rodc);
1795 if (ret != LDB_SUCCESS) {
1796 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1801 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1802 is_forced_rodc = true;
1805 our_invocation_id = samdb_ntds_invocation_id(ldb);
1806 if (!our_invocation_id) {
1807 /* this happens during an initial vampire while
1808 updating the schema */
1809 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1813 unix_to_nt_time(&now, t);
1815 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1816 rmd_is_provided = true;
1817 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1818 rmd_is_just_resorted = true;
1821 rmd_is_provided = false;
1824 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1825 * otherwise we consider we are updating */
1826 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1827 situation = REPL_URGENT_ON_DELETE;
1828 } else if (rename_attrs) {
1829 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1831 situation = REPL_URGENT_ON_UPDATE;
1834 if (rmd_is_provided) {
1835 /* In this case the change_replmetadata control was supplied */
1836 /* We check that it's the only attribute that is provided
1837 * (it's a rare case so it's better to keep the code simplier)
1838 * We also check that the highest local_usn is bigger or the same as
1841 if( msg->num_elements != 1 ||
1842 strncmp(msg->elements[0].name,
1843 "replPropertyMetaData", 20) ) {
1844 DEBUG(0,(__location__ ": changereplmetada control called without "\
1845 "a specified replPropertyMetaData attribute or with others\n"));
1846 return LDB_ERR_OPERATIONS_ERROR;
1848 if (situation != REPL_URGENT_ON_UPDATE) {
1849 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1850 return LDB_ERR_OPERATIONS_ERROR;
1852 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1854 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1855 ldb_dn_get_linearized(msg->dn)));
1856 return LDB_ERR_OPERATIONS_ERROR;
1858 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1859 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1860 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1861 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1862 ldb_dn_get_linearized(msg->dn)));
1863 return LDB_ERR_OPERATIONS_ERROR;
1866 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1867 DSDB_FLAG_NEXT_MODULE |
1868 DSDB_SEARCH_SHOW_RECYCLED |
1869 DSDB_SEARCH_SHOW_EXTENDED_DN |
1870 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1871 DSDB_SEARCH_REVEAL_INTERNALS, req);
1873 if (ret != LDB_SUCCESS) {
1877 if (rmd_is_just_resorted == false) {
1878 *seq_num = find_max_local_usn(omd);
1880 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1883 * The test here now allows for a new
1884 * replPropertyMetaData with no change, if was
1885 * just dbcheck re-sorting the values.
1887 if (*seq_num <= db_seq) {
1888 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1889 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1890 (long long)*seq_num, (long long)db_seq));
1891 return LDB_ERR_OPERATIONS_ERROR;
1896 /* search for the existing replPropertyMetaDataBlob. We need
1897 * to use REVEAL and ask for DNs in storage format to support
1898 * the check for values being the same in
1899 * replmd_update_rpmd_element()
1901 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1902 DSDB_FLAG_NEXT_MODULE |
1903 DSDB_SEARCH_SHOW_RECYCLED |
1904 DSDB_SEARCH_SHOW_EXTENDED_DN |
1905 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1906 DSDB_SEARCH_REVEAL_INTERNALS, req);
1907 if (ret != LDB_SUCCESS) {
1911 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1913 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1914 ldb_dn_get_linearized(msg->dn)));
1915 return LDB_ERR_OPERATIONS_ERROR;
1918 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1919 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1920 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1921 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1922 ldb_dn_get_linearized(msg->dn)));
1923 return LDB_ERR_OPERATIONS_ERROR;
1926 if (omd.version != 1) {
1927 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1928 omd.version, ldb_dn_get_linearized(msg->dn)));
1929 return LDB_ERR_OPERATIONS_ERROR;
1932 for (i=0; i<msg->num_elements;) {
1933 struct ldb_message_element *el = &msg->elements[i];
1934 struct ldb_message_element *old_el;
1936 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1937 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1938 &omd, schema, seq_num,
1943 if (ret != LDB_SUCCESS) {
1947 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1948 *is_urgent = replmd_check_urgent_attribute(el);
1951 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1956 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1958 if (el->num_values != 0) {
1963 ldb_msg_remove_element(msg, el);
1968 * Assert that we have an objectClass attribute - this is major
1969 * corruption if we don't have this!
1971 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1972 if (objectclass_el != NULL) {
1974 * Now check if this objectClass means we need to do urgent replication
1976 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1980 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1981 ldb_asprintf_errstring(ldb, __location__
1982 ": objectClass missing on %s\n",
1983 ldb_dn_get_linearized(msg->dn));
1984 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1988 * replmd_update_rpmd_element has done an update if the
1991 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1992 struct ldb_val *md_value;
1993 struct ldb_message_element *el;
1995 /*if we are RODC and this is a DRSR update then its ok*/
1996 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1997 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1998 && !is_forced_rodc) {
1999 unsigned instanceType;
2002 ldb_set_errstring(ldb, "RODC modify is forbidden!");
2003 return LDB_ERR_REFERRAL;
2006 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
2007 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
2008 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
2009 "cannot change replicated attribute on partial replica");
2013 md_value = talloc(msg, struct ldb_val);
2014 if (md_value == NULL) {
2016 return LDB_ERR_OPERATIONS_ERROR;
2019 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2020 if (ret != LDB_SUCCESS) {
2021 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2025 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2026 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2027 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2028 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2029 ldb_dn_get_linearized(msg->dn)));
2030 return LDB_ERR_OPERATIONS_ERROR;
2033 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2034 if (ret != LDB_SUCCESS) {
2035 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2036 ldb_dn_get_linearized(msg->dn)));
2041 el->values = md_value;
2047 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2049 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2051 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2052 &pdn2->dsdb_dn->extra_part);
2058 get a series of message element values as an array of DNs and GUIDs
2059 the result is sorted by GUID
2061 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2062 struct ldb_message_element *el, struct parsed_dn **pdn,
2063 const char *ldap_oid, struct ldb_request *parent)
2066 bool values_are_sorted = true;
2067 struct ldb_context *ldb = ldb_module_get_ctx(module);
2074 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2076 ldb_module_oom(module);
2077 return LDB_ERR_OPERATIONS_ERROR;
2080 for (i=0; i<el->num_values; i++) {
2081 struct ldb_val *v = &el->values[i];
2084 struct parsed_dn *p;
2088 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2089 if (p->dsdb_dn == NULL) {
2090 return LDB_ERR_INVALID_DN_SYNTAX;
2093 dn = p->dsdb_dn->dn;
2095 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2096 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2097 unlikely(GUID_all_zero(&p->guid))) {
2098 /* we got a DN without a GUID - go find the GUID */
2099 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2100 if (ret != LDB_SUCCESS) {
2101 char *dn_str = NULL;
2102 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2104 ldb_asprintf_errstring(ldb,
2105 "Unable to find GUID for DN %s\n",
2107 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2108 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2109 ldb_attr_cmp(el->name, "member") == 0) {
2110 return LDB_ERR_UNWILLING_TO_PERFORM;
2114 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2115 if (ret != LDB_SUCCESS) {
2118 } else if (!NT_STATUS_IS_OK(status)) {
2119 return LDB_ERR_OPERATIONS_ERROR;
2121 if (i > 0 && values_are_sorted) {
2122 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2124 values_are_sorted = false;
2127 /* keep a pointer to the original ldb_val */
2130 if (! values_are_sorted) {
2131 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2137 * Get a series of trusted message element values. The result is sorted by
2138 * GUID, even though the GUIDs might not be known. That works because we trust
2139 * the database to give us the elements like that if the
2140 * replmd_private->sorted_links flag is set.
2142 * We also ensure that the links are in the Functional Level 2003
2143 * linked attributes format.
2145 static int get_parsed_dns_trusted(struct ldb_module *module,
2146 struct replmd_private *replmd_private,
2147 TALLOC_CTX *mem_ctx,
2148 struct ldb_message_element *el,
2149 struct parsed_dn **pdn,
2150 const char *ldap_oid,
2151 struct ldb_request *parent)
2160 if (!replmd_private->sorted_links) {
2161 /* We need to sort the list. This is the slow old path we want
2164 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2166 if (ret != LDB_SUCCESS) {
2170 /* Here we get a list of 'struct parsed_dns' without the parsing */
2171 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2174 ldb_module_oom(module);
2175 return LDB_ERR_OPERATIONS_ERROR;
2178 for (i = 0; i < el->num_values; i++) {
2179 (*pdn)[i].v = &el->values[i];
2184 * This upgrades links to FL2003 style, and sorts the result
2185 * if that was needed.
2187 * TODO: Add a database feature that asserts we have no FL2000
2188 * style links to avoid this check or add a feature that
2189 * uses a similar check to find sorted/unsorted links
2190 * for an on-the-fly upgrade.
2193 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2194 *pdn, el->num_values,
2197 if (ret != LDB_SUCCESS) {
2205 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2206 otherwise an error code. For compatibility the error code differs depending
2207 on whether or not the attribute is "member".
2209 As always, the parsed_dn list is assumed to be sorted.
2211 static int check_parsed_dn_duplicates(struct ldb_module *module,
2212 struct ldb_message_element *el,
2213 struct parsed_dn *pdn)
2216 struct ldb_context *ldb = ldb_module_get_ctx(module);
2218 for (i = 1; i < el->num_values; i++) {
2219 struct parsed_dn *p = &pdn[i];
2220 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2221 ldb_asprintf_errstring(ldb,
2222 "Linked attribute %s has "
2223 "multiple identical values",
2225 if (ldb_attr_cmp(el->name, "member") == 0) {
2226 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2228 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2236 build a new extended DN, including all meta data fields
2238 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2239 RMD_ADDTIME = originating_add_time
2240 RMD_INVOCID = originating_invocation_id
2241 RMD_CHANGETIME = originating_change_time
2242 RMD_ORIGINATING_USN = originating_usn
2243 RMD_LOCAL_USN = local_usn
2244 RMD_VERSION = version
2246 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2247 struct dsdb_dn *dsdb_dn,
2248 const struct GUID *invocation_id,
2249 uint64_t local_usn, NTTIME nttime)
2251 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2252 local_usn, local_usn, nttime,
2253 RMD_VERSION_INITIAL, false);
2256 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2257 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2258 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2262 check if any links need upgrading from w2k format
2264 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2265 struct parsed_dn *dns, uint32_t count,
2266 struct ldb_message_element *el,
2267 const char *ldap_oid)
2270 const struct GUID *invocation_id = NULL;
2271 for (i=0; i<count; i++) {
2275 if (dns[i].dsdb_dn == NULL) {
2276 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2278 if (ret != LDB_SUCCESS) {
2279 return LDB_ERR_INVALID_DN_SYNTAX;
2283 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2284 &version, "RMD_VERSION");
2285 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2287 * We optimistically assume they are all the same; if
2288 * the first one is fixed, they are all fixed.
2290 * If the first one was *not* fixed and we find a
2291 * later one that is, that is an occasion to shout
2297 DEBUG(0, ("Mixed w2k and fixed format "
2298 "linked attributes\n"));
2302 if (invocation_id == NULL) {
2303 invocation_id = samdb_ntds_invocation_id(ldb);
2304 if (invocation_id == NULL) {
2305 return LDB_ERR_OPERATIONS_ERROR;
2310 /* it's an old one that needs upgrading */
2311 ret = replmd_update_la_val(el->values, dns[i].v,
2312 dns[i].dsdb_dn, dns[i].dsdb_dn,
2313 invocation_id, 1, 1, 0, false);
2314 if (ret != LDB_SUCCESS) {
2320 * This sort() is critical for the operation of
2321 * get_parsed_dns_trusted() because callers of this function
2322 * expect a sorted list, and FL2000 style links are not
2323 * sorted. In particular, as well as the upgrade case,
2324 * get_parsed_dns_trusted() is called from
2325 * replmd_delete_remove_link() even in FL2000 mode
2327 * We do not normally pay the cost of the qsort() due to the
2328 * early return in the RMD_VERSION found case.
2330 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2335 Sets the value for a linked attribute, including all meta data fields
2337 see replmd_build_la_val for value names
2339 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2340 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2341 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2342 uint32_t version, bool deleted)
2344 struct ldb_dn *dn = dsdb_dn->dn;
2345 const char *tstring, *usn_string, *flags_string;
2346 struct ldb_val tval;
2348 struct ldb_val usnv, local_usnv;
2349 struct ldb_val vers, flagsv;
2350 const struct ldb_val *old_addtime = NULL;
2353 const char *dnstring;
2355 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2357 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2359 return LDB_ERR_OPERATIONS_ERROR;
2361 tval = data_blob_string_const(tstring);
2363 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2365 return LDB_ERR_OPERATIONS_ERROR;
2367 usnv = data_blob_string_const(usn_string);
2369 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2371 return LDB_ERR_OPERATIONS_ERROR;
2373 local_usnv = data_blob_string_const(usn_string);
2375 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2376 if (!NT_STATUS_IS_OK(status)) {
2377 return LDB_ERR_OPERATIONS_ERROR;
2380 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2381 if (!flags_string) {
2382 return LDB_ERR_OPERATIONS_ERROR;
2384 flagsv = data_blob_string_const(flags_string);
2386 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2387 if (ret != LDB_SUCCESS) return ret;
2389 /* get the ADDTIME from the original */
2390 if (old_dsdb_dn != NULL) {
2391 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2394 if (old_addtime == NULL) {
2395 old_addtime = &tval;
2397 if (dsdb_dn != old_dsdb_dn ||
2398 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2399 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2400 if (ret != LDB_SUCCESS) return ret;
2403 /* use our invocation id */
2404 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2405 if (ret != LDB_SUCCESS) return ret;
2407 /* changetime is the current time */
2408 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2409 if (ret != LDB_SUCCESS) return ret;
2411 /* update the USN */
2412 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2413 if (ret != LDB_SUCCESS) return ret;
2415 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2416 if (ret != LDB_SUCCESS) return ret;
2418 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2419 vers = data_blob_string_const(vstring);
2420 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2421 if (ret != LDB_SUCCESS) return ret;
2423 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2424 if (dnstring == NULL) {
2425 return LDB_ERR_OPERATIONS_ERROR;
2427 *v = data_blob_string_const(dnstring);
2433 * Updates the value for a linked attribute, including all meta data fields
2435 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2436 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2437 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2440 uint32_t old_version;
2441 uint32_t version = RMD_VERSION_INITIAL;
2445 * We're updating the linked attribute locally, so increase the version
2446 * by 1 so that other DCs will see the change when it gets replicated out
2448 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2451 if (NT_STATUS_IS_OK(status)) {
2452 version = old_version + 1;
2455 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2456 usn, local_usn, nttime, version, deleted);
2460 handle adding a linked attribute
2462 static int replmd_modify_la_add(struct ldb_module *module,
2463 struct replmd_private *replmd_private,
2464 struct replmd_replicated_request *ac,
2465 struct ldb_message *msg,
2466 struct ldb_message_element *el,
2467 struct ldb_message_element *old_el,
2468 const struct dsdb_attribute *schema_attr,
2470 struct ldb_dn *msg_dn,
2471 struct ldb_request *parent)
2474 struct parsed_dn *dns, *old_dns;
2475 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2477 struct ldb_val *new_values = NULL;
2478 unsigned old_num_values = old_el ? old_el->num_values : 0;
2479 unsigned num_values = 0;
2480 unsigned max_num_values;
2481 struct ldb_context *ldb = ldb_module_get_ctx(module);
2483 unix_to_nt_time(&now, t);
2485 /* get the DNs to be added, fully parsed.
2487 * We need full parsing because they came off the wire and we don't
2488 * trust them, besides which we need their details to know where to put
2491 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2492 schema_attr->syntax->ldap_oid, parent);
2493 if (ret != LDB_SUCCESS) {
2494 talloc_free(tmp_ctx);
2498 /* get the existing DNs, lazily parsed */
2499 ret = get_parsed_dns_trusted(module, replmd_private,
2500 tmp_ctx, old_el, &old_dns,
2501 schema_attr->syntax->ldap_oid, parent);
2503 if (ret != LDB_SUCCESS) {
2504 talloc_free(tmp_ctx);
2508 max_num_values = old_num_values + el->num_values;
2509 if (max_num_values < old_num_values) {
2510 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2511 "old values: %u, new values: %u, sum: %u\n",
2512 old_num_values, el->num_values, max_num_values));
2513 talloc_free(tmp_ctx);
2514 return LDB_ERR_OPERATIONS_ERROR;
2517 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2519 if (new_values == NULL) {
2520 ldb_module_oom(module);
2521 talloc_free(tmp_ctx);
2522 return LDB_ERR_OPERATIONS_ERROR;
2526 * For each new value, find where it would go in the list. If there is
2527 * a matching GUID there, we update the existing value; otherwise we
2531 for (i = 0; i < el->num_values; i++) {
2532 struct parsed_dn *exact;
2533 struct parsed_dn *next;
2535 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2538 dns[i].dsdb_dn->extra_part, 0,
2540 schema_attr->syntax->ldap_oid,
2542 if (err != LDB_SUCCESS) {
2543 talloc_free(tmp_ctx);
2547 if (ac->fix_link_sid) {
2548 char *fixed_dnstring = NULL;
2549 struct dom_sid tmp_sid = { 0, };
2550 DATA_BLOB sid_blob = data_blob_null;
2551 enum ndr_err_code ndr_err;
2555 if (exact == NULL) {
2556 talloc_free(tmp_ctx);
2557 return ldb_operr(ldb);
2560 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2561 talloc_free(tmp_ctx);
2562 return ldb_operr(ldb);
2566 * Only "<GUID=...><SID=...>" is allowed.
2568 * We get the GUID to just to find the old
2569 * value and the SID in order to add it
2570 * to the found value.
2573 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2575 talloc_free(tmp_ctx);
2576 return ldb_operr(ldb);
2579 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2581 talloc_free(tmp_ctx);
2582 return ldb_operr(ldb);
2585 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2587 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2588 /* this is what we expect */
2589 } else if (NT_STATUS_IS_OK(status)) {
2590 struct GUID_txt_buf guid_str;
2591 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2592 "i[%u] SID NOT MISSING... Attribute %s already "
2593 "exists for target GUID %s, SID %s, DN: %s",
2595 GUID_buf_string(&exact->guid,
2597 dom_sid_string(tmp_ctx, &tmp_sid),
2598 dsdb_dn_get_extended_linearized(tmp_ctx,
2599 exact->dsdb_dn, 1));
2600 talloc_free(tmp_ctx);
2601 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2603 talloc_free(tmp_ctx);
2604 return ldb_operr(ldb);
2607 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2609 if (!NT_STATUS_IS_OK(status)) {
2610 struct GUID_txt_buf guid_str;
2611 ldb_asprintf_errstring(ldb,
2612 "NO SID PROVIDED... Attribute %s already "
2613 "exists for target GUID %s",
2615 GUID_buf_string(&exact->guid,
2617 talloc_free(tmp_ctx);
2618 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2621 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2622 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2623 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2624 talloc_free(tmp_ctx);
2625 return ldb_operr(ldb);
2628 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2629 data_blob_free(&sid_blob);
2630 if (ret != LDB_SUCCESS) {
2631 talloc_free(tmp_ctx);
2635 fixed_dnstring = dsdb_dn_get_extended_linearized(
2636 new_values, exact->dsdb_dn, 1);
2637 if (fixed_dnstring == NULL) {
2638 talloc_free(tmp_ctx);
2639 return ldb_operr(ldb);
2643 * We just replace the existing value...
2645 *exact->v = data_blob_string_const(fixed_dnstring);
2650 if (exact != NULL) {
2652 * We are trying to add one that exists, which is only
2653 * allowed if it was previously deleted.
2655 * When we do undelete a link we change it in place.
2656 * It will be copied across into the right spot in due
2660 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2662 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2663 struct GUID_txt_buf guid_str;
2664 ldb_asprintf_errstring(ldb,
2665 "Attribute %s already "
2666 "exists for target GUID %s",
2668 GUID_buf_string(&exact->guid,
2670 talloc_free(tmp_ctx);
2671 /* error codes for 'member' need to be
2673 if (ldb_attr_cmp(el->name, "member") == 0) {
2674 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2676 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2680 ret = replmd_update_la_val(new_values, exact->v,
2683 &ac->our_invocation_id,
2684 ac->seq_num, ac->seq_num,
2686 if (ret != LDB_SUCCESS) {
2687 talloc_free(tmp_ctx);
2691 ret = replmd_add_backlink(module, replmd_private,
2698 if (ret != LDB_SUCCESS) {
2699 talloc_free(tmp_ctx);
2705 * Here we don't have an exact match.
2707 * If next is NULL, this one goes beyond the end of the
2708 * existing list, so we need to add all of those ones first.
2710 * If next is not NULL, we need to add all the ones before
2714 offset = old_num_values;
2716 /* next should have been parsed, but let's make sure */
2717 if (next->dsdb_dn == NULL) {
2718 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2719 schema_attr->syntax->ldap_oid);
2720 if (ret != LDB_SUCCESS) {
2724 offset = MIN(next - old_dns, old_num_values);
2727 /* put all the old ones before next on the list */
2728 for (; j < offset; j++) {
2729 new_values[num_values] = *old_dns[j].v;
2733 ret = replmd_add_backlink(module, replmd_private,
2738 /* Make the new linked attribute ldb_val. */
2739 ret = replmd_build_la_val(new_values, &new_values[num_values],
2740 dns[i].dsdb_dn, &ac->our_invocation_id,
2742 if (ret != LDB_SUCCESS) {
2743 talloc_free(tmp_ctx);
2747 if (ret != LDB_SUCCESS) {
2748 talloc_free(tmp_ctx);
2752 /* copy the rest of the old ones (if any) */
2753 for (; j < old_num_values; j++) {
2754 new_values[num_values] = *old_dns[j].v;
2758 talloc_steal(msg->elements, new_values);
2759 if (old_el != NULL) {
2760 talloc_steal(msg->elements, old_el->values);
2762 el->values = new_values;
2763 el->num_values = num_values;
2765 talloc_free(tmp_ctx);
2767 /* we now tell the backend to replace all existing values
2768 with the one we have constructed */
2769 el->flags = LDB_FLAG_MOD_REPLACE;
2776 handle deleting all active linked attributes
2778 static int replmd_modify_la_delete(struct ldb_module *module,
2779 struct replmd_private *replmd_private,
2780 struct replmd_replicated_request *ac,
2781 struct ldb_message *msg,
2782 struct ldb_message_element *el,
2783 struct ldb_message_element *old_el,
2784 const struct dsdb_attribute *schema_attr,
2786 struct ldb_dn *msg_dn,
2787 struct ldb_request *parent)
2790 struct parsed_dn *dns, *old_dns;
2791 TALLOC_CTX *tmp_ctx = NULL;
2793 struct ldb_context *ldb = ldb_module_get_ctx(module);
2794 struct ldb_control *vanish_links_ctrl = NULL;
2795 bool vanish_links = false;
2796 unsigned int num_to_delete = el->num_values;
2800 unix_to_nt_time(&now, t);
2802 if (old_el == NULL || old_el->num_values == 0) {
2803 /* there is nothing to delete... */
2804 if (num_to_delete == 0) {
2805 /* and we're deleting nothing, so that's OK */
2808 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2811 tmp_ctx = talloc_new(msg);
2812 if (tmp_ctx == NULL) {
2813 return LDB_ERR_OPERATIONS_ERROR;
2816 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2817 schema_attr->syntax->ldap_oid, parent);
2818 if (ret != LDB_SUCCESS) {
2819 talloc_free(tmp_ctx);
2823 ret = get_parsed_dns_trusted(module, replmd_private,
2824 tmp_ctx, old_el, &old_dns,
2825 schema_attr->syntax->ldap_oid, parent);
2827 if (ret != LDB_SUCCESS) {
2828 talloc_free(tmp_ctx);
2833 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2834 if (vanish_links_ctrl) {
2835 vanish_links = true;
2836 vanish_links_ctrl->critical = false;
2840 /* we empty out el->values here to avoid damage if we return early. */
2845 * If vanish links is set, we are actually removing members of
2846 * old_el->values; otherwise we are just marking them deleted.
2848 * There is a special case when no values are given: we remove them
2849 * all. When we have the vanish_links control we just have to remove
2850 * the backlinks and change our element to replace the existing values
2851 * with the empty list.
2854 if (num_to_delete == 0) {
2855 for (i = 0; i < old_el->num_values; i++) {
2856 struct parsed_dn *p = &old_dns[i];
2857 if (p->dsdb_dn == NULL) {
2858 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2859 schema_attr->syntax->ldap_oid);
2860 if (ret != LDB_SUCCESS) {
2864 ret = replmd_add_backlink(module, replmd_private,
2865 ac->schema, msg_dn, &p->guid,
2868 if (ret != LDB_SUCCESS) {
2869 talloc_free(tmp_ctx);
2876 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2877 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2881 ret = replmd_update_la_val(old_el->values, p->v,
2882 p->dsdb_dn, p->dsdb_dn,
2883 &ac->our_invocation_id,
2884 ac->seq_num, ac->seq_num,
2886 if (ret != LDB_SUCCESS) {
2887 talloc_free(tmp_ctx);
2893 el->flags = LDB_FLAG_MOD_REPLACE;
2894 talloc_free(tmp_ctx);
2900 for (i = 0; i < num_to_delete; i++) {
2901 struct parsed_dn *p = &dns[i];
2902 struct parsed_dn *exact = NULL;
2903 struct parsed_dn *next = NULL;
2904 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2907 p->dsdb_dn->extra_part, 0,
2909 schema_attr->syntax->ldap_oid,
2911 if (ret != LDB_SUCCESS) {
2912 talloc_free(tmp_ctx);
2915 if (exact == NULL) {
2916 struct GUID_txt_buf buf;
2917 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2918 "exist for target GUID %s",
2920 GUID_buf_string(&p->guid, &buf));
2921 if (ldb_attr_cmp(el->name, "member") == 0) {
2922 talloc_free(tmp_ctx);
2923 return LDB_ERR_UNWILLING_TO_PERFORM;
2925 talloc_free(tmp_ctx);
2926 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2931 if (CHECK_DEBUGLVL(5)) {
2932 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2933 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2934 struct GUID_txt_buf buf;
2935 const char *guid_str = \
2936 GUID_buf_string(&p->guid, &buf);
2937 DEBUG(5, ("Deleting deleted linked "
2938 "attribute %s to %s, because "
2939 "vanish_links control is set\n",
2940 el->name, guid_str));
2944 /* remove the backlink */
2945 ret = replmd_add_backlink(module,
2952 if (ret != LDB_SUCCESS) {
2953 talloc_free(tmp_ctx);
2957 /* We flag the deletion and tidy it up later. */
2962 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2964 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2965 struct GUID_txt_buf buf;
2966 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2967 ldb_asprintf_errstring(ldb, "Attribute %s already "
2968 "deleted for target GUID %s",
2969 el->name, guid_str);
2970 if (ldb_attr_cmp(el->name, "member") == 0) {
2971 talloc_free(tmp_ctx);
2972 return LDB_ERR_UNWILLING_TO_PERFORM;
2974 talloc_free(tmp_ctx);
2975 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2979 ret = replmd_update_la_val(old_el->values, exact->v,
2980 exact->dsdb_dn, exact->dsdb_dn,
2981 &ac->our_invocation_id,
2982 ac->seq_num, ac->seq_num,
2984 if (ret != LDB_SUCCESS) {
2985 talloc_free(tmp_ctx);
2988 ret = replmd_add_backlink(module, replmd_private,
2993 if (ret != LDB_SUCCESS) {
2994 talloc_free(tmp_ctx);
3001 struct ldb_val *tmp_vals = NULL;
3003 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
3004 old_el->num_values);
3005 if (tmp_vals == NULL) {
3006 talloc_free(tmp_ctx);
3007 return ldb_module_oom(module);
3009 for (i = 0; i < old_el->num_values; i++) {
3010 if (old_dns[i].v == NULL) {
3013 tmp_vals[j] = *old_dns[i].v;
3016 for (i = 0; i < j; i++) {
3017 old_el->values[i] = tmp_vals[i];
3019 old_el->num_values = j;
3022 el->values = talloc_steal(msg->elements, old_el->values);
3023 el->num_values = old_el->num_values;
3025 talloc_free(tmp_ctx);
3027 /* we now tell the backend to replace all existing values
3028 with the one we have constructed */
3029 el->flags = LDB_FLAG_MOD_REPLACE;
3035 handle replacing a linked attribute
3037 static int replmd_modify_la_replace(struct ldb_module *module,
3038 struct replmd_private *replmd_private,
3039 struct replmd_replicated_request *ac,
3040 struct ldb_message *msg,
3041 struct ldb_message_element *el,
3042 struct ldb_message_element *old_el,
3043 const struct dsdb_attribute *schema_attr,
3045 struct ldb_dn *msg_dn,
3046 struct ldb_request *parent)
3048 unsigned int i, old_i, new_i;
3049 struct parsed_dn *dns, *old_dns;
3050 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3052 struct ldb_context *ldb = ldb_module_get_ctx(module);
3053 struct ldb_val *new_values = NULL;
3054 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3055 unsigned int old_num_values;
3056 unsigned int repl_num_values;
3057 unsigned int max_num_values;
3060 unix_to_nt_time(&now, t);
3063 * The replace operation is unlike the replace and delete cases in that
3064 * we need to look at every existing link to see whether it is being
3065 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3067 * As we are trying to combine two sorted lists, the algorithm we use
3068 * is akin to the merge phase of a merge sort. We interleave the two
3069 * lists, doing different things depending on which side the current
3072 * There are three main cases, with some sub-cases.
3074 * - a DN is in the old list but not the new one. It needs to be
3075 * marked as deleted (but left in the list).
3076 * - maybe it is already deleted, and we have less to do.
3078 * - a DN is in both lists. The old data gets replaced by the new,
3079 * and the list doesn't grow. The old link may have been marked as
3080 * deleted, in which case we undelete it.
3082 * - a DN is in the new list only. We add it in the right place.
3085 old_num_values = old_el ? old_el->num_values : 0;
3086 repl_num_values = el->num_values;
3087 max_num_values = old_num_values + repl_num_values;
3089 if (max_num_values == 0) {
3090 /* There is nothing to do! */
3094 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3095 if (ret != LDB_SUCCESS) {
3096 talloc_free(tmp_ctx);
3100 ret = check_parsed_dn_duplicates(module, el, dns);
3101 if (ret != LDB_SUCCESS) {
3102 talloc_free(tmp_ctx);
3106 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3108 if (ret != LDB_SUCCESS) {
3109 talloc_free(tmp_ctx);
3113 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3115 if (ret != LDB_SUCCESS) {
3116 talloc_free(tmp_ctx);
3120 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3121 if (new_values == NULL) {
3122 ldb_module_oom(module);
3123 talloc_free(tmp_ctx);
3124 return LDB_ERR_OPERATIONS_ERROR;
3129 for (i = 0; i < max_num_values; i++) {
3131 struct parsed_dn *old_p, *new_p;
3132 if (old_i < old_num_values && new_i < repl_num_values) {
3133 old_p = &old_dns[old_i];
3134 new_p = &dns[new_i];
3135 cmp = parsed_dn_compare(old_p, new_p);
3136 } else if (old_i < old_num_values) {
3137 /* the new list is empty, read the old list */
3138 old_p = &old_dns[old_i];
3141 } else if (new_i < repl_num_values) {
3142 /* the old list is empty, read new list */
3144 new_p = &dns[new_i];
3152 * An old ones that come before the next replacement
3153 * (if any). We mark it as deleted and add it to the
3156 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3157 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3158 ret = replmd_update_la_val(new_values, old_p->v,
3161 &ac->our_invocation_id,
3162 ac->seq_num, ac->seq_num,
3164 if (ret != LDB_SUCCESS) {
3165 talloc_free(tmp_ctx);
3169 ret = replmd_add_backlink(module, replmd_private,
3172 &old_p->guid, false,
3175 if (ret != LDB_SUCCESS) {
3176 talloc_free(tmp_ctx);
3180 new_values[i] = *old_p->v;
3182 } else if (cmp == 0) {
3184 * We are overwriting one. If it was previously
3185 * deleted, we need to add a backlink.
3187 * Note that if any RMD_FLAGs in an extended new DN
3192 ret = replmd_update_la_val(new_values, old_p->v,
3195 &ac->our_invocation_id,
3196 ac->seq_num, ac->seq_num,
3198 if (ret != LDB_SUCCESS) {
3199 talloc_free(tmp_ctx);
3203 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3204 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3205 ret = replmd_add_backlink(module, replmd_private,
3211 if (ret != LDB_SUCCESS) {
3212 talloc_free(tmp_ctx);
3217 new_values[i] = *old_p->v;
3222 * Replacements that don't match an existing one. We
3223 * just add them to the final list.
3225 ret = replmd_build_la_val(new_values,
3228 &ac->our_invocation_id,
3230 if (ret != LDB_SUCCESS) {
3231 talloc_free(tmp_ctx);
3234 ret = replmd_add_backlink(module, replmd_private,
3240 if (ret != LDB_SUCCESS) {
3241 talloc_free(tmp_ctx);
3244 new_values[i] = *new_p->v;
3248 if (old_el != NULL) {
3249 talloc_steal(msg->elements, old_el->values);
3251 el->values = talloc_steal(msg->elements, new_values);
3253 talloc_free(tmp_ctx);
3255 el->flags = LDB_FLAG_MOD_REPLACE;
3262 handle linked attributes in modify requests
3264 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3265 struct replmd_private *replmd_private,
3266 struct replmd_replicated_request *ac,
3267 struct ldb_message *msg,
3269 struct ldb_request *parent)
3271 struct ldb_result *res;
3274 struct ldb_context *ldb = ldb_module_get_ctx(module);
3275 struct ldb_message *old_msg;
3277 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3279 * Nothing special is required for modifying or vanishing links
3280 * in fl2000 since they are just strings in a multi-valued
3283 struct ldb_control *ctrl = ldb_request_get_control(parent,
3284 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3286 ctrl->critical = false;
3294 * We should restrict this to the intersection of the list of
3295 * linked attributes in the schema and the list of attributes
3298 * This will help performance a little, as otherwise we have
3299 * to allocate the entire object value-by-value.
3301 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3302 DSDB_FLAG_NEXT_MODULE |
3303 DSDB_SEARCH_SHOW_RECYCLED |
3304 DSDB_SEARCH_REVEAL_INTERNALS |
3305 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3307 if (ret != LDB_SUCCESS) {
3311 old_msg = res->msgs[0];
3313 for (i=0; i<msg->num_elements; i++) {
3314 struct ldb_message_element *el = &msg->elements[i];
3315 struct ldb_message_element *old_el, *new_el;
3316 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3317 const struct dsdb_attribute *schema_attr
3318 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3320 ldb_asprintf_errstring(ldb,
3321 "%s: attribute %s is not a valid attribute in schema",
3322 __FUNCTION__, el->name);
3323 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3325 if (schema_attr->linkID == 0) {
3328 if ((schema_attr->linkID & 1) == 1) {
3330 struct ldb_control *ctrl;
3332 ctrl = ldb_request_get_control(parent,
3333 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3335 ctrl->critical = false;
3338 ctrl = ldb_request_get_control(parent,
3339 DSDB_CONTROL_DBCHECK);
3345 /* Odd is for the target. Illegal to modify */
3346 ldb_asprintf_errstring(ldb,
3347 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3348 return LDB_ERR_UNWILLING_TO_PERFORM;
3350 old_el = ldb_msg_find_element(old_msg, el->name);
3352 case LDB_FLAG_MOD_REPLACE:
3353 ret = replmd_modify_la_replace(module, replmd_private,
3354 ac, msg, el, old_el,
3359 case LDB_FLAG_MOD_DELETE:
3360 ret = replmd_modify_la_delete(module, replmd_private,
3361 ac, msg, el, old_el,
3366 case LDB_FLAG_MOD_ADD:
3367 ret = replmd_modify_la_add(module, replmd_private,
3368 ac, msg, el, old_el,
3374 ldb_asprintf_errstring(ldb,
3375 "invalid flags 0x%x for %s linked attribute",
3376 el->flags, el->name);
3377 return LDB_ERR_UNWILLING_TO_PERFORM;
3379 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3380 ldb_asprintf_errstring(ldb,
3381 "Attribute %s is single valued but more than one value has been supplied",
3383 /* Return codes as found on Windows 2012r2 */
3384 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3385 return LDB_ERR_CONSTRAINT_VIOLATION;
3387 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3390 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3393 if (ret != LDB_SUCCESS) {
3397 ldb_msg_remove_attr(old_msg, el->name);
3399 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3400 new_el->num_values = el->num_values;
3401 new_el->values = talloc_steal(msg->elements, el->values);
3403 /* TODO: this relises a bit too heavily on the exact
3404 behaviour of ldb_msg_find_element and
3405 ldb_msg_remove_element */
3406 old_el = ldb_msg_find_element(msg, el->name);
3408 ldb_msg_remove_element(msg, old_el);
3418 static int send_rodc_referral(struct ldb_request *req,
3419 struct ldb_context *ldb,
3422 char *referral = NULL;
3423 struct loadparm_context *lp_ctx = NULL;
3424 struct ldb_dn *fsmo_role_dn = NULL;
3425 struct ldb_dn *role_owner_dn = NULL;
3426 const char *domain = NULL;
3429 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3430 struct loadparm_context);
3432 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3433 &fsmo_role_dn, &role_owner_dn);
3435 if (W_ERROR_IS_OK(werr)) {
3436 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3437 if (server_dn != NULL) {
3438 ldb_dn_remove_child_components(server_dn, 1);
3439 domain = samdb_dn_to_dnshostname(ldb, req,
3444 if (domain == NULL) {
3445 domain = lpcfg_dnsdomain(lp_ctx);
3448 referral = talloc_asprintf(req, "ldap://%s/%s",
3450 ldb_dn_get_linearized(dn));
3451 if (referral == NULL) {
3453 return LDB_ERR_OPERATIONS_ERROR;
3456 return ldb_module_send_referral(req, referral);
3460 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3462 struct ldb_context *ldb;
3463 struct replmd_replicated_request *ac;
3464 struct ldb_request *down_req;
3465 struct ldb_message *msg;
3466 time_t t = time(NULL);
3468 bool is_urgent = false, rodc = false;
3469 bool is_schema_nc = false;
3470 unsigned int functional_level;
3471 const struct ldb_message_element *guid_el = NULL;
3472 struct ldb_control *sd_propagation_control;
3473 struct ldb_control *fix_links_control = NULL;
3474 struct ldb_control *fix_dn_name_control = NULL;
3475 struct ldb_control *fix_dn_sid_control = NULL;
3476 struct replmd_private *replmd_private =
3477 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3479 /* do not manipulate our control entries */
3480 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3481 return ldb_next_request(module, req);
3484 sd_propagation_control = ldb_request_get_control(req,
3485 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3486 if (sd_propagation_control != NULL) {
3487 if (req->op.mod.message->num_elements != 1) {
3488 return ldb_module_operr(module);
3490 ret = strcmp(req->op.mod.message->elements[0].name,
3491 "nTSecurityDescriptor");
3493 return ldb_module_operr(module);
3496 return ldb_next_request(module, req);
3499 ldb = ldb_module_get_ctx(module);
3501 fix_links_control = ldb_request_get_control(req,
3502 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3503 if (fix_links_control != NULL) {
3504 struct dsdb_schema *schema = NULL;
3505 const struct dsdb_attribute *sa = NULL;
3507 if (req->op.mod.message->num_elements != 1) {
3508 return ldb_module_operr(module);
3511 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3512 return ldb_module_operr(module);
3515 schema = dsdb_get_schema(ldb, req);
3516 if (schema == NULL) {
3517 return ldb_module_operr(module);
3520 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3521 req->op.mod.message->elements[0].name);
3523 return ldb_module_operr(module);
3526 if (sa->linkID == 0) {
3527 return ldb_module_operr(module);
3530 fix_links_control->critical = false;
3531 return ldb_next_request(module, req);
3534 fix_dn_name_control = ldb_request_get_control(req,
3535 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3536 if (fix_dn_name_control != NULL) {
3537 struct dsdb_schema *schema = NULL;
3538 const struct dsdb_attribute *sa = NULL;
3540 if (req->op.mod.message->num_elements != 2) {
3541 return ldb_module_operr(module);
3544 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3545 return ldb_module_operr(module);
3548 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3549 return ldb_module_operr(module);
3552 if (req->op.mod.message->elements[0].num_values != 1) {
3553 return ldb_module_operr(module);
3556 if (req->op.mod.message->elements[1].num_values != 1) {
3557 return ldb_module_operr(module);
3560 schema = dsdb_get_schema(ldb, req);
3561 if (schema == NULL) {
3562 return ldb_module_operr(module);
3565 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3566 req->op.mod.message->elements[1].name) != 0) {
3567 return ldb_module_operr(module);
3570 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3571 req->op.mod.message->elements[0].name);
3573 return ldb_module_operr(module);
3576 if (sa->dn_format == DSDB_INVALID_DN) {
3577 return ldb_module_operr(module);
3580 if (sa->linkID != 0) {
3581 return ldb_module_operr(module);
3585 * If we are run from dbcheck and we are not updating
3586 * a link (as these would need to be sorted and so
3587 * can't go via such a simple update, then do not
3588 * trigger replicated updates and a new USN from this
3589 * change, it wasn't a real change, just a new
3590 * (correct) string DN
3593 fix_dn_name_control->critical = false;
3594 return ldb_next_request(module, req);
3597 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3599 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3600 if (guid_el != NULL) {
3601 ldb_set_errstring(ldb,
3602 "replmd_modify: it's not allowed to change the objectGUID!");
3603 return LDB_ERR_CONSTRAINT_VIOLATION;
3606 ac = replmd_ctx_init(module, req);
3608 return ldb_module_oom(module);
3611 functional_level = dsdb_functional_level(ldb);
3613 /* we have to copy the message as the caller might have it as a const */
3614 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3618 return LDB_ERR_OPERATIONS_ERROR;
3621 fix_dn_sid_control = ldb_request_get_control(req,
3622 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3623 if (fix_dn_sid_control != NULL) {
3624 const struct dsdb_attribute *sa = NULL;
3626 if (msg->num_elements != 1) {
3628 return ldb_module_operr(module);
3631 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3633 return ldb_module_operr(module);
3636 if (msg->elements[0].num_values != 1) {
3638 return ldb_module_operr(module);
3641 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3642 msg->elements[0].name);
3645 return ldb_module_operr(module);
3648 if (sa->dn_format != DSDB_NORMAL_DN) {
3650 return ldb_module_operr(module);
3653 fix_dn_sid_control->critical = false;
3654 ac->fix_link_sid = true;
3656 goto handle_linked_attribs;
3659 ldb_msg_remove_attr(msg, "whenChanged");
3660 ldb_msg_remove_attr(msg, "uSNChanged");
3662 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3664 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3665 msg, &ac->seq_num, t, is_schema_nc,
3667 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3668 ret = send_rodc_referral(req, ldb, msg->dn);
3674 if (ret != LDB_SUCCESS) {
3679 handle_linked_attribs:
3680 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3682 if (ret != LDB_SUCCESS) {
3688 * - replace the old object with the newly constructed one
3691 ac->is_urgent = is_urgent;
3693 ret = ldb_build_mod_req(&down_req, ldb, ac,
3696 ac, replmd_op_callback,
3698 LDB_REQ_SET_LOCATION(down_req);
3699 if (ret != LDB_SUCCESS) {
3704 /* current partition control is needed by "replmd_op_callback" */
3705 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3706 ret = ldb_request_add_control(down_req,
3707 DSDB_CONTROL_CURRENT_PARTITION_OID,
3709 if (ret != LDB_SUCCESS) {
3715 /* If we are in functional level 2000, then
3716 * replmd_modify_handle_linked_attribs will have done
3718 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3719 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3720 if (ret != LDB_SUCCESS) {
3726 talloc_steal(down_req, msg);
3728 /* we only change whenChanged and uSNChanged if the seq_num
3730 if (ac->seq_num != 0) {
3731 ret = add_time_element(msg, "whenChanged", t);
3732 if (ret != LDB_SUCCESS) {
3738 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3739 if (ret != LDB_SUCCESS) {
3746 /* go on with the call chain */
3747 return ldb_next_request(module, down_req);
3750 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3753 handle a rename request
3755 On a rename we need to do an extra ldb_modify which sets the
3756 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3758 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3760 struct ldb_context *ldb;
3761 struct replmd_replicated_request *ac;
3763 struct ldb_request *down_req;
3765 /* do not manipulate our control entries */
3766 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3767 return ldb_next_request(module, req);
3770 ldb = ldb_module_get_ctx(module);
3772 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3774 ac = replmd_ctx_init(module, req);
3776 return ldb_module_oom(module);
3779 ret = ldb_build_rename_req(&down_req, ldb, ac,
3780 ac->req->op.rename.olddn,
3781 ac->req->op.rename.newdn,
3783 ac, replmd_rename_callback,
3785 LDB_REQ_SET_LOCATION(down_req);
3786 if (ret != LDB_SUCCESS) {
3791 /* go on with the call chain */
3792 return ldb_next_request(module, down_req);
3795 /* After the rename is compleated, update the whenchanged etc */
3796 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3798 struct ldb_context *ldb;
3799 struct ldb_request *down_req;
3800 struct ldb_message *msg;
3801 const struct dsdb_attribute *rdn_attr;
3802 const char *rdn_name;
3803 const struct ldb_val *rdn_val;
3804 const char *attrs[5] = { NULL, };
3805 time_t t = time(NULL);
3807 bool is_urgent = false, rodc = false;
3809 struct replmd_replicated_request *ac =
3810 talloc_get_type(req->context, struct replmd_replicated_request);
3811 struct replmd_private *replmd_private =
3812 talloc_get_type(ldb_module_get_private(ac->module),
3813 struct replmd_private);
3815 ldb = ldb_module_get_ctx(ac->module);
3817 if (ares->error != LDB_SUCCESS) {
3818 return ldb_module_done(ac->req, ares->controls,
3819 ares->response, ares->error);
3822 if (ares->type != LDB_REPLY_DONE) {
3823 ldb_set_errstring(ldb,
3824 "invalid ldb_reply_type in callback");
3826 return ldb_module_done(ac->req, NULL, NULL,
3827 LDB_ERR_OPERATIONS_ERROR);
3831 * - replace the old object with the newly constructed one
3834 msg = ldb_msg_new(ac);
3837 return LDB_ERR_OPERATIONS_ERROR;
3840 msg->dn = ac->req->op.rename.newdn;
3842 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3844 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3845 if (rdn_name == NULL) {
3847 return ldb_module_done(ac->req, NULL, NULL,
3851 /* normalize the rdn attribute name */
3852 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3853 if (rdn_attr == NULL) {
3855 return ldb_module_done(ac->req, NULL, NULL,
3858 rdn_name = rdn_attr->lDAPDisplayName;
3860 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3861 if (rdn_val == NULL) {
3863 return ldb_module_done(ac->req, NULL, NULL,
3867 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3869 return ldb_module_done(ac->req, NULL, NULL,
3872 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3874 return ldb_module_done(ac->req, NULL, NULL,
3877 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3879 return ldb_module_done(ac->req, NULL, NULL,
3882 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3884 return ldb_module_done(ac->req, NULL, NULL,
3889 * here we let replmd_update_rpmd() only search for
3890 * the existing "replPropertyMetaData" and rdn_name attributes.
3892 * We do not want the existing "name" attribute as
3893 * the "name" attribute needs to get the version
3894 * updated on rename even if the rdn value hasn't changed.
3896 * This is the diff of the meta data, for a moved user
3897 * on a w2k8r2 server:
3900 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3901 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3902 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3903 * version : 0x00000001 (1)
3904 * reserved : 0x00000000 (0)
3905 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3906 * local_usn : 0x00000000000037a5 (14245)
3907 * array: struct replPropertyMetaData1
3908 * attid : DRSUAPI_ATTID_name (0x90001)
3909 * - version : 0x00000001 (1)
3910 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3911 * + version : 0x00000002 (2)
3912 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3913 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3914 * - originating_usn : 0x00000000000037a5 (14245)
3915 * - local_usn : 0x00000000000037a5 (14245)
3916 * + originating_usn : 0x0000000000003834 (14388)
3917 * + local_usn : 0x0000000000003834 (14388)
3918 * array: struct replPropertyMetaData1
3919 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3920 * version : 0x00000004 (4)
3922 attrs[0] = "replPropertyMetaData";
3923 attrs[1] = "objectClass";
3924 attrs[2] = "instanceType";
3925 attrs[3] = rdn_name;
3928 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3929 msg, &ac->seq_num, t,
3930 is_schema_nc, &is_urgent, &rodc);
3931 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3932 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3934 return ldb_module_done(req, NULL, NULL, ret);
3937 if (ret != LDB_SUCCESS) {
3939 return ldb_module_done(ac->req, NULL, NULL, ret);
3942 if (ac->seq_num == 0) {
3944 return ldb_module_done(ac->req, NULL, NULL,
3946 "internal error seq_num == 0"));
3948 ac->is_urgent = is_urgent;
3950 ret = ldb_build_mod_req(&down_req, ldb, ac,
3953 ac, replmd_op_callback,
3955 LDB_REQ_SET_LOCATION(down_req);
3956 if (ret != LDB_SUCCESS) {
3961 /* current partition control is needed by "replmd_op_callback" */
3962 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3963 ret = ldb_request_add_control(down_req,
3964 DSDB_CONTROL_CURRENT_PARTITION_OID,
3966 if (ret != LDB_SUCCESS) {
3972 talloc_steal(down_req, msg);
3974 ret = add_time_element(msg, "whenChanged", t);
3975 if (ret != LDB_SUCCESS) {
3981 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3982 if (ret != LDB_SUCCESS) {
3988 /* go on with the call chain - do the modify after the rename */
3989 return ldb_next_request(ac->module, down_req);
3993 * remove links from objects that point at this object when an object
3994 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3995 * RemoveObj which states that link removal due to the object being
3996 * deleted is NOT an originating update - they just go away!
3999 static int replmd_delete_remove_link(struct ldb_module *module,
4000 const struct dsdb_schema *schema,
4001 struct replmd_private *replmd_private,
4004 struct ldb_message_element *el,
4005 const struct dsdb_attribute *sa,
4006 struct ldb_request *parent)
4009 TALLOC_CTX *tmp_ctx = talloc_new(module);
4010 struct ldb_context *ldb = ldb_module_get_ctx(module);
4012 for (i=0; i<el->num_values; i++) {
4013 struct dsdb_dn *dsdb_dn;
4015 struct ldb_message *msg;
4016 const struct dsdb_attribute *target_attr;
4017 struct ldb_message_element *el2;
4019 struct ldb_val dn_val;
4020 uint32_t dsdb_flags = 0;
4021 const char *attrs[] = { NULL, NULL };
4022 struct ldb_result *link_res;
4023 struct ldb_message *link_msg;
4024 struct ldb_message_element *link_el;
4025 struct parsed_dn *link_dns;
4026 struct parsed_dn *p = NULL, *unused = NULL;
4028 if (dsdb_dn_is_deleted_val(&el->values[i])) {
4032 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4034 talloc_free(tmp_ctx);
4035 return LDB_ERR_OPERATIONS_ERROR;
4038 /* remove the link */
4039 msg = ldb_msg_new(tmp_ctx);
4041 ldb_module_oom(module);
4042 talloc_free(tmp_ctx);
4043 return LDB_ERR_OPERATIONS_ERROR;
4047 msg->dn = dsdb_dn->dn;
4049 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4050 if (target_attr == NULL) {
4053 attrs[0] = target_attr->lDAPDisplayName;
4055 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4056 LDB_FLAG_MOD_DELETE, &el2);
4057 if (ret != LDB_SUCCESS) {
4058 ldb_module_oom(module);
4059 talloc_free(tmp_ctx);
4060 return LDB_ERR_OPERATIONS_ERROR;
4063 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4065 DSDB_FLAG_NEXT_MODULE |
4066 DSDB_SEARCH_SHOW_EXTENDED_DN |
4067 DSDB_SEARCH_SHOW_RECYCLED,
4070 if (ret != LDB_SUCCESS) {
4071 talloc_free(tmp_ctx);
4075 link_msg = link_res->msgs[0];
4076 link_el = ldb_msg_find_element(link_msg,
4077 target_attr->lDAPDisplayName);
4078 if (link_el == NULL) {
4079 talloc_free(tmp_ctx);
4080 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4084 * This call 'upgrades' the links in link_dns, but we
4085 * do not commit the result back into the database, so
4086 * this is safe to call in FL2000 or on databases that
4087 * have been run at that level in the past.
4089 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
4091 target_attr->syntax->ldap_oid, parent);
4092 if (ret != LDB_SUCCESS) {
4093 talloc_free(tmp_ctx);
4097 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4101 target_attr->syntax->ldap_oid, false);
4102 if (ret != LDB_SUCCESS) {
4103 talloc_free(tmp_ctx);
4108 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4109 "Failed to find forward link on %s "
4110 "as %s to remove backlink %s on %s",
4111 ldb_dn_get_linearized(msg->dn),
4112 target_attr->lDAPDisplayName,
4113 sa->lDAPDisplayName,
4114 ldb_dn_get_linearized(dn));
4115 talloc_free(tmp_ctx);
4116 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4120 /* This needs to get the Binary DN, by first searching */
4121 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4124 dn_val = data_blob_string_const(dn_str);
4125 el2->values = &dn_val;
4126 el2->num_values = 1;
4129 * Ensure that we tell the modification to vanish any linked
4130 * attributes (not simply mark them as isDeleted = TRUE)
4132 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4134 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4135 if (ret != LDB_SUCCESS) {
4136 talloc_free(tmp_ctx);
4140 talloc_free(tmp_ctx);
4146 handle update of replication meta data for deletion of objects
4148 This also handles the mapping of delete to a rename operation
4149 to allow deletes to be replicated.
4151 It also handles the incoming deleted objects, to ensure they are
4152 fully deleted here. In that case re_delete is true, and we do not
4153 use this as a signal to change the deleted state, just reinforce it.
4156 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4158 int ret = LDB_ERR_OTHER;
4159 bool retb, disallow_move_on_delete;
4160 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4161 const char *rdn_name;
4162 const struct ldb_val *rdn_value, *new_rdn_value;
4164 struct ldb_context *ldb = ldb_module_get_ctx(module);
4165 const struct dsdb_schema *schema;
4166 struct ldb_message *msg, *old_msg;
4167 struct ldb_message_element *el;
4168 TALLOC_CTX *tmp_ctx;
4169 struct ldb_result *res, *parent_res;
4170 static const char * const preserved_attrs[] = {
4171 /* yes, this really is a hard coded list. See MS-ADTS
4172 section 3.1.1.5.5.1.1 */
4175 "dNReferenceUpdate",
4186 "msDS-LastKnownRDN",
4192 "distinguishedName",
4196 "proxiedObjectName",
4198 "nTSecurityDescriptor",
4199 "replPropertyMetaData",
4201 "securityIdentifier",
4209 "userAccountControl",
4216 static const char * const all_attrs[] = {
4217 DSDB_SECRET_ATTRIBUTES,
4221 static const struct ldb_val true_val = {
4222 .data = discard_const_p(uint8_t, "TRUE"),
4227 uint32_t dsdb_flags = 0;
4228 struct replmd_private *replmd_private;
4229 enum deletion_state deletion_state, next_deletion_state;
4231 if (ldb_dn_is_special(req->op.del.dn)) {
4232 return ldb_next_request(module, req);
4236 * We have to allow dbcheck to remove an object that
4237 * is beyond repair, and to do so totally. This could
4238 * mean we we can get a partial object from the other
4239 * DC, causing havoc, so dbcheck suggests
4240 * re-replication first. dbcheck sets both DBCHECK
4241 * and RELAX in this situation.
4243 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4244 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4245 /* really, really remove it */
4246 return ldb_next_request(module, req);
4249 tmp_ctx = talloc_new(ldb);
4252 return LDB_ERR_OPERATIONS_ERROR;
4255 schema = dsdb_get_schema(ldb, tmp_ctx);
4257 talloc_free(tmp_ctx);
4258 return LDB_ERR_OPERATIONS_ERROR;
4261 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4263 /* we need the complete msg off disk, so we can work out which
4264 attributes need to be removed */
4265 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4266 DSDB_FLAG_NEXT_MODULE |
4267 DSDB_SEARCH_SHOW_RECYCLED |
4268 DSDB_SEARCH_REVEAL_INTERNALS |
4269 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4270 if (ret != LDB_SUCCESS) {
4271 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4272 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4273 re_delete ? "re-delete" : "delete",
4274 ldb_dn_get_linearized(old_dn),
4275 ldb_errstring(ldb_module_get_ctx(module)));
4276 talloc_free(tmp_ctx);
4279 old_msg = res->msgs[0];
4281 replmd_deletion_state(module, old_msg,
4283 &next_deletion_state);
4285 /* This supports us noticing an incoming isDeleted and acting on it */
4287 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4288 next_deletion_state = deletion_state;
4291 if (next_deletion_state == OBJECT_REMOVED) {
4293 * We have to prevent objects being deleted, even if
4294 * the administrator really wants them gone, as
4295 * without the tombstone, we can get a partial object
4296 * from the other DC, causing havoc.
4298 * The only other valid case is when the 180 day
4299 * timeout has expired, when relax is specified.
4301 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4302 /* it is already deleted - really remove it this time */
4303 talloc_free(tmp_ctx);
4304 return ldb_next_request(module, req);
4307 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4308 "This check is to prevent corruption of the replicated state.",
4309 ldb_dn_get_linearized(old_msg->dn));
4310 return LDB_ERR_UNWILLING_TO_PERFORM;
4313 rdn_name = ldb_dn_get_rdn_name(old_dn);
4314 rdn_value = ldb_dn_get_rdn_val(old_dn);
4315 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4316 talloc_free(tmp_ctx);
4317 return ldb_operr(ldb);
4320 msg = ldb_msg_new(tmp_ctx);
4322 ldb_module_oom(module);
4323 talloc_free(tmp_ctx);
4324 return LDB_ERR_OPERATIONS_ERROR;
4329 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4330 disallow_move_on_delete =
4331 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4332 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4334 /* work out where we will be renaming this object to */
4335 if (!disallow_move_on_delete) {
4336 struct ldb_dn *deleted_objects_dn;
4337 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4338 &deleted_objects_dn);
4341 * We should not move objects if we can't find the
4342 * deleted objects DN. Not moving (or otherwise
4343 * harming) the Deleted Objects DN itself is handled
4346 if (re_delete && (ret != LDB_SUCCESS)) {
4347 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4348 if (new_dn == NULL) {
4349 ldb_module_oom(module);
4350 talloc_free(tmp_ctx);
4351 return LDB_ERR_OPERATIONS_ERROR;
4353 } else if (ret != LDB_SUCCESS) {
4354 /* this is probably an attempted delete on a partition
4355 * that doesn't allow delete operations, such as the
4356 * schema partition */
4357 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4358 ldb_dn_get_linearized(old_dn));
4359 talloc_free(tmp_ctx);
4360 return LDB_ERR_UNWILLING_TO_PERFORM;
4362 new_dn = deleted_objects_dn;
4365 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4366 if (new_dn == NULL) {
4367 ldb_module_oom(module);
4368 talloc_free(tmp_ctx);
4369 return LDB_ERR_OPERATIONS_ERROR;
4373 /* get the objects GUID from the search we just did */
4374 guid = samdb_result_guid(old_msg, "objectGUID");
4376 if (deletion_state == OBJECT_NOT_DELETED) {
4377 struct ldb_message_element *is_deleted_el;
4379 ret = replmd_make_deleted_child_dn(tmp_ctx,
4382 rdn_name, rdn_value,
4385 if (ret != LDB_SUCCESS) {
4386 talloc_free(tmp_ctx);
4390 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4392 if (ret != LDB_SUCCESS) {
4393 ldb_asprintf_errstring(ldb, __location__
4394 ": Failed to add isDeleted string to the msg");
4395 talloc_free(tmp_ctx);
4398 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4401 * No matter what has happened with other renames etc, try again to
4402 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4405 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4406 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4408 ldb_asprintf_errstring(ldb, __location__
4409 ": Unable to add a prepare rdn of %s",
4410 ldb_dn_get_linearized(rdn));
4411 talloc_free(tmp_ctx);
4412 return LDB_ERR_OPERATIONS_ERROR;
4414 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4416 retb = ldb_dn_add_child(new_dn, rdn);
4418 ldb_asprintf_errstring(ldb, __location__
4419 ": Unable to add rdn %s to base dn: %s",
4420 ldb_dn_get_linearized(rdn),
4421 ldb_dn_get_linearized(new_dn));
4422 talloc_free(tmp_ctx);
4423 return LDB_ERR_OPERATIONS_ERROR;
4428 now we need to modify the object in the following ways:
4430 - add isDeleted=TRUE
4431 - update rDN and name, with new rDN
4432 - remove linked attributes
4433 - remove objectCategory and sAMAccountType
4434 - remove attribs not on the preserved list
4435 - preserved if in above list, or is rDN
4436 - remove all linked attribs from this object
4437 - remove all links from other objects to this object
4438 (note we use the backlinks to do this, so we won't find one-way
4439 links that still point to this object, or deactivated two-way
4440 links, i.e. 'member' after the user has been removed from the
4442 - add lastKnownParent
4443 - update replPropertyMetaData?
4445 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4448 if (deletion_state == OBJECT_NOT_DELETED) {
4449 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4450 char *parent_dn_str = NULL;
4451 struct ldb_message_element *p_el;
4453 /* we need the storage form of the parent GUID */
4454 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4456 DSDB_FLAG_NEXT_MODULE |
4457 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4458 DSDB_SEARCH_REVEAL_INTERNALS|
4459 DSDB_SEARCH_SHOW_RECYCLED, req);
4460 if (ret != LDB_SUCCESS) {
4461 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4462 "repmd_delete: Failed to %s %s, "
4463 "because we failed to find it's parent (%s): %s",
4464 re_delete ? "re-delete" : "delete",
4465 ldb_dn_get_linearized(old_dn),
4466 ldb_dn_get_linearized(parent_dn),
4467 ldb_errstring(ldb_module_get_ctx(module)));
4468 talloc_free(tmp_ctx);
4473 * Now we can use the DB version,
4474 * it will have the extended DN info in it
4476 parent_dn = parent_res->msgs[0]->dn;
4477 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4480 if (parent_dn_str == NULL) {
4481 talloc_free(tmp_ctx);
4482 return ldb_module_oom(module);
4485 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4487 if (ret != LDB_SUCCESS) {
4488 ldb_asprintf_errstring(ldb, __location__
4489 ": Failed to add lastKnownParent "
4490 "string when deleting %s",
4491 ldb_dn_get_linearized(old_dn));
4492 talloc_free(tmp_ctx);
4495 p_el = ldb_msg_find_element(msg,
4498 talloc_free(tmp_ctx);
4499 return ldb_module_operr(module);
4501 p_el->flags = LDB_FLAG_MOD_REPLACE;
4503 if (next_deletion_state == OBJECT_DELETED) {
4504 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4505 if (ret != LDB_SUCCESS) {
4506 ldb_asprintf_errstring(ldb, __location__
4507 ": Failed to add msDS-LastKnownRDN "
4508 "string when deleting %s",
4509 ldb_dn_get_linearized(old_dn));
4510 talloc_free(tmp_ctx);
4513 p_el = ldb_msg_find_element(msg,
4514 "msDS-LastKnownRDN");
4516 talloc_free(tmp_ctx);
4517 return ldb_module_operr(module);
4519 p_el->flags = LDB_FLAG_MOD_ADD;
4523 switch (next_deletion_state) {
4525 case OBJECT_RECYCLED:
4526 case OBJECT_TOMBSTONE:
4529 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4530 * describes what must be removed from a tombstone
4533 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4534 * describes what must be removed from a recycled
4540 * we also mark it as recycled, meaning this object can't be
4541 * recovered (we are stripping its attributes).
4542 * This is done only if we have this schema object of course ...
4543 * This behavior is identical to the one of Windows 2008R2 which
4544 * always set the isRecycled attribute, even if the recycle-bin is
4545 * not activated and what ever the forest level is.
4547 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4548 struct ldb_message_element *is_recycled_el;
4550 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4552 if (ret != LDB_SUCCESS) {
4553 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4554 ldb_module_oom(module);
4555 talloc_free(tmp_ctx);
4558 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4561 replmd_private = talloc_get_type(ldb_module_get_private(module),
4562 struct replmd_private);
4563 /* work out which of the old attributes we will be removing */
4564 for (i=0; i<old_msg->num_elements; i++) {
4565 const struct dsdb_attribute *sa;
4566 el = &old_msg->elements[i];
4567 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4569 talloc_free(tmp_ctx);
4570 return LDB_ERR_OPERATIONS_ERROR;
4572 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4573 /* don't remove the rDN */
4577 if (sa->linkID & 1) {
4579 * we have a backlink in this object
4580 * that needs to be removed. We're not
4581 * allowed to remove it directly
4582 * however, so we instead setup a
4583 * modify to delete the corresponding
4586 ret = replmd_delete_remove_link(module, schema,
4590 if (ret == LDB_SUCCESS) {
4592 * now we continue, which means we
4593 * won't remove this backlink
4599 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4600 const char *old_dn_str
4601 = ldb_dn_get_linearized(old_dn);
4602 ldb_asprintf_errstring(ldb,
4604 ": Failed to remove backlink of "
4605 "%s when deleting %s: %s",
4608 ldb_errstring(ldb));
4609 talloc_free(tmp_ctx);
4610 return LDB_ERR_OPERATIONS_ERROR;
4614 * Otherwise vanish the link, we are
4615 * out of sync and the controlling
4616 * object does not have the source
4620 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4622 } else if (sa->linkID == 0) {
4623 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4626 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4631 * Ensure that we tell the modification to vanish any linked
4632 * attributes (not simply mark them as isDeleted = TRUE)
4634 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4636 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4637 if (ret != LDB_SUCCESS) {
4638 talloc_free(tmp_ctx);
4639 ldb_module_oom(module);
4646 case OBJECT_DELETED:
4648 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4649 * describes what must be removed from a deleted
4653 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4654 if (ret != LDB_SUCCESS) {
4655 talloc_free(tmp_ctx);
4656 ldb_module_oom(module);
4660 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4661 if (ret != LDB_SUCCESS) {
4662 talloc_free(tmp_ctx);
4663 ldb_module_oom(module);
4673 if (deletion_state == OBJECT_NOT_DELETED) {
4674 const struct dsdb_attribute *sa;
4676 /* work out what the new rdn value is, for updating the
4677 rDN and name fields */
4678 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4679 if (new_rdn_value == NULL) {
4680 talloc_free(tmp_ctx);
4681 return ldb_operr(ldb);
4684 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4686 talloc_free(tmp_ctx);
4687 return LDB_ERR_OPERATIONS_ERROR;
4690 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4692 if (ret != LDB_SUCCESS) {
4693 talloc_free(tmp_ctx);
4696 el->flags = LDB_FLAG_MOD_REPLACE;
4698 el = ldb_msg_find_element(old_msg, "name");
4700 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4701 if (ret != LDB_SUCCESS) {
4702 talloc_free(tmp_ctx);
4705 el->flags = LDB_FLAG_MOD_REPLACE;
4710 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4715 * No matter what has happned with other renames, try again to
4716 * get this to be under the deleted DN.
4718 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4719 /* now rename onto the new DN */
4720 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4721 if (ret != LDB_SUCCESS){
4722 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4723 ldb_dn_get_linearized(old_dn),
4724 ldb_dn_get_linearized(new_dn),
4725 ldb_errstring(ldb)));
4726 talloc_free(tmp_ctx);
4732 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4733 if (ret != LDB_SUCCESS) {
4734 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4735 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4736 talloc_free(tmp_ctx);
4740 talloc_free(tmp_ctx);
4742 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4745 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4747 return replmd_delete_internals(module, req, false);
4751 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4756 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4758 int ret = LDB_ERR_OTHER;
4759 /* TODO: do some error mapping */
4761 /* Let the caller know the full WERROR */
4762 ar->objs->error = status;
4768 static struct replPropertyMetaData1 *
4769 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4770 enum drsuapi_DsAttributeId attid)
4773 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4775 for (i = 0; i < rpmd_ctr->count; i++) {
4776 if (rpmd_ctr->array[i].attid == attid) {
4777 return &rpmd_ctr->array[i];
4785 return true if an update is newer than an existing entry
4786 see section 5.11 of MS-ADTS
4788 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4789 const struct GUID *update_invocation_id,
4790 uint32_t current_version,
4791 uint32_t update_version,
4792 NTTIME current_change_time,
4793 NTTIME update_change_time)
4795 if (update_version != current_version) {
4796 return update_version > current_version;
4798 if (update_change_time != current_change_time) {
4799 return update_change_time > current_change_time;
4801 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4804 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4805 struct replPropertyMetaData1 *new_m)
4807 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4808 &new_m->originating_invocation_id,
4811 cur_m->originating_change_time,
4812 new_m->originating_change_time);
4815 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4816 struct replPropertyMetaData1 *cur_m,
4817 struct replPropertyMetaData1 *new_m)
4822 * If the new replPropertyMetaData entry for this attribute is
4823 * not provided (this happens in the case where we look for
4824 * ATTID_name, but the name was not changed), then the local
4825 * state is clearly still current, as the remote
4826 * server didn't send it due to being older the high watermark
4829 if (new_m == NULL) {
4833 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4835 * if we compare equal then do an
4836 * update. This is used when a client
4837 * asks for a FULL_SYNC, and can be
4838 * used to recover a corrupt
4841 * This call is a bit tricky, what we
4842 * are doing it turning the 'is_newer'
4843 * call into a 'not is older' by
4844 * swapping cur_m and new_m, and negating the
4847 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4850 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4858 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4860 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4861 struct ldb_context *ldb,
4863 const char *four_char_prefix,
4864 const char *rdn_name,
4865 const struct ldb_val *rdn_value,
4868 struct ldb_val deleted_child_rdn_val;
4869 struct GUID_txt_buf guid_str;
4873 GUID_buf_string(&guid, &guid_str);
4875 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4877 ldb_asprintf_errstring(ldb, __location__
4878 ": Unable to add a formatted child to dn: %s",
4879 ldb_dn_get_linearized(dn));
4880 return LDB_ERR_OPERATIONS_ERROR;
4884 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4885 * we should truncate this value to ensure the RDN is not more than 255 chars.
4887 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4889 * "Naming constraints are not enforced for replicated
4890 * updates." so this is safe and we don't have to work out not
4891 * splitting a UTF8 char right now.
4893 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4896 * sizeof(guid_str.buf) will always be longer than
4897 * strlen(guid_str.buf) but we allocate using this and
4898 * waste the trailing bytes to avoid scaring folks
4899 * with memcpy() using strlen() below
4902 deleted_child_rdn_val.data
4903 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4905 rdn_value->length + 5
4906 + sizeof(guid_str.buf));
4907 if (!deleted_child_rdn_val.data) {
4908 ldb_asprintf_errstring(ldb, __location__
4909 ": Unable to add a formatted child to dn: %s",
4910 ldb_dn_get_linearized(dn));
4911 return LDB_ERR_OPERATIONS_ERROR;
4914 deleted_child_rdn_val.length =
4915 rdn_value->length + 5
4916 + strlen(guid_str.buf);
4918 SMB_ASSERT(deleted_child_rdn_val.length <
4919 talloc_get_size(deleted_child_rdn_val.data));
4922 * talloc won't allocate more than 256MB so we can't
4923 * overflow but just to be sure
4925 if (deleted_child_rdn_val.length < rdn_value->length) {
4926 return LDB_ERR_OPERATIONS_ERROR;
4929 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4930 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4931 four_char_prefix, 4);
4932 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4934 sizeof(guid_str.buf));
4936 /* Now set the value into the RDN, without parsing it */
4937 ret = ldb_dn_set_component(
4941 deleted_child_rdn_val);
4950 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4951 struct ldb_context *ldb,
4955 const struct ldb_val *rdn_val;
4956 const char *rdn_name;
4957 struct ldb_dn *new_dn;
4960 rdn_val = ldb_dn_get_rdn_val(dn);
4961 rdn_name = ldb_dn_get_rdn_name(dn);
4962 if (!rdn_val || !rdn_name) {
4966 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4971 ret = replmd_make_prefix_child_dn(mem_ctx,
4977 if (ret != LDB_SUCCESS) {
4986 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4987 struct ldb_context *ldb,
4989 const char *rdn_name,
4990 const struct ldb_val *rdn_value,
4993 return replmd_make_prefix_child_dn(tmp_ctx,
5003 perform a modify operation which sets the rDN and name attributes to
5004 their current values. This has the effect of changing these
5005 attributes to have been last updated by the current DC. This is
5006 needed to ensure that renames performed as part of conflict
5007 resolution are propagated to other DCs
5009 static int replmd_name_modify(struct replmd_replicated_request *ar,
5010 struct ldb_request *req, struct ldb_dn *dn)
5012 struct ldb_message *msg;
5013 const char *rdn_name;
5014 const struct ldb_val *rdn_val;
5015 const struct dsdb_attribute *rdn_attr;
5018 msg = ldb_msg_new(req);
5024 rdn_name = ldb_dn_get_rdn_name(dn);
5025 if (rdn_name == NULL) {
5029 /* normalize the rdn attribute name */
5030 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5031 if (rdn_attr == NULL) {
5034 rdn_name = rdn_attr->lDAPDisplayName;
5036 rdn_val = ldb_dn_get_rdn_val(dn);
5037 if (rdn_val == NULL) {
5041 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5044 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
5047 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5050 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
5055 * We have to mark this as a replicated update otherwise
5056 * schema_data may reject a rename in the schema partition
5059 ret = dsdb_module_modify(ar->module, msg,
5060 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5062 if (ret != LDB_SUCCESS) {
5063 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5064 ldb_dn_get_linearized(dn),
5065 ldb_errstring(ldb_module_get_ctx(ar->module))));
5075 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5076 ldb_dn_get_linearized(dn)));
5077 return LDB_ERR_OPERATIONS_ERROR;
5082 callback for conflict DN handling where we have renamed the incoming
5083 record. After renaming it, we need to ensure the change of name and
5084 rDN for the incoming record is seen as an originating update by this DC.
5086 This also handles updating lastKnownParent for entries sent to lostAndFound
5088 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5090 struct replmd_replicated_request *ar =
5091 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5092 struct ldb_dn *conflict_dn = NULL;
5095 if (ares->error != LDB_SUCCESS) {
5096 /* call the normal callback for everything except success */
5097 return replmd_op_callback(req, ares);
5100 switch (req->operation) {
5102 conflict_dn = req->op.add.message->dn;
5105 conflict_dn = req->op.mod.message->dn;
5108 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5111 /* perform a modify of the rDN and name of the record */
5112 ret = replmd_name_modify(ar, req, conflict_dn);
5113 if (ret != LDB_SUCCESS) {
5115 return replmd_op_callback(req, ares);
5118 if (ar->objs->objects[ar->index_current].last_known_parent) {
5119 struct ldb_message *msg = ldb_msg_new(req);
5121 ldb_module_oom(ar->module);
5122 return LDB_ERR_OPERATIONS_ERROR;
5125 msg->dn = req->op.add.message->dn;
5127 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5128 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5129 if (ret != LDB_SUCCESS) {
5130 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5131 ldb_module_oom(ar->module);
5134 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5136 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5137 if (ret != LDB_SUCCESS) {
5138 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5139 ldb_dn_get_linearized(msg->dn),
5140 ldb_errstring(ldb_module_get_ctx(ar->module))));
5146 return replmd_op_callback(req, ares);
5150 callback for replmd_replicated_apply_add()
5151 This copes with the creation of conflict records in the case where
5152 the DN exists, but with a different objectGUID
5154 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))
5156 struct ldb_dn *conflict_dn;
5157 struct replmd_replicated_request *ar =
5158 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5159 struct ldb_result *res;
5160 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5162 const struct ldb_val *omd_value;
5163 struct replPropertyMetaDataBlob omd, *rmd;
5164 enum ndr_err_code ndr_err;
5165 bool rename_incoming_record, rodc;
5166 struct replPropertyMetaData1 *rmd_name, *omd_name;
5167 struct ldb_message *msg;
5168 struct ldb_request *down_req = NULL;
5170 /* call the normal callback for success */
5171 if (ares->error == LDB_SUCCESS) {
5172 return callback(req, ares);
5176 * we have a conflict, and need to decide if we will keep the
5177 * new record or the old record
5180 msg = ar->objs->objects[ar->index_current].msg;
5181 conflict_dn = msg->dn;
5183 /* For failures other than conflicts, fail the whole operation here */
5184 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5185 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5186 ldb_dn_get_linearized(conflict_dn),
5187 ldb_errstring(ldb_module_get_ctx(ar->module)));
5189 return ldb_module_done(ar->req, NULL, NULL,
5190 LDB_ERR_OPERATIONS_ERROR);
5193 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5194 if (ret != LDB_SUCCESS) {
5195 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)));
5196 return ldb_module_done(ar->req, NULL, NULL,
5197 LDB_ERR_OPERATIONS_ERROR);
5203 * We are on an RODC, or were a GC for this
5204 * partition, so we have to fail this until
5205 * someone who owns the partition sorts it
5208 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5209 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5210 " - We must fail the operation until a master for this partition resolves the conflict",
5211 ldb_dn_get_linearized(conflict_dn));
5212 ret = LDB_ERR_OPERATIONS_ERROR;
5217 * first we need the replPropertyMetaData attribute from the
5218 * local, conflicting record
5220 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5222 DSDB_FLAG_NEXT_MODULE |
5223 DSDB_SEARCH_SHOW_DELETED |
5224 DSDB_SEARCH_SHOW_RECYCLED, req);
5225 if (ret != LDB_SUCCESS) {
5226 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5227 ldb_dn_get_linearized(conflict_dn)));
5231 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5232 if (omd_value == NULL) {
5233 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5234 ldb_dn_get_linearized(conflict_dn)));
5238 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5239 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5240 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5241 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5242 ldb_dn_get_linearized(conflict_dn)));
5246 rmd = ar->objs->objects[ar->index_current].meta_data;
5249 * we decide which is newer based on the RPMD on the name
5250 * attribute. See [MS-DRSR] ResolveNameConflict.
5252 * We expect omd_name to be present, as this is from a local
5253 * search, but while rmd_name should have been given to us by
5254 * the remote server, if it is missing we just prefer the
5256 * replmd_replPropertyMetaData1_new_should_be_taken()
5258 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5259 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5261 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5262 ldb_dn_get_linearized(conflict_dn)));
5267 * Should we preserve the current record, and so rename the
5268 * incoming record to be a conflict?
5270 rename_incoming_record
5271 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5272 omd_name, rmd_name);
5274 if (rename_incoming_record) {
5276 struct ldb_dn *new_dn;
5278 guid = samdb_result_guid(msg, "objectGUID");
5279 if (GUID_all_zero(&guid)) {
5280 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5281 ldb_dn_get_linearized(conflict_dn)));
5284 new_dn = replmd_conflict_dn(req,
5285 ldb_module_get_ctx(ar->module),
5286 conflict_dn, &guid);
5287 if (new_dn == NULL) {
5288 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5289 ldb_dn_get_linearized(conflict_dn)));
5293 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5294 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5296 /* re-submit the request, but with the new DN */
5297 callback = replmd_op_name_modify_callback;
5300 /* we are renaming the existing record */
5302 struct ldb_dn *new_dn;
5304 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5305 if (GUID_all_zero(&guid)) {
5306 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5307 ldb_dn_get_linearized(conflict_dn)));
5311 new_dn = replmd_conflict_dn(req,
5312 ldb_module_get_ctx(ar->module),
5313 conflict_dn, &guid);
5314 if (new_dn == NULL) {
5315 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5316 ldb_dn_get_linearized(conflict_dn)));
5320 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5321 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5323 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5324 DSDB_FLAG_OWN_MODULE, req);
5325 if (ret != LDB_SUCCESS) {
5326 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5327 ldb_dn_get_linearized(conflict_dn),
5328 ldb_dn_get_linearized(new_dn),
5329 ldb_errstring(ldb_module_get_ctx(ar->module))));
5334 * now we need to ensure that the rename is seen as an
5335 * originating update. We do that with a modify.
5337 ret = replmd_name_modify(ar, req, new_dn);
5338 if (ret != LDB_SUCCESS) {
5342 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5343 ldb_dn_get_linearized(req->op.add.message->dn)));
5346 ret = ldb_build_add_req(&down_req,
5347 ldb_module_get_ctx(ar->module),
5354 if (ret != LDB_SUCCESS) {
5357 LDB_REQ_SET_LOCATION(down_req);
5359 /* current partition control needed by "repmd_op_callback" */
5360 ret = ldb_request_add_control(down_req,
5361 DSDB_CONTROL_CURRENT_PARTITION_OID,
5363 if (ret != LDB_SUCCESS) {
5364 return replmd_replicated_request_error(ar, ret);
5367 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5368 /* this tells the partition module to make it a
5369 partial replica if creating an NC */
5370 ret = ldb_request_add_control(down_req,
5371 DSDB_CONTROL_PARTIAL_REPLICA,
5373 if (ret != LDB_SUCCESS) {
5374 return replmd_replicated_request_error(ar, ret);
5379 * Finally we re-run the add, otherwise the new record won't
5380 * exist, as we are here because of that exact failure!
5382 return ldb_next_request(ar->module, down_req);
5385 /* on failure make the caller get the error. This means
5386 * replication will stop with an error, but there is not much
5389 if (ret == LDB_SUCCESS) {
5390 ret = LDB_ERR_OPERATIONS_ERROR;
5392 return ldb_module_done(ar->req, NULL, NULL,
5397 callback for replmd_replicated_apply_add()
5398 This copes with the creation of conflict records in the case where
5399 the DN exists, but with a different objectGUID
5401 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5403 struct replmd_replicated_request *ar =
5404 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5406 if (ar->objs->objects[ar->index_current].last_known_parent) {
5407 /* This is like a conflict DN, where we put the object in LostAndFound
5408 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5409 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5412 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5416 this is called when a new object comes in over DRS
5418 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5420 struct ldb_context *ldb;
5421 struct ldb_request *change_req;
5422 enum ndr_err_code ndr_err;
5423 struct ldb_message *msg;
5424 struct replPropertyMetaDataBlob *md;
5425 struct ldb_val md_value;
5428 bool remote_isDeleted = false;
5431 time_t t = time(NULL);
5432 const struct ldb_val *rdn_val;
5433 struct replmd_private *replmd_private =
5434 talloc_get_type(ldb_module_get_private(ar->module),
5435 struct replmd_private);
5436 unix_to_nt_time(&now, t);
5438 ldb = ldb_module_get_ctx(ar->module);
5439 msg = ar->objs->objects[ar->index_current].msg;
5440 md = ar->objs->objects[ar->index_current].meta_data;
5441 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5443 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5444 if (ret != LDB_SUCCESS) {
5445 return replmd_replicated_request_error(ar, ret);
5448 ret = dsdb_msg_add_guid(msg,
5449 &ar->objs->objects[ar->index_current].object_guid,
5451 if (ret != LDB_SUCCESS) {
5452 return replmd_replicated_request_error(ar, ret);
5455 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5456 if (ret != LDB_SUCCESS) {
5457 return replmd_replicated_request_error(ar, ret);
5460 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5461 if (ret != LDB_SUCCESS) {
5462 return replmd_replicated_request_error(ar, ret);
5465 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5466 if (ret != LDB_SUCCESS) {
5467 return replmd_replicated_request_error(ar, ret);
5470 /* remove any message elements that have zero values */
5471 for (i=0; i<msg->num_elements; i++) {
5472 struct ldb_message_element *el = &msg->elements[i];
5474 if (el->num_values == 0) {
5475 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5476 ldb_asprintf_errstring(ldb, __location__
5477 ": empty objectClass sent on %s, aborting replication\n",
5478 ldb_dn_get_linearized(msg->dn));
5479 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5482 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5484 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5485 msg->num_elements--;
5492 struct GUID_txt_buf guid_txt;
5494 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5497 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5498 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5501 } else if (DEBUGLVL(4)) {
5502 struct GUID_txt_buf guid_txt;
5503 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5504 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5505 ldb_dn_get_linearized(msg->dn)));
5507 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5508 "isDeleted", false);
5511 * the meta data array is already sorted by the caller, except
5512 * for the RDN, which needs to be added.
5516 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5517 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5518 md, ar, now, is_schema_nc,
5520 if (ret != LDB_SUCCESS) {
5521 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5522 return replmd_replicated_request_error(ar, ret);
5525 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5526 if (ret != LDB_SUCCESS) {
5527 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5528 return replmd_replicated_request_error(ar, ret);
5531 for (i=0; i < md->ctr.ctr1.count; i++) {
5532 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5534 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5535 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5536 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5537 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5538 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5540 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5541 if (ret != LDB_SUCCESS) {
5542 return replmd_replicated_request_error(ar, ret);
5545 replmd_ldb_message_sort(msg, ar->schema);
5547 if (!remote_isDeleted) {
5548 ret = dsdb_module_schedule_sd_propagation(ar->module,
5549 ar->objs->partition_dn,
5551 if (ret != LDB_SUCCESS) {
5552 return replmd_replicated_request_error(ar, ret);
5556 ar->isDeleted = remote_isDeleted;
5558 ret = ldb_build_add_req(&change_req,
5564 replmd_op_add_callback,
5566 LDB_REQ_SET_LOCATION(change_req);
5567 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5569 /* current partition control needed by "repmd_op_callback" */
5570 ret = ldb_request_add_control(change_req,
5571 DSDB_CONTROL_CURRENT_PARTITION_OID,
5573 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5575 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5576 /* this tells the partition module to make it a
5577 partial replica if creating an NC */
5578 ret = ldb_request_add_control(change_req,
5579 DSDB_CONTROL_PARTIAL_REPLICA,
5581 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5584 return ldb_next_request(ar->module, change_req);
5587 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5588 struct ldb_reply *ares)
5590 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5591 struct replmd_replicated_request);
5595 return ldb_module_done(ar->req, NULL, NULL,
5596 LDB_ERR_OPERATIONS_ERROR);
5600 * The error NO_SUCH_OBJECT is not expected, unless the search
5601 * base is the partition DN, and that case doesn't happen here
5602 * because then we wouldn't get a parent_guid_value in any
5605 if (ares->error != LDB_SUCCESS) {
5606 return ldb_module_done(ar->req, ares->controls,
5607 ares->response, ares->error);
5610 switch (ares->type) {
5611 case LDB_REPLY_ENTRY:
5613 struct ldb_message *parent_msg = ares->message;
5614 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5615 struct ldb_dn *parent_dn = NULL;
5618 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5619 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5620 /* Per MS-DRSR 4.1.10.6.10
5621 * FindBestParentObject we need to move this
5622 * new object under a deleted object to
5624 struct ldb_dn *nc_root;
5626 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5627 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5628 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5629 "No suitable NC root found for %s. "
5630 "We need to move this object because parent object %s "
5631 "is deleted, but this object is not.",
5632 ldb_dn_get_linearized(msg->dn),
5633 ldb_dn_get_linearized(parent_msg->dn));
5634 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5635 } else if (ret != LDB_SUCCESS) {
5636 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5637 "Unable to find NC root for %s: %s. "
5638 "We need to move this object because parent object %s "
5639 "is deleted, but this object is not.",
5640 ldb_dn_get_linearized(msg->dn),
5641 ldb_errstring(ldb_module_get_ctx(ar->module)),
5642 ldb_dn_get_linearized(parent_msg->dn));
5643 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5646 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5648 DS_GUID_LOSTANDFOUND_CONTAINER,
5650 if (ret != LDB_SUCCESS) {
5651 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5652 "Unable to find LostAndFound Container for %s "
5653 "in partition %s: %s. "
5654 "We need to move this object because parent object %s "
5655 "is deleted, but this object is not.",
5656 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5657 ldb_errstring(ldb_module_get_ctx(ar->module)),
5658 ldb_dn_get_linearized(parent_msg->dn));
5659 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5661 ar->objs->objects[ar->index_current].last_known_parent
5662 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5666 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5669 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5671 comp_num = ldb_dn_get_comp_num(msg->dn);
5673 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5675 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5678 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5680 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5684 case LDB_REPLY_REFERRAL:
5685 /* we ignore referrals */
5688 case LDB_REPLY_DONE:
5690 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5691 struct GUID_txt_buf str_buf;
5692 if (ar->search_msg != NULL) {
5693 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5694 "No parent with GUID %s found for object locally known as %s",
5695 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5696 ldb_dn_get_linearized(ar->search_msg->dn));
5698 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5699 "No parent with GUID %s found for object remotely known as %s",
5700 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5701 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5705 * This error code is really important, as it
5706 * is the flag back to the callers to retry
5707 * this with DRSUAPI_DRS_GET_ANC, and so get
5708 * the parent objects before the child
5711 return ldb_module_done(ar->req, NULL, NULL,
5712 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5715 if (ar->search_msg != NULL) {
5716 ret = replmd_replicated_apply_merge(ar);
5718 ret = replmd_replicated_apply_add(ar);
5720 if (ret != LDB_SUCCESS) {
5721 return ldb_module_done(ar->req, NULL, NULL, ret);
5730 * Look for the parent object, so we put the new object in the right
5731 * place This is akin to NameObject in MS-DRSR - this routine and the
5732 * callbacks find the right parent name, and correct name for this
5736 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5738 struct ldb_context *ldb;
5742 struct ldb_request *search_req;
5743 static const char *attrs[] = {"isDeleted", NULL};
5744 struct GUID_txt_buf guid_str_buf;
5746 ldb = ldb_module_get_ctx(ar->module);
5748 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5749 if (ar->search_msg != NULL) {
5750 return replmd_replicated_apply_merge(ar);
5752 return replmd_replicated_apply_add(ar);
5756 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5759 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5760 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5762 ret = ldb_build_search_req(&search_req,
5765 ar->objs->partition_dn,
5771 replmd_replicated_apply_search_for_parent_callback,
5773 LDB_REQ_SET_LOCATION(search_req);
5775 ret = dsdb_request_add_controls(search_req,
5776 DSDB_SEARCH_SHOW_RECYCLED|
5777 DSDB_SEARCH_SHOW_DELETED|
5778 DSDB_SEARCH_SHOW_EXTENDED_DN);
5779 if (ret != LDB_SUCCESS) {
5783 return ldb_next_request(ar->module, search_req);
5787 handle renames that come in over DRS replication
5789 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5790 struct ldb_message *msg,
5791 struct ldb_request *parent,
5795 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5796 struct ldb_result *res;
5797 struct ldb_dn *conflict_dn;
5798 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5799 const struct ldb_val *omd_value;
5800 struct replPropertyMetaDataBlob omd, *rmd;
5801 enum ndr_err_code ndr_err;
5802 bool rename_incoming_record, rodc;
5803 struct replPropertyMetaData1 *rmd_name, *omd_name;
5804 struct ldb_dn *new_dn;
5807 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5808 ldb_dn_get_linearized(ar->search_msg->dn),
5809 ldb_dn_get_linearized(msg->dn)));
5812 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5813 DSDB_FLAG_NEXT_MODULE, ar->req);
5814 if (ret == LDB_SUCCESS) {
5815 talloc_free(tmp_ctx);
5820 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5821 talloc_free(tmp_ctx);
5822 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5823 ldb_dn_get_linearized(ar->search_msg->dn),
5824 ldb_dn_get_linearized(msg->dn),
5825 ldb_errstring(ldb_module_get_ctx(ar->module)));
5829 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5830 if (ret != LDB_SUCCESS) {
5831 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5832 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5833 ldb_errstring(ldb_module_get_ctx(ar->module)));
5834 return LDB_ERR_OPERATIONS_ERROR;
5837 * we have a conflict, and need to decide if we will keep the
5838 * new record or the old record
5841 conflict_dn = msg->dn;
5845 * We are on an RODC, or were a GC for this
5846 * partition, so we have to fail this until
5847 * someone who owns the partition sorts it
5850 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5851 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5852 " - We must fail the operation until a master for this partition resolves the conflict",
5853 ldb_dn_get_linearized(conflict_dn));
5854 ret = LDB_ERR_OPERATIONS_ERROR;
5859 * first we need the replPropertyMetaData attribute from the
5862 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5864 DSDB_FLAG_NEXT_MODULE |
5865 DSDB_SEARCH_SHOW_DELETED |
5866 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5867 if (ret != LDB_SUCCESS) {
5868 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5869 ldb_dn_get_linearized(conflict_dn)));
5873 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5874 if (omd_value == NULL) {
5875 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5876 ldb_dn_get_linearized(conflict_dn)));
5880 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5881 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5882 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5883 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5884 ldb_dn_get_linearized(conflict_dn)));
5888 rmd = ar->objs->objects[ar->index_current].meta_data;
5891 * we decide which is newer based on the RPMD on the name
5892 * attribute. See [MS-DRSR] ResolveNameConflict.
5894 * We expect omd_name to be present, as this is from a local
5895 * search, but while rmd_name should have been given to us by
5896 * the remote server, if it is missing we just prefer the
5898 * replmd_replPropertyMetaData1_new_should_be_taken()
5900 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5901 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5903 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5904 ldb_dn_get_linearized(conflict_dn)));
5909 * Should we preserve the current record, and so rename the
5910 * incoming record to be a conflict?
5912 rename_incoming_record =
5913 !replmd_replPropertyMetaData1_new_should_be_taken(
5914 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5915 omd_name, rmd_name);
5917 if (rename_incoming_record) {
5919 new_dn = replmd_conflict_dn(msg,
5920 ldb_module_get_ctx(ar->module),
5922 &ar->objs->objects[ar->index_current].object_guid);
5923 if (new_dn == NULL) {
5924 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5925 "Failed to form conflict DN for %s\n",
5926 ldb_dn_get_linearized(msg->dn));
5928 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5931 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5932 DSDB_FLAG_NEXT_MODULE, ar->req);
5933 if (ret != LDB_SUCCESS) {
5934 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5935 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5936 ldb_dn_get_linearized(conflict_dn),
5937 ldb_dn_get_linearized(ar->search_msg->dn),
5938 ldb_dn_get_linearized(new_dn),
5939 ldb_errstring(ldb_module_get_ctx(ar->module)));
5940 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5948 /* we are renaming the existing record */
5950 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5951 if (GUID_all_zero(&guid)) {
5952 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5953 ldb_dn_get_linearized(conflict_dn)));
5957 new_dn = replmd_conflict_dn(tmp_ctx,
5958 ldb_module_get_ctx(ar->module),
5959 conflict_dn, &guid);
5960 if (new_dn == NULL) {
5961 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5962 ldb_dn_get_linearized(conflict_dn)));
5966 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5967 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5969 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5970 DSDB_FLAG_OWN_MODULE, ar->req);
5971 if (ret != LDB_SUCCESS) {
5972 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5973 ldb_dn_get_linearized(conflict_dn),
5974 ldb_dn_get_linearized(new_dn),
5975 ldb_errstring(ldb_module_get_ctx(ar->module))));
5980 * now we need to ensure that the rename is seen as an
5981 * originating update. We do that with a modify.
5983 ret = replmd_name_modify(ar, ar->req, new_dn);
5984 if (ret != LDB_SUCCESS) {
5988 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5989 ldb_dn_get_linearized(ar->search_msg->dn),
5990 ldb_dn_get_linearized(msg->dn)));
5993 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5994 DSDB_FLAG_NEXT_MODULE, ar->req);
5995 if (ret != LDB_SUCCESS) {
5996 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5997 ldb_dn_get_linearized(ar->search_msg->dn),
5998 ldb_dn_get_linearized(msg->dn),
5999 ldb_errstring(ldb_module_get_ctx(ar->module))));
6003 talloc_free(tmp_ctx);
6007 * On failure make the caller get the error
6008 * This means replication will stop with an error,
6009 * but there is not much else we can do. In the
6010 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6013 if (ret == LDB_SUCCESS) {
6014 ret = LDB_ERR_OPERATIONS_ERROR;
6017 talloc_free(tmp_ctx);
6022 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6024 struct ldb_context *ldb;
6025 struct ldb_request *change_req;
6026 enum ndr_err_code ndr_err;
6027 struct ldb_message *msg;
6028 struct replPropertyMetaDataBlob *rmd;
6029 struct replPropertyMetaDataBlob omd;
6030 const struct ldb_val *omd_value;
6031 struct replPropertyMetaDataBlob nmd;
6032 struct ldb_val nmd_value;
6033 struct GUID remote_parent_guid;
6036 unsigned int removed_attrs = 0;
6038 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6039 bool isDeleted = false;
6040 bool local_isDeleted = false;
6041 bool remote_isDeleted = false;
6042 bool take_remote_isDeleted = false;
6043 bool sd_updated = false;
6044 bool renamed = false;
6045 bool is_schema_nc = false;
6047 const struct ldb_val *old_rdn, *new_rdn;
6048 struct replmd_private *replmd_private =
6049 talloc_get_type(ldb_module_get_private(ar->module),
6050 struct replmd_private);
6052 time_t t = time(NULL);
6053 unix_to_nt_time(&now, t);
6055 ldb = ldb_module_get_ctx(ar->module);
6056 msg = ar->objs->objects[ar->index_current].msg;
6058 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6060 rmd = ar->objs->objects[ar->index_current].meta_data;
6064 /* find existing meta data */
6065 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6067 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6068 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6069 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6070 nt_status = ndr_map_error2ntstatus(ndr_err);
6071 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6074 if (omd.version != 1) {
6075 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6080 struct GUID_txt_buf guid_txt;
6082 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6083 LDB_CHANGETYPE_MODIFY, msg);
6084 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6087 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6089 ndr_print_struct_string(s,
6090 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6091 "existing replPropertyMetaData",
6093 ndr_print_struct_string(s,
6094 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6095 "incoming replPropertyMetaData",
6098 } else if (DEBUGLVL(4)) {
6099 struct GUID_txt_buf guid_txt;
6101 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6102 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6104 ldb_dn_get_linearized(msg->dn)));
6107 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6108 "isDeleted", false);
6109 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6110 "isDeleted", false);
6113 * Fill in the remote_parent_guid with the GUID or an all-zero
6116 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6117 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6119 remote_parent_guid = GUID_zero();
6123 * To ensure we follow a complex rename chain around, we have
6124 * to confirm that the DN is the same (mostly to confirm the
6125 * RDN) and the parentGUID is the same.
6127 * This ensures we keep things under the correct parent, which
6128 * replmd_replicated_handle_rename() will do.
6131 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6132 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6136 * handle renames, even just by case that come in over
6137 * DRS. Changes in the parent DN don't hit us here,
6138 * because the search for a parent will clean up those
6141 * We also have already filtered out the case where
6142 * the peer has an older name to what we have (see
6143 * replmd_replicated_apply_search_callback())
6145 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6148 if (ret != LDB_SUCCESS) {
6149 ldb_debug(ldb, LDB_DEBUG_FATAL,
6150 "replmd_replicated_request rename %s => %s failed - %s\n",
6151 ldb_dn_get_linearized(ar->search_msg->dn),
6152 ldb_dn_get_linearized(msg->dn),
6153 ldb_errstring(ldb));
6154 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6157 if (renamed == true) {
6159 * Set the callback to one that will fix up the name
6160 * metadata on the new conflict DN
6162 callback = replmd_op_name_modify_callback;
6167 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6168 nmd.ctr.ctr1.array = talloc_array(ar,
6169 struct replPropertyMetaData1,
6170 nmd.ctr.ctr1.count);
6171 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6173 /* first copy the old meta data */
6174 for (i=0; i < omd.ctr.ctr1.count; i++) {
6175 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6180 /* now merge in the new meta data */
6181 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6184 for (j=0; j < ni; j++) {
6187 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6191 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6192 ar->objs->dsdb_repl_flags,
6193 &nmd.ctr.ctr1.array[j],
6194 &rmd->ctr.ctr1.array[i]);
6196 /* replace the entry */
6197 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6198 if (ar->seq_num == 0) {
6199 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6200 if (ret != LDB_SUCCESS) {
6201 return replmd_replicated_request_error(ar, ret);
6204 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6205 switch (nmd.ctr.ctr1.array[j].attid) {
6206 case DRSUAPI_ATTID_ntSecurityDescriptor:
6209 case DRSUAPI_ATTID_isDeleted:
6210 take_remote_isDeleted = true;
6219 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6220 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6221 msg->elements[i-removed_attrs].name,
6222 ldb_dn_get_linearized(msg->dn),
6223 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6226 /* we don't want to apply this change so remove the attribute */
6227 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6234 if (found) continue;
6236 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6237 if (ar->seq_num == 0) {
6238 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6239 if (ret != LDB_SUCCESS) {
6240 return replmd_replicated_request_error(ar, ret);
6243 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6244 switch (nmd.ctr.ctr1.array[ni].attid) {
6245 case DRSUAPI_ATTID_ntSecurityDescriptor:
6248 case DRSUAPI_ATTID_isDeleted:
6249 take_remote_isDeleted = true;
6258 * finally correct the size of the meta_data array
6260 nmd.ctr.ctr1.count = ni;
6262 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6263 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6266 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6267 &nmd, ar, now, is_schema_nc,
6269 if (ret != LDB_SUCCESS) {
6270 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6271 return replmd_replicated_request_error(ar, ret);
6275 * sort the new meta data array
6277 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6278 if (ret != LDB_SUCCESS) {
6279 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6284 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6287 * This also controls SD propagation below
6289 if (take_remote_isDeleted) {
6290 isDeleted = remote_isDeleted;
6292 isDeleted = local_isDeleted;
6295 ar->isDeleted = isDeleted;
6298 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6300 if (msg->num_elements == 0) {
6301 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6304 return replmd_replicated_apply_isDeleted(ar);
6307 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6308 ar->index_current, msg->num_elements);
6314 if (sd_updated && !isDeleted) {
6315 ret = dsdb_module_schedule_sd_propagation(ar->module,
6316 ar->objs->partition_dn,
6318 if (ret != LDB_SUCCESS) {
6319 return ldb_operr(ldb);
6323 /* create the meta data value */
6324 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6325 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6326 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6327 nt_status = ndr_map_error2ntstatus(ndr_err);
6328 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6332 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6333 * and replPopertyMetaData attributes
6335 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6336 if (ret != LDB_SUCCESS) {
6337 return replmd_replicated_request_error(ar, ret);
6339 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6340 if (ret != LDB_SUCCESS) {
6341 return replmd_replicated_request_error(ar, ret);
6343 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6344 if (ret != LDB_SUCCESS) {
6345 return replmd_replicated_request_error(ar, ret);
6348 replmd_ldb_message_sort(msg, ar->schema);
6350 /* we want to replace the old values */
6351 for (i=0; i < msg->num_elements; i++) {
6352 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6353 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6354 if (msg->elements[i].num_values == 0) {
6355 ldb_asprintf_errstring(ldb, __location__
6356 ": objectClass removed on %s, aborting replication\n",
6357 ldb_dn_get_linearized(msg->dn));
6358 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6364 struct GUID_txt_buf guid_txt;
6366 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6367 LDB_CHANGETYPE_MODIFY,
6369 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6370 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6374 } else if (DEBUGLVL(4)) {
6375 struct GUID_txt_buf guid_txt;
6377 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6378 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6380 ldb_dn_get_linearized(msg->dn)));
6383 ret = ldb_build_mod_req(&change_req,
6391 LDB_REQ_SET_LOCATION(change_req);
6392 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6394 /* current partition control needed by "repmd_op_callback" */
6395 ret = ldb_request_add_control(change_req,
6396 DSDB_CONTROL_CURRENT_PARTITION_OID,
6398 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6400 return ldb_next_request(ar->module, change_req);
6403 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6404 struct ldb_reply *ares)
6406 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6407 struct replmd_replicated_request);
6411 return ldb_module_done(ar->req, NULL, NULL,
6412 LDB_ERR_OPERATIONS_ERROR);
6414 if (ares->error != LDB_SUCCESS &&
6415 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6416 return ldb_module_done(ar->req, ares->controls,
6417 ares->response, ares->error);
6420 switch (ares->type) {
6421 case LDB_REPLY_ENTRY:
6422 ar->search_msg = talloc_steal(ar, ares->message);
6425 case LDB_REPLY_REFERRAL:
6426 /* we ignore referrals */
6429 case LDB_REPLY_DONE:
6431 struct replPropertyMetaData1 *md_remote;
6432 struct replPropertyMetaData1 *md_local;
6434 struct replPropertyMetaDataBlob omd;
6435 const struct ldb_val *omd_value;
6436 struct replPropertyMetaDataBlob *rmd;
6437 struct ldb_message *msg;
6439 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6440 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6443 * This is the ADD case, find the appropriate parent,
6444 * as this object doesn't exist locally:
6446 if (ar->search_msg == NULL) {
6447 ret = replmd_replicated_apply_search_for_parent(ar);
6448 if (ret != LDB_SUCCESS) {
6449 return ldb_module_done(ar->req, NULL, NULL, ret);
6456 * Otherwise, in the MERGE case, work out if we are
6457 * attempting a rename, and if so find the parent the
6458 * newly renamed object wants to belong under (which
6459 * may not be the parent in it's attached string DN
6461 rmd = ar->objs->objects[ar->index_current].meta_data;
6465 /* find existing meta data */
6466 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6468 enum ndr_err_code ndr_err;
6469 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6470 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6471 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6472 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6473 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6476 if (omd.version != 1) {
6477 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6481 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6483 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6484 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6485 && GUID_all_zero(&ar->local_parent_guid)) {
6486 DEBUG(0, ("Refusing to replicate new version of %s "
6487 "as local object has an all-zero parentGUID attribute, "
6488 "despite not being an NC root\n",
6489 ldb_dn_get_linearized(ar->search_msg->dn)));
6490 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6494 * now we need to check for double renames. We could have a
6495 * local rename pending which our replication partner hasn't
6496 * received yet. We choose which one wins by looking at the
6497 * attribute stamps on the two objects, the newer one wins.
6499 * This also simply applies the correct algorithms for
6500 * determining if a change was made to name at all, or
6501 * if the object has just been renamed under the same
6504 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6505 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6507 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6508 ldb_dn_get_linearized(ar->search_msg->dn)));
6509 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6513 * if there is no name attribute given then we have to assume the
6514 * object we've received has the older name
6516 if (replmd_replPropertyMetaData1_new_should_be_taken(
6517 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6518 md_local, md_remote)) {
6519 struct GUID_txt_buf p_guid_local;
6520 struct GUID_txt_buf p_guid_remote;
6521 msg = ar->objs->objects[ar->index_current].msg;
6523 /* Merge on the existing object, with rename */
6525 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6526 "as incoming object changing to %s under %s\n",
6527 ldb_dn_get_linearized(ar->search_msg->dn),
6528 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6529 ldb_dn_get_linearized(msg->dn),
6530 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6532 ret = replmd_replicated_apply_search_for_parent(ar);
6534 struct GUID_txt_buf p_guid_local;
6535 struct GUID_txt_buf p_guid_remote;
6536 msg = ar->objs->objects[ar->index_current].msg;
6539 * Merge on the existing object, force no
6540 * rename (code below just to explain why in
6544 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6545 ldb_dn_get_linearized(msg->dn)) == 0) {
6546 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6547 GUID_equal(&ar->local_parent_guid,
6548 ar->objs->objects[ar->index_current].parent_guid)
6550 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6551 "despite incoming object changing parent to %s\n",
6552 ldb_dn_get_linearized(ar->search_msg->dn),
6553 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6554 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6558 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6559 " and rejecting older rename to %s under %s\n",
6560 ldb_dn_get_linearized(ar->search_msg->dn),
6561 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6562 ldb_dn_get_linearized(msg->dn),
6563 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6567 * This assignment ensures that the strcmp()
6568 * and GUID_equal() calls in
6569 * replmd_replicated_apply_merge() avoids the
6572 ar->objs->objects[ar->index_current].parent_guid =
6573 &ar->local_parent_guid;
6575 msg->dn = ar->search_msg->dn;
6576 ret = replmd_replicated_apply_merge(ar);
6578 if (ret != LDB_SUCCESS) {
6579 return ldb_module_done(ar->req, NULL, NULL, ret);
6589 * Returns true if we can group together processing this link attribute,
6590 * i.e. it has the same source-object and attribute ID as other links
6591 * already in the group
6593 static bool la_entry_matches_group(struct la_entry *la_entry,
6594 struct la_group *la_group)
6596 struct la_entry *prev = la_group->la_entries;
6598 return (la_entry->la->attid == prev->la->attid &&
6599 GUID_equal(&la_entry->la->identifier->guid,
6600 &prev->la->identifier->guid));
6604 * Creates a new la_entry to store replication info for a single
6607 static struct la_entry *
6608 create_la_entry(struct replmd_private *replmd_private,
6609 struct drsuapi_DsReplicaLinkedAttribute *la,
6610 uint32_t dsdb_repl_flags)
6612 struct la_entry *la_entry;
6614 if (replmd_private->la_ctx == NULL) {
6615 replmd_private->la_ctx = talloc_new(replmd_private);
6617 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6618 if (la_entry == NULL) {
6621 la_entry->la = talloc(la_entry,
6622 struct drsuapi_DsReplicaLinkedAttribute);
6623 if (la_entry->la == NULL) {
6624 talloc_free(la_entry);
6627 *la_entry->la = *la;
6628 la_entry->dsdb_repl_flags = dsdb_repl_flags;
6631 * we need to steal the non-scalars so they stay
6632 * around until the end of the transaction
6634 talloc_steal(la_entry->la, la_entry->la->identifier);
6635 talloc_steal(la_entry->la, la_entry->la->value.blob);
6641 * Stores the linked attributes received in the replication chunk - these get
6642 * applied at the end of the transaction. We also check that each linked
6643 * attribute is valid, i.e. source and target objects are known.
6645 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6647 int ret = LDB_SUCCESS;
6649 struct ldb_module *module = ar->module;
6650 struct replmd_private *replmd_private =
6651 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6652 struct la_group *la_group = NULL;
6653 struct ldb_context *ldb;
6654 TALLOC_CTX *tmp_ctx = NULL;
6655 struct ldb_message *src_msg = NULL;
6656 const struct dsdb_attribute *attr = NULL;
6658 ldb = ldb_module_get_ctx(module);
6660 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6662 /* save away the linked attributes for the end of the transaction */
6663 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6664 struct la_entry *la_entry;
6667 /* create an entry to store the received link attribute info */
6668 la_entry = create_la_entry(replmd_private,
6669 &ar->objs->linked_attributes[i],
6670 ar->objs->dsdb_repl_flags);
6671 if (la_entry == NULL) {
6673 return LDB_ERR_OPERATIONS_ERROR;
6677 * check if we're still dealing with the same source object
6680 new_srcobj = (la_group == NULL ||
6681 !la_entry_matches_group(la_entry, la_group));
6685 /* get a new mem_ctx to lookup the source object */
6686 TALLOC_FREE(tmp_ctx);
6687 tmp_ctx = talloc_new(ar);
6688 if (tmp_ctx == NULL) {
6690 return LDB_ERR_OPERATIONS_ERROR;
6693 /* verify the link source exists */
6694 ret = replmd_get_la_entry_source(module, la_entry,
6699 * When we fail to find the source object, the error
6700 * code we pass back here is really important. It flags
6701 * back to the callers to retry this request with
6702 * DRSUAPI_DRS_GET_ANC. This case should never happen
6703 * if we're replicating from a Samba DC, but it is
6704 * needed to talk to a Windows DC
6706 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6707 WERROR err = WERR_DS_DRA_MISSING_PARENT;
6708 ret = replmd_replicated_request_werror(ar,
6714 ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6716 if (ret != LDB_SUCCESS) {
6720 /* group the links together by source-object for efficiency */
6722 la_group = talloc_zero(replmd_private->la_ctx,
6724 if (la_group == NULL) {
6726 return LDB_ERR_OPERATIONS_ERROR;
6728 DLIST_ADD(replmd_private->la_list, la_group);
6730 DLIST_ADD(la_group->la_entries, la_entry);
6731 replmd_private->total_links++;
6734 TALLOC_FREE(tmp_ctx);
6738 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6740 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6742 struct ldb_context *ldb;
6746 struct ldb_request *search_req;
6747 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6748 "parentGUID", "instanceType",
6749 "replPropertyMetaData", "nTSecurityDescriptor",
6750 "isDeleted", NULL };
6751 struct GUID_txt_buf guid_str_buf;
6753 if (ar->index_current >= ar->objs->num_objects) {
6756 * Now that we've applied all the objects, check the new linked
6757 * attributes and store them (we apply them in .prepare_commit)
6759 ret = replmd_store_linked_attributes(ar);
6761 if (ret != LDB_SUCCESS) {
6765 /* done applying objects, move on to the next stage */
6766 return replmd_replicated_uptodate_vector(ar);
6769 ldb = ldb_module_get_ctx(ar->module);
6770 ar->search_msg = NULL;
6771 ar->isDeleted = false;
6773 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6776 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6777 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6779 ret = ldb_build_search_req(&search_req,
6782 ar->objs->partition_dn,
6788 replmd_replicated_apply_search_callback,
6790 LDB_REQ_SET_LOCATION(search_req);
6792 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6794 if (ret != LDB_SUCCESS) {
6798 return ldb_next_request(ar->module, search_req);
6802 * This is essentially a wrapper for replmd_replicated_apply_next()
6804 * This is needed to ensure that both codepaths call this handler.
6806 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6808 struct ldb_dn *deleted_objects_dn;
6809 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6810 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6811 &deleted_objects_dn);
6812 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6814 * Do a delete here again, so that if there is
6815 * anything local that conflicts with this
6816 * object being deleted, it is removed. This
6817 * includes links. See MS-DRSR 4.1.10.6.9
6820 * If the object is already deleted, and there
6821 * is no more work required, it doesn't do
6825 /* This has been updated to point to the DN we eventually did the modify on */
6827 struct ldb_request *del_req;
6828 struct ldb_result *res;
6830 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6832 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6836 res = talloc_zero(tmp_ctx, struct ldb_result);
6838 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6839 talloc_free(tmp_ctx);
6843 /* Build a delete request, which hopefully will artually turn into nothing */
6844 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6848 ldb_modify_default_callback,
6850 LDB_REQ_SET_LOCATION(del_req);
6851 if (ret != LDB_SUCCESS) {
6852 talloc_free(tmp_ctx);
6857 * This is the guts of the call, call back
6858 * into our delete code, but setting the
6859 * re_delete flag so we delete anything that
6860 * shouldn't be there on a deleted or recycled
6863 ret = replmd_delete_internals(ar->module, del_req, true);
6864 if (ret == LDB_SUCCESS) {
6865 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6868 talloc_free(tmp_ctx);
6869 if (ret != LDB_SUCCESS) {
6874 ar->index_current++;
6875 return replmd_replicated_apply_next(ar);
6878 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6879 struct ldb_reply *ares)
6881 struct ldb_context *ldb;
6882 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6883 struct replmd_replicated_request);
6884 ldb = ldb_module_get_ctx(ar->module);
6887 return ldb_module_done(ar->req, NULL, NULL,
6888 LDB_ERR_OPERATIONS_ERROR);
6890 if (ares->error != LDB_SUCCESS) {
6891 return ldb_module_done(ar->req, ares->controls,
6892 ares->response, ares->error);
6895 if (ares->type != LDB_REPLY_DONE) {
6896 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6897 return ldb_module_done(ar->req, NULL, NULL,
6898 LDB_ERR_OPERATIONS_ERROR);
6903 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6906 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6908 struct ldb_context *ldb;
6909 struct ldb_request *change_req;
6910 enum ndr_err_code ndr_err;
6911 struct ldb_message *msg;
6912 struct replUpToDateVectorBlob ouv;
6913 const struct ldb_val *ouv_value;
6914 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6915 struct replUpToDateVectorBlob nuv;
6916 struct ldb_val nuv_value;
6917 struct ldb_message_element *nuv_el = NULL;
6918 struct ldb_message_element *orf_el = NULL;
6919 struct repsFromToBlob nrf;
6920 struct ldb_val *nrf_value = NULL;
6921 struct ldb_message_element *nrf_el = NULL;
6925 time_t t = time(NULL);
6928 uint32_t instanceType;
6930 ldb = ldb_module_get_ctx(ar->module);
6931 ruv = ar->objs->uptodateness_vector;
6937 unix_to_nt_time(&now, t);
6939 if (ar->search_msg == NULL) {
6940 /* this happens for a REPL_OBJ call where we are
6941 creating the target object by replicating it. The
6942 subdomain join code does this for the partition DN
6944 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6945 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6948 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6949 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6950 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6951 ldb_dn_get_linearized(ar->search_msg->dn)));
6952 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6956 * first create the new replUpToDateVector
6958 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6960 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6961 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6962 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6963 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6964 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6967 if (ouv.version != 2) {
6968 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6973 * the new uptodateness vector will at least
6974 * contain 1 entry, one for the source_dsa
6976 * plus optional values from our old vector and the one from the source_dsa
6978 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6979 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6980 nuv.ctr.ctr2.cursors = talloc_array(ar,
6981 struct drsuapi_DsReplicaCursor2,
6982 nuv.ctr.ctr2.count);
6983 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6985 /* first copy the old vector */
6986 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6987 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6991 /* merge in the source_dsa vector is available */
6992 for (i=0; (ruv && i < ruv->count); i++) {
6995 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6996 &ar->our_invocation_id)) {
7000 for (j=0; j < ni; j++) {
7001 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7002 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7008 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7009 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7014 if (found) continue;
7016 /* if it's not there yet, add it */
7017 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7022 * finally correct the size of the cursors array
7024 nuv.ctr.ctr2.count = ni;
7029 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7032 * create the change ldb_message
7034 msg = ldb_msg_new(ar);
7035 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7036 msg->dn = ar->search_msg->dn;
7038 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7039 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7040 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7041 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7042 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7044 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7045 if (ret != LDB_SUCCESS) {
7046 return replmd_replicated_request_error(ar, ret);
7048 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7051 * now create the new repsFrom value from the given repsFromTo1 structure
7055 nrf.ctr.ctr1 = *ar->objs->source_dsa;
7056 nrf.ctr.ctr1.last_attempt = now;
7057 nrf.ctr.ctr1.last_success = now;
7058 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7061 * first see if we already have a repsFrom value for the current source dsa
7062 * if so we'll later replace this value
7064 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7066 for (i=0; i < orf_el->num_values; i++) {
7067 struct repsFromToBlob *trf;
7069 trf = talloc(ar, struct repsFromToBlob);
7070 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7072 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7073 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7074 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7075 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7076 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7079 if (trf->version != 1) {
7080 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7084 * we compare the source dsa objectGUID not the invocation_id
7085 * because we want only one repsFrom value per source dsa
7086 * and when the invocation_id of the source dsa has changed we don't need
7087 * the old repsFrom with the old invocation_id
7089 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7090 &ar->objs->source_dsa->source_dsa_obj_guid)) {
7096 nrf_value = &orf_el->values[i];
7101 * copy over all old values to the new ldb_message
7103 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7104 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7109 * if we haven't found an old repsFrom value for the current source dsa
7110 * we'll add a new value
7113 struct ldb_val zero_value;
7114 ZERO_STRUCT(zero_value);
7115 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7116 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7118 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7121 /* we now fill the value which is already attached to ldb_message */
7122 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7124 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7125 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7126 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7127 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7131 * the ldb_message_element for the attribute, has all the old values and the new one
7132 * so we'll replace the whole attribute with all values
7134 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7136 if (CHECK_DEBUGLVL(4)) {
7137 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7138 LDB_CHANGETYPE_MODIFY,
7140 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7144 /* prepare the ldb_modify() request */
7145 ret = ldb_build_mod_req(&change_req,
7151 replmd_replicated_uptodate_modify_callback,
7153 LDB_REQ_SET_LOCATION(change_req);
7154 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7156 return ldb_next_request(ar->module, change_req);
7159 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7160 struct ldb_reply *ares)
7162 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7163 struct replmd_replicated_request);
7167 return ldb_module_done(ar->req, NULL, NULL,
7168 LDB_ERR_OPERATIONS_ERROR);
7170 if (ares->error != LDB_SUCCESS &&
7171 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7172 return ldb_module_done(ar->req, ares->controls,
7173 ares->response, ares->error);
7176 switch (ares->type) {
7177 case LDB_REPLY_ENTRY:
7178 ar->search_msg = talloc_steal(ar, ares->message);
7181 case LDB_REPLY_REFERRAL:
7182 /* we ignore referrals */
7185 case LDB_REPLY_DONE:
7186 ret = replmd_replicated_uptodate_modify(ar);
7187 if (ret != LDB_SUCCESS) {
7188 return ldb_module_done(ar->req, NULL, NULL, ret);
7197 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7199 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7200 struct replmd_private *replmd_private =
7201 talloc_get_type_abort(ldb_module_get_private(ar->module),
7202 struct replmd_private);
7204 static const char *attrs[] = {
7205 "replUpToDateVector",
7210 struct ldb_request *search_req;
7212 ar->search_msg = NULL;
7215 * Let the caller know that we did an originating updates
7217 ar->objs->originating_updates = replmd_private->originating_updates;
7219 ret = ldb_build_search_req(&search_req,
7222 ar->objs->partition_dn,
7228 replmd_replicated_uptodate_search_callback,
7230 LDB_REQ_SET_LOCATION(search_req);
7231 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7233 return ldb_next_request(ar->module, search_req);
7238 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7240 struct ldb_context *ldb;
7241 struct dsdb_extended_replicated_objects *objs;
7242 struct replmd_replicated_request *ar;
7243 struct ldb_control **ctrls;
7246 ldb = ldb_module_get_ctx(module);
7248 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7250 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7252 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7253 return LDB_ERR_PROTOCOL_ERROR;
7256 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7257 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7258 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7259 return LDB_ERR_PROTOCOL_ERROR;
7262 ar = replmd_ctx_init(module, req);
7264 return LDB_ERR_OPERATIONS_ERROR;
7266 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7267 ar->apply_mode = true;
7269 ar->schema = dsdb_get_schema(ldb, ar);
7271 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7273 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7274 return LDB_ERR_CONSTRAINT_VIOLATION;
7277 ctrls = req->controls;
7279 if (req->controls) {
7280 req->controls = talloc_memdup(ar, req->controls,
7281 talloc_get_size(req->controls));
7282 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7285 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7286 if (ret != LDB_SUCCESS) {
7290 /* If this change contained linked attributes in the body
7291 * (rather than in the links section) we need to update
7292 * backlinks in linked_attributes */
7293 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7294 if (ret != LDB_SUCCESS) {
7298 ar->controls = req->controls;
7299 req->controls = ctrls;
7301 return replmd_replicated_apply_next(ar);
7305 * Checks how to handle an missing target - either we need to fail the
7306 * replication and retry with GET_TGT, ignore the link and continue, or try to
7307 * add a partial link to an unknown target.
7309 static int replmd_allow_missing_target(struct ldb_module *module,
7310 TALLOC_CTX *mem_ctx,
7311 struct ldb_dn *target_dn,
7312 struct ldb_dn *source_dn,
7315 uint32_t dsdb_repl_flags,
7317 const char * missing_str)
7319 struct ldb_context *ldb = ldb_module_get_ctx(module);
7323 * we may not be able to resolve link targets properly when
7324 * dealing with subsets of objects, e.g. the source is a
7325 * critical object and the target isn't
7328 * When we implement Trusted Domains we need to consider
7329 * whether they get treated as an incomplete replica here or not
7331 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7334 * Ignore the link. We don't increase the highwater-mark in
7335 * the object subset cases, so subsequent replications should
7336 * resolve any missing links
7338 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7339 ldb_dn_get_linearized(target_dn),
7340 ldb_dn_get_linearized(source_dn)));
7341 *ignore_link = true;
7345 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7348 * target should already be up-to-date so there's no point in
7349 * retrying. This could be due to bad timing, or if a target
7350 * on a one-way link was deleted. We ignore the link rather
7351 * than failing the replication cycle completely
7353 *ignore_link = true;
7354 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7355 ldb_dn_get_linearized(target_dn), missing_str,
7356 ldb_dn_get_linearized(source_dn));
7360 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7364 if (is_in_same_nc) {
7365 /* fail the replication and retry with GET_TGT */
7366 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7368 ldb_dn_get_linearized(target_dn),
7369 GUID_string(mem_ctx, guid),
7370 ldb_dn_get_linearized(source_dn));
7371 return LDB_ERR_NO_SUCH_OBJECT;
7375 * The target of the cross-partition link is missing. Continue
7376 * and try to at least add the forward-link. This isn't great,
7377 * but a partial link can be fixed by dbcheck, so it's better
7378 * than dropping the link completely.
7380 *ignore_link = false;
7382 if (is_obj_commit) {
7385 * Only log this when we're actually committing the objects.
7386 * This avoids spurious logs, i.e. if we're just verifying the
7387 * received link during a join.
7389 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7390 missing_str, ldb_dn_get_linearized(target_dn),
7391 ldb_dn_get_linearized(source_dn));
7398 * Checks that the target object for a linked attribute exists.
7399 * @param guid returns the target object's GUID (is returned)if it exists)
7400 * @param ignore_link set to true if the linked attribute should be ignored
7401 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7403 static int replmd_check_target_exists(struct ldb_module *module,
7404 struct dsdb_dn *dsdb_dn,
7405 struct la_entry *la_entry,
7406 struct ldb_dn *source_dn,
7411 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7412 struct ldb_context *ldb = ldb_module_get_ctx(module);
7413 struct ldb_result *target_res;
7414 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7415 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7418 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7419 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7421 *ignore_link = false;
7422 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7424 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7427 * This strange behaviour (allowing a NULL/missing
7428 * GUID) originally comes from:
7430 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7431 * Author: Andrew Tridgell <tridge@samba.org>
7432 * Date: Mon Dec 21 21:21:55 2009 +1100
7434 * s4-drs: cope better with NULL GUIDS from DRS
7436 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7437 * need to match by DN if possible when seeing if we should update an
7440 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7442 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7444 DSDB_FLAG_NEXT_MODULE |
7445 DSDB_SEARCH_SHOW_RECYCLED |
7446 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7447 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7449 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7450 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7452 ldb_dn_get_linearized(dsdb_dn->dn),
7453 ldb_dn_get_linearized(source_dn));
7454 talloc_free(tmp_ctx);
7455 return LDB_ERR_OPERATIONS_ERROR;
7457 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7458 NULL, LDB_SCOPE_SUBTREE,
7460 DSDB_FLAG_NEXT_MODULE |
7461 DSDB_SEARCH_SHOW_RECYCLED |
7462 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7463 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7466 GUID_string(tmp_ctx, guid));
7469 if (ret != LDB_SUCCESS) {
7470 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7471 GUID_string(tmp_ctx, guid),
7472 ldb_errstring(ldb));
7473 talloc_free(tmp_ctx);
7477 if (target_res->count == 0) {
7480 * target object is unknown. Check whether to ignore the link,
7481 * fail the replication, or add a partial link
7483 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7484 source_dn, is_obj_commit, guid,
7485 la_entry->dsdb_repl_flags,
7486 ignore_link, "Unknown");
7488 } else if (target_res->count != 1) {
7489 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7490 GUID_string(tmp_ctx, guid));
7491 ret = LDB_ERR_OPERATIONS_ERROR;
7493 struct ldb_message *target_msg = target_res->msgs[0];
7495 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7497 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7498 replmd_deletion_state(module, target_msg,
7499 &target_deletion_state, NULL);
7502 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7503 * ProcessLinkValue(). Link updates should not be sent for
7504 * recycled and tombstone objects (deleting the links should
7505 * happen when we delete the object). This probably means our
7506 * copy of the target object isn't up to date.
7508 if (target_deletion_state >= OBJECT_RECYCLED) {
7511 * target object is deleted. Check whether to ignore the
7512 * link, fail the replication, or add a partial link
7514 ret = replmd_allow_missing_target(module, tmp_ctx,
7515 dsdb_dn->dn, source_dn,
7516 is_obj_commit, guid,
7517 la_entry->dsdb_repl_flags,
7518 ignore_link, "Deleted");
7522 talloc_free(tmp_ctx);
7527 * Extracts the key details about the source object for a
7528 * linked-attribute entry.
7529 * This returns the following details:
7530 * @param ret_attr the schema details for the linked attribute
7531 * @param source_msg the search result for the source object
7533 static int replmd_get_la_entry_source(struct ldb_module *module,
7534 struct la_entry *la_entry,
7535 TALLOC_CTX *mem_ctx,
7536 const struct dsdb_attribute **ret_attr,
7537 struct ldb_message **source_msg)
7539 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7540 struct ldb_context *ldb = ldb_module_get_ctx(module);
7541 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7543 const struct dsdb_attribute *attr;
7544 struct ldb_result *res;
7545 const char *attrs[4];
7548 linked_attributes[0]:
7549 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7551 identifier: struct drsuapi_DsReplicaObjectIdentifier
7552 __ndr_size : 0x0000003a (58)
7553 __ndr_size_sid : 0x00000000 (0)
7554 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7556 __ndr_size_dn : 0x00000000 (0)
7558 attid : DRSUAPI_ATTID_member (0x1F)
7559 value: struct drsuapi_DsAttributeValue
7560 __ndr_size : 0x0000007e (126)
7562 blob : DATA_BLOB length=126
7563 flags : 0x00000001 (1)
7564 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7565 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7566 meta_data: struct drsuapi_DsReplicaMetaData
7567 version : 0x00000015 (21)
7568 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7569 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7570 originating_usn : 0x000000000001e19c (123292)
7572 (for cases where the link is to a normal DN)
7573 &target: struct drsuapi_DsReplicaObjectIdentifier3
7574 __ndr_size : 0x0000007e (126)
7575 __ndr_size_sid : 0x0000001c (28)
7576 guid : 7639e594-db75-4086-b0d4-67890ae46031
7577 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7578 __ndr_size_dn : 0x00000022 (34)
7579 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7582 /* find the attribute being modified */
7583 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7585 struct GUID_txt_buf guid_str;
7586 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7588 GUID_buf_string(&la->identifier->guid,
7590 return LDB_ERR_OPERATIONS_ERROR;
7594 * All attributes listed here must be dealt with in some way
7595 * by replmd_process_linked_attribute() otherwise in the case
7596 * of isDeleted: FALSE the modify will fail with:
7598 * Failed to apply linked attribute change 'attribute 'isDeleted':
7599 * invalid modify flags on
7600 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7603 * This is becaue isDeleted is a Boolean, so FALSE is a
7604 * legitimate value (set by Samba's deletetest.py)
7606 attrs[0] = attr->lDAPDisplayName;
7607 attrs[1] = "isDeleted";
7608 attrs[2] = "isRecycled";
7612 * get the existing message from the db for the object with
7613 * this GUID, returning attribute being modified. We will then
7614 * use this msg as the basis for a modify call
7616 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7617 DSDB_FLAG_NEXT_MODULE |
7618 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7619 DSDB_SEARCH_SHOW_RECYCLED |
7620 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7621 DSDB_SEARCH_REVEAL_INTERNALS,
7623 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7624 if (ret != LDB_SUCCESS) {
7627 if (res->count != 1) {
7628 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7629 GUID_string(mem_ctx, &la->identifier->guid));
7630 return LDB_ERR_NO_SUCH_OBJECT;
7633 *source_msg = res->msgs[0];
7640 * Verifies the target object is known for a linked attribute
7642 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7643 TALLOC_CTX *mem_ctx,
7644 struct la_entry *la_entry,
7645 struct ldb_dn *src_dn,
7646 const struct dsdb_attribute *attr)
7648 int ret = LDB_SUCCESS;
7649 struct ldb_module *module = ar->module;
7650 struct dsdb_dn *tgt_dsdb_dn = NULL;
7651 struct GUID guid = GUID_zero();
7654 struct ldb_context *ldb = ldb_module_get_ctx(module);
7655 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7656 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7658 /* the value blob for the attribute holds the target object DN */
7659 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7660 la->value.blob, &tgt_dsdb_dn);
7661 if (!W_ERROR_IS_OK(status)) {
7662 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7663 attr->lDAPDisplayName,
7664 ldb_dn_get_linearized(src_dn),
7665 win_errstr(status));
7666 return LDB_ERR_OPERATIONS_ERROR;
7670 * We can skip the target object checks if we're only syncing critical
7671 * objects, or we know the target is up-to-date. If either case, we
7672 * still continue even if the target doesn't exist
7674 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7675 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7677 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7678 src_dn, false, &guid, &dummy);
7682 * When we fail to find the target object, the error code we pass
7683 * back here is really important. It flags back to the callers to
7684 * retry this request with DRSUAPI_DRS_GET_TGT
7686 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7687 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7694 * Finds the current active Parsed-DN value for a single-valued linked
7695 * attribute, if one exists.
7696 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7697 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7700 static int replmd_get_active_singleval_link(struct ldb_module *module,
7701 TALLOC_CTX *mem_ctx,
7702 struct parsed_dn pdn_list[],
7704 const struct dsdb_attribute *attr,
7705 struct parsed_dn **ret_pdn)
7711 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7713 /* nothing to do for multi-valued linked attributes */
7717 for (i = 0; i < count; i++) {
7718 int ret = LDB_SUCCESS;
7719 struct parsed_dn *pdn = &pdn_list[i];
7721 /* skip any inactive links */
7722 if (dsdb_dn_is_deleted_val(pdn->v)) {
7726 /* we've found an active value for this attribute */
7729 if (pdn->dsdb_dn == NULL) {
7730 struct ldb_context *ldb = ldb_module_get_ctx(module);
7732 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7733 attr->syntax->ldap_oid);
7739 /* no active link found */
7744 * @returns true if the replication linked attribute info is newer than we
7745 * already have in our DB
7746 * @param pdn the existing linked attribute info in our DB
7747 * @param la the new linked attribute info received during replication
7749 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7750 struct drsuapi_DsReplicaLinkedAttribute *la)
7752 /* see if this update is newer than what we have already */
7753 struct GUID invocation_id = GUID_zero();
7754 uint32_t version = 0;
7755 NTTIME change_time = 0;
7759 /* no existing info so update is newer */
7763 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7764 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7765 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7767 return replmd_update_is_newer(&invocation_id,
7768 &la->meta_data.originating_invocation_id,
7770 la->meta_data.version,
7772 la->meta_data.originating_change_time);
7776 * Marks an existing linked attribute value as deleted in the DB
7777 * @param pdn the parsed-DN of the target-value to delete
7779 static int replmd_delete_link_value(struct ldb_module *module,
7780 struct replmd_private *replmd_private,
7781 TALLOC_CTX *mem_ctx,
7782 struct ldb_dn *src_obj_dn,
7783 const struct dsdb_schema *schema,
7784 const struct dsdb_attribute *attr,
7787 struct GUID *target_guid,
7788 struct dsdb_dn *target_dsdb_dn,
7789 struct ldb_val *output_val)
7791 struct ldb_context *ldb = ldb_module_get_ctx(module);
7794 const struct GUID *invocation_id = NULL;
7798 unix_to_nt_time(&now, t);
7800 invocation_id = samdb_ntds_invocation_id(ldb);
7801 if (invocation_id == NULL) {
7802 return LDB_ERR_OPERATIONS_ERROR;
7805 /* if the existing link is active, remove its backlink */
7809 * NOTE WELL: After this we will never (at runtime) be
7810 * able to find this forward link (for instant
7811 * removal) if/when the link target is deleted.
7813 * We have dbcheck rules to cover this and cope otherwise
7814 * by filtering at runtime (i.e. in the extended_dn module).
7816 ret = replmd_add_backlink(module, replmd_private, schema,
7817 src_obj_dn, target_guid, false,
7819 if (ret != LDB_SUCCESS) {
7824 /* mark the existing value as deleted */
7825 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7826 target_dsdb_dn, invocation_id, seq_num,
7827 seq_num, now, true);
7832 * Checks for a conflict in single-valued link attributes, and tries to
7833 * resolve the problem if possible.
7835 * Single-valued links should only ever have one active value. If we already
7836 * have an active link value, and during replication we receive an active link
7837 * value for a different target DN, then we need to resolve this inconsistency
7838 * and determine which value should be active. If the received info is better/
7839 * newer than the existing link attribute, then we need to set our existing
7840 * link as deleted. If the received info is worse/older, then we should continue
7841 * to add it, but set it as an inactive link.
7843 * Note that this is a corner-case that is unlikely to happen (but if it does
7844 * happen, we don't want it to break replication completely).
7846 * @param pdn_being_modified the parsed DN corresponding to the received link
7847 * target (note this is NULL if the link does not already exist in our DB)
7848 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7849 * any existing active or inactive values for the attribute in our DB.
7850 * @param dsdb_dn the target DN for the received link attribute
7851 * @param add_as_inactive gets set to true if the received link is worse than
7852 * the existing link - it should still be added, but as an inactive link.
7854 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7855 struct replmd_private *replmd_private,
7856 TALLOC_CTX *mem_ctx,
7857 struct ldb_dn *src_obj_dn,
7858 struct drsuapi_DsReplicaLinkedAttribute *la,
7859 struct dsdb_dn *dsdb_dn,
7860 struct parsed_dn *pdn_being_modified,
7861 struct parsed_dn *pdn_list,
7862 struct ldb_message_element *old_el,
7863 const struct dsdb_schema *schema,
7864 const struct dsdb_attribute *attr,
7866 bool *add_as_inactive)
7868 struct parsed_dn *active_pdn = NULL;
7869 bool update_is_newer = false;
7873 * check if there's a conflict for single-valued links, i.e. an active
7874 * linked attribute already exists, but it has a different target value
7876 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7877 old_el->num_values, attr,
7880 if (ret != LDB_SUCCESS) {
7885 * If no active value exists (or the received info is for the currently
7886 * active value), then no conflict exists
7888 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7892 DBG_WARNING("Link conflict for %s attribute on %s\n",
7893 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7895 /* Work out how to resolve the conflict based on which info is better */
7896 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7898 if (update_is_newer) {
7899 DBG_WARNING("Using received value %s, over existing target %s\n",
7900 ldb_dn_get_linearized(dsdb_dn->dn),
7901 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7904 * Delete our existing active link. The received info will then
7905 * be added (through normal link processing) as the active value
7907 ret = replmd_delete_link_value(module, replmd_private, old_el,
7908 src_obj_dn, schema, attr,
7909 seq_num, true, &active_pdn->guid,
7910 active_pdn->dsdb_dn,
7913 if (ret != LDB_SUCCESS) {
7917 DBG_WARNING("Using existing target %s, over received value %s\n",
7918 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7919 ldb_dn_get_linearized(dsdb_dn->dn));
7922 * we want to keep our existing active link and add the
7923 * received link as inactive
7925 *add_as_inactive = true;
7932 process one linked attribute structure
7934 static int replmd_process_linked_attribute(struct ldb_module *module,
7935 TALLOC_CTX *mem_ctx,
7936 struct replmd_private *replmd_private,
7937 struct ldb_message *msg,
7938 const struct dsdb_attribute *attr,
7939 struct la_entry *la_entry,
7940 struct ldb_request *parent,
7941 replmd_link_changed *change)
7943 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7944 struct ldb_context *ldb = ldb_module_get_ctx(module);
7945 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7947 struct dsdb_dn *dsdb_dn = NULL;
7948 uint64_t seq_num = 0;
7949 struct ldb_message_element *old_el;
7950 time_t t = time(NULL);
7951 struct parsed_dn *pdn_list, *pdn, *next;
7952 struct GUID guid = GUID_zero();
7953 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7955 struct dsdb_dn *old_dsdb_dn = NULL;
7956 struct ldb_val *val_to_update = NULL;
7957 bool add_as_inactive = false;
7960 *change = LINK_CHANGE_NONE;
7962 /* the value blob for the attribute holds the target object DN */
7963 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7964 la->value.blob, &dsdb_dn);
7965 if (!W_ERROR_IS_OK(status)) {
7966 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7967 attr->lDAPDisplayName,
7968 ldb_dn_get_linearized(msg->dn),
7969 win_errstr(status));
7970 return LDB_ERR_OPERATIONS_ERROR;
7973 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7974 if (old_el == NULL) {
7975 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7976 if (ret != LDB_SUCCESS) {
7977 ldb_module_oom(module);
7978 return LDB_ERR_OPERATIONS_ERROR;
7981 old_el->flags = LDB_FLAG_MOD_REPLACE;
7984 /* parse the existing links */
7985 ret = get_parsed_dns_trusted(module, replmd_private, mem_ctx, old_el, &pdn_list,
7986 attr->syntax->ldap_oid, parent);
7988 if (ret != LDB_SUCCESS) {
7992 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7993 true, &guid, &ignore_link);
7995 if (ret != LDB_SUCCESS) {
8000 * there are some cases where the target object doesn't exist, but it's
8001 * OK to ignore the linked attribute
8007 /* see if this link already exists */
8008 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8011 dsdb_dn->extra_part, 0,
8013 attr->syntax->ldap_oid,
8015 if (ret != LDB_SUCCESS) {
8019 if (!replmd_link_update_is_newer(pdn, la)) {
8020 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8021 old_el->name, ldb_dn_get_linearized(msg->dn),
8022 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8026 /* get a seq_num for this change */
8027 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8028 if (ret != LDB_SUCCESS) {
8033 * check for single-valued link conflicts, i.e. an active linked
8034 * attribute already exists, but it has a different target value
8037 ret = replmd_check_singleval_la_conflict(module, replmd_private,
8038 mem_ctx, msg->dn, la,
8039 dsdb_dn, pdn, pdn_list,
8040 old_el, schema, attr,
8043 if (ret != LDB_SUCCESS) {
8049 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8051 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8052 /* remove the existing backlink */
8053 ret = replmd_add_backlink(module, replmd_private,
8056 &pdn->guid, false, attr,
8058 if (ret != LDB_SUCCESS) {
8063 val_to_update = pdn->v;
8064 old_dsdb_dn = pdn->dsdb_dn;
8065 *change = LINK_CHANGE_MODIFIED;
8071 * We know where the new one needs to be, from the *next
8072 * pointer into pdn_list.
8075 offset = old_el->num_values;
8077 if (next->dsdb_dn == NULL) {
8078 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8079 attr->syntax->ldap_oid);
8080 if (ret != LDB_SUCCESS) {
8084 offset = next - pdn_list;
8085 if (offset > old_el->num_values) {
8086 return LDB_ERR_OPERATIONS_ERROR;
8090 old_el->values = talloc_realloc(msg->elements, old_el->values,
8091 struct ldb_val, old_el->num_values+1);
8092 if (!old_el->values) {
8093 ldb_module_oom(module);
8094 return LDB_ERR_OPERATIONS_ERROR;
8097 if (offset != old_el->num_values) {
8098 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8099 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8102 old_el->num_values++;
8104 val_to_update = &old_el->values[offset];
8106 *change = LINK_CHANGE_ADDED;
8109 /* set the link attribute's value to the info that was received */
8110 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8111 &la->meta_data.originating_invocation_id,
8112 la->meta_data.originating_usn, seq_num,
8113 la->meta_data.originating_change_time,
8114 la->meta_data.version,
8116 if (ret != LDB_SUCCESS) {
8120 if (add_as_inactive) {
8122 /* Set the new link as inactive/deleted to avoid conflicts */
8123 ret = replmd_delete_link_value(module, replmd_private, old_el,
8124 msg->dn, schema, attr, seq_num,
8125 false, &guid, dsdb_dn,
8128 if (ret != LDB_SUCCESS) {
8132 } else if (active) {
8134 /* if the new link is active, then add the new backlink */
8135 ret = replmd_add_backlink(module, replmd_private,
8140 if (ret != LDB_SUCCESS) {
8145 /* we only change whenChanged and uSNChanged if the seq_num
8147 ldb_msg_remove_attr(msg, "whenChanged");
8148 ldb_msg_remove_attr(msg, "uSNChanged");
8149 ret = add_time_element(msg, "whenChanged", t);
8150 if (ret != LDB_SUCCESS) {
8155 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8156 if (ret != LDB_SUCCESS) {
8161 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8162 if (old_el == NULL) {
8163 return ldb_operr(ldb);
8166 ret = dsdb_check_single_valued_link(attr, old_el);
8167 if (ret != LDB_SUCCESS) {
8171 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8176 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8178 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8179 return replmd_extended_replicated_objects(module, req);
8182 return ldb_next_request(module, req);
8187 we hook into the transaction operations to allow us to
8188 perform the linked attribute updates at the end of the whole
8189 transaction. This allows a forward linked attribute to be created
8190 before the object is created. During a vampire, w2k8 sends us linked
8191 attributes before the objects they are part of.
8193 static int replmd_start_transaction(struct ldb_module *module)
8195 /* create our private structure for this transaction */
8196 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8197 struct replmd_private);
8198 replmd_txn_cleanup(replmd_private);
8200 /* free any leftover mod_usn records from cancelled
8202 while (replmd_private->ncs) {
8203 struct nc_entry *e = replmd_private->ncs;
8204 DLIST_REMOVE(replmd_private->ncs, e);
8208 replmd_private->originating_updates = false;
8210 return ldb_next_start_trans(module);
8214 * Processes a group of linked attributes that apply to the same source-object
8217 static int replmd_process_la_group(struct ldb_module *module,
8218 struct replmd_private *replmd_private,
8219 struct la_group *la_group)
8221 struct la_entry *la = NULL;
8222 struct la_entry *prev = NULL;
8224 TALLOC_CTX *tmp_ctx = talloc_new(la_group);
8225 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8226 struct ldb_message *msg = NULL;
8227 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8228 struct ldb_context *ldb = ldb_module_get_ctx(module);
8229 const struct dsdb_attribute *attr = NULL;
8230 replmd_link_changed change_type;
8231 uint32_t num_changes = 0;
8234 * get the attribute being modified and the search result for the
8237 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8240 if (ret != LDB_SUCCESS) {
8245 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8246 * ProcessLinkValue, because link updates are not applied to
8247 * recycled and tombstone objects. We don't have to delete
8248 * any existing link, that should have happened when the
8249 * object deletion was replicated or initiated.
8251 * This needs isDeleted and isRecycled to be included as
8252 * attributes in the search and so in msg if set.
8254 replmd_deletion_state(module, msg, &deletion_state, NULL);
8256 if (deletion_state >= OBJECT_RECYCLED) {
8257 TALLOC_FREE(tmp_ctx);
8262 * Now that we know the deletion_state, remove the extra
8263 * attributes added for that purpose. We need to do this
8264 * otherwise in the case of isDeleted: FALSE the modify will
8267 * Failed to apply linked attribute change 'attribute 'isDeleted':
8268 * invalid modify flags on
8269 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8272 * This is becaue isDeleted is a Boolean, so FALSE is a
8273 * legitimate value (set by Samba's deletetest.py)
8275 ldb_msg_remove_attr(msg, "isDeleted");
8276 ldb_msg_remove_attr(msg, "isRecycled");
8278 /* go through and process the link targets for this source object */
8279 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8280 prev = DLIST_PREV(la);
8281 DLIST_REMOVE(la_group->la_entries, la);
8282 ret = replmd_process_linked_attribute(module, tmp_ctx,
8284 msg, attr, la, NULL,
8286 if (ret != LDB_SUCCESS) {
8287 replmd_txn_cleanup(replmd_private);
8291 if (change_type != LINK_CHANGE_NONE) {
8295 if ((++replmd_private->num_processed % 8192) == 0) {
8296 DBG_NOTICE("Processed %u/%u linked attributes\n",
8297 replmd_private->num_processed,
8298 replmd_private->total_links);
8303 * it's possible we're already up-to-date and so don't need to modify
8304 * the object at all (e.g. doing a 'drs replicate --full-sync')
8306 if (num_changes == 0) {
8307 TALLOC_FREE(tmp_ctx);
8311 /* apply the link changes to the source object */
8312 ret = linked_attr_modify(module, msg, NULL);
8313 if (ret != LDB_SUCCESS) {
8314 ldb_debug(ldb, LDB_DEBUG_WARNING,
8315 "Failed to apply linked attribute change '%s'\n%s\n",
8317 ldb_ldif_message_redacted_string(ldb,
8319 LDB_CHANGETYPE_MODIFY,
8321 TALLOC_FREE(tmp_ctx);
8325 TALLOC_FREE(tmp_ctx);
8330 on prepare commit we loop over our queued la_context structures and
8333 static int replmd_prepare_commit(struct ldb_module *module)
8335 struct replmd_private *replmd_private =
8336 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8337 struct la_group *la_group, *prev;
8340 if (replmd_private->la_list != NULL) {
8341 DBG_NOTICE("Processing linked attributes\n");
8345 * Walk the list of linked attributes from DRS replication.
8347 * We walk backwards, to do the first entry first, as we
8348 * added the entries with DLIST_ADD() which puts them at the
8351 * Links are grouped together so we process links for the same
8352 * source object in one go.
8354 for (la_group = DLIST_TAIL(replmd_private->la_list);
8358 prev = DLIST_PREV(la_group);
8359 DLIST_REMOVE(replmd_private->la_list, la_group);
8360 ret = replmd_process_la_group(module, replmd_private,
8362 if (ret != LDB_SUCCESS) {
8363 replmd_txn_cleanup(replmd_private);
8368 replmd_txn_cleanup(replmd_private);
8370 /* possibly change @REPLCHANGED */
8371 ret = replmd_notify_store(module, NULL);
8372 if (ret != LDB_SUCCESS) {
8376 return ldb_next_prepare_commit(module);
8379 static int replmd_del_transaction(struct ldb_module *module)
8381 struct replmd_private *replmd_private =
8382 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8383 replmd_txn_cleanup(replmd_private);
8385 return ldb_next_del_trans(module);
8389 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8390 .name = "repl_meta_data",
8391 .init_context = replmd_init,
8393 .modify = replmd_modify,
8394 .rename = replmd_rename,
8395 .del = replmd_delete,
8396 .extended = replmd_extended,
8397 .start_transaction = replmd_start_transaction,
8398 .prepare_commit = replmd_prepare_commit,
8399 .del_transaction = replmd_del_transaction,
8402 int ldb_repl_meta_data_module_init(const char *version)
8404 LDB_MODULE_CHECK_VERSION(version);
8405 return ldb_register_module(&ldb_repl_meta_data_module_ops);