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;
137 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
138 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
139 static int replmd_check_upgrade_links(struct ldb_context *ldb,
140 struct parsed_dn *dns, uint32_t count,
141 struct ldb_message_element *el,
142 const char *ldap_oid);
143 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
145 struct la_entry *la_entry,
146 struct ldb_dn *src_dn,
147 const struct dsdb_attribute *attr);
148 static int replmd_get_la_entry_source(struct ldb_module *module,
149 struct la_entry *la_entry,
151 const struct dsdb_attribute **ret_attr,
152 struct ldb_message **source_msg);
153 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
154 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
155 uint64_t usn, uint64_t local_usn, NTTIME nttime,
156 uint32_t version, bool deleted);
158 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
159 struct ldb_context *ldb,
161 const char *rdn_name,
162 const struct ldb_val *rdn_value,
165 enum urgent_situation {
166 REPL_URGENT_ON_CREATE = 1,
167 REPL_URGENT_ON_UPDATE = 2,
168 REPL_URGENT_ON_DELETE = 4
171 enum deletion_state {
172 OBJECT_NOT_DELETED=1,
179 static bool replmd_recyclebin_enabled(struct ldb_module *module)
181 bool enabled = false;
182 struct replmd_private *replmd_private =
183 talloc_get_type_abort(ldb_module_get_private(module),
184 struct replmd_private);
187 * only lookup the recycle-bin state once per replication, then cache
188 * the result. This can save us 1000s of DB searches
190 if (!replmd_private->recyclebin_state_known) {
191 int ret = dsdb_recyclebin_enabled(module, &enabled);
192 if (ret != LDB_SUCCESS) {
196 replmd_private->recyclebin_enabled = enabled;
197 replmd_private->recyclebin_state_known = true;
200 return replmd_private->recyclebin_enabled;
203 static void replmd_deletion_state(struct ldb_module *module,
204 const struct ldb_message *msg,
205 enum deletion_state *current_state,
206 enum deletion_state *next_state)
208 bool enabled = false;
211 *current_state = OBJECT_REMOVED;
212 if (next_state != NULL) {
213 *next_state = OBJECT_REMOVED;
218 enabled = replmd_recyclebin_enabled(module);
220 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
222 *current_state = OBJECT_TOMBSTONE;
223 if (next_state != NULL) {
224 *next_state = OBJECT_REMOVED;
229 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
230 *current_state = OBJECT_RECYCLED;
231 if (next_state != NULL) {
232 *next_state = OBJECT_REMOVED;
237 *current_state = OBJECT_DELETED;
238 if (next_state != NULL) {
239 *next_state = OBJECT_RECYCLED;
244 *current_state = OBJECT_NOT_DELETED;
245 if (next_state == NULL) {
250 *next_state = OBJECT_DELETED;
252 *next_state = OBJECT_TOMBSTONE;
256 static const struct {
257 const char *update_name;
258 enum urgent_situation repl_situation;
259 } urgent_objects[] = {
260 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
261 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
262 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
263 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
264 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
265 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
269 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
270 static const char *urgent_attrs[] = {
273 "userAccountControl",
278 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
279 enum urgent_situation situation)
282 for (i=0; urgent_objects[i].update_name; i++) {
284 if ((situation & urgent_objects[i].repl_situation) == 0) {
288 for (j=0; j<objectclass_el->num_values; j++) {
289 const struct ldb_val *v = &objectclass_el->values[j];
290 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
298 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
300 if (ldb_attr_in_list(urgent_attrs, el->name)) {
306 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
309 initialise the module
310 allocate the private structure and build the list
311 of partition DNs for use by replmd_notify()
313 static int replmd_init(struct ldb_module *module)
315 struct replmd_private *replmd_private;
316 struct ldb_context *ldb = ldb_module_get_ctx(module);
317 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
318 struct ldb_dn *samba_dsdb_dn;
319 struct ldb_result *res;
321 TALLOC_CTX *frame = talloc_stackframe();
322 replmd_private = talloc_zero(module, struct replmd_private);
323 if (replmd_private == NULL) {
326 return LDB_ERR_OPERATIONS_ERROR;
328 ldb_module_set_private(module, replmd_private);
330 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
332 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
333 if (!samba_dsdb_dn) {
338 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
339 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
340 if (ret == LDB_SUCCESS) {
341 replmd_private->sorted_links
342 = ldb_msg_check_string_attribute(res->msgs[0],
343 SAMBA_COMPATIBLE_FEATURES_ATTR,
344 SAMBA_SORTED_LINKS_FEATURE);
348 return ldb_next_init(module);
352 cleanup our per-transaction contexts
354 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
356 talloc_free(replmd_private->la_ctx);
357 replmd_private->la_list = NULL;
358 replmd_private->la_ctx = NULL;
359 replmd_private->recyclebin_state_known = false;
364 struct la_backlink *next, *prev;
365 const char *attr_name;
366 struct ldb_dn *forward_dn;
367 struct GUID target_guid;
372 a ldb_modify request operating on modules below the
375 static int linked_attr_modify(struct ldb_module *module,
376 const struct ldb_message *message,
377 struct ldb_request *parent)
379 struct ldb_request *mod_req;
381 struct ldb_context *ldb = ldb_module_get_ctx(module);
382 TALLOC_CTX *tmp_ctx = talloc_new(module);
383 struct ldb_result *res;
385 res = talloc_zero(tmp_ctx, struct ldb_result);
387 talloc_free(tmp_ctx);
388 return ldb_oom(ldb_module_get_ctx(module));
391 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
395 ldb_modify_default_callback,
397 LDB_REQ_SET_LOCATION(mod_req);
398 if (ret != LDB_SUCCESS) {
399 talloc_free(tmp_ctx);
403 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
405 if (ret != LDB_SUCCESS) {
409 /* Run the new request */
410 ret = ldb_next_request(module, mod_req);
412 if (ret == LDB_SUCCESS) {
413 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
416 talloc_free(tmp_ctx);
421 process a backlinks we accumulated during a transaction, adding and
422 deleting the backlinks from the target objects
424 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
426 struct ldb_dn *target_dn, *source_dn;
428 struct ldb_context *ldb = ldb_module_get_ctx(module);
429 struct ldb_message *msg;
430 TALLOC_CTX *frame = talloc_stackframe();
436 - construct ldb_message
437 - either an add or a delete
439 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
440 if (ret != LDB_SUCCESS) {
441 struct GUID_txt_buf guid_str;
442 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
443 GUID_buf_string(&bl->target_guid, &guid_str));
444 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
449 msg = ldb_msg_new(frame);
451 ldb_module_oom(module);
453 return LDB_ERR_OPERATIONS_ERROR;
456 source_dn = ldb_dn_copy(frame, bl->forward_dn);
458 ldb_module_oom(module);
460 return LDB_ERR_OPERATIONS_ERROR;
462 /* Filter down to the attributes we want in the backlink */
463 const char *accept[] = { "GUID", "SID", NULL };
464 ldb_dn_extended_filter(source_dn, accept);
467 /* construct a ldb_message for adding/deleting the backlink */
469 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
471 ldb_module_oom(module);
473 return LDB_ERR_OPERATIONS_ERROR;
475 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
476 if (ret != LDB_SUCCESS) {
480 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
482 /* a backlink should never be single valued. Unfortunately the
483 exchange schema has a attribute
484 msExchBridgeheadedLocalConnectorsDNBL which is single
485 valued and a backlink. We need to cope with that by
486 ignoring the single value flag */
487 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
489 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
490 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
491 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
492 cope with possible corruption where the backlink has
493 already been removed */
494 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
495 ldb_dn_get_linearized(target_dn),
496 ldb_dn_get_linearized(source_dn),
497 ldb_errstring(ldb)));
499 } else if (ret != LDB_SUCCESS) {
500 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
501 bl->active?"add":"remove",
502 ldb_dn_get_linearized(source_dn),
503 ldb_dn_get_linearized(target_dn),
513 add a backlink to the list of backlinks to add/delete in the prepare
516 forward_dn is stolen onto the defereed context
518 static int replmd_defer_add_backlink(struct ldb_module *module,
519 struct replmd_private *replmd_private,
520 const struct dsdb_schema *schema,
521 struct replmd_replicated_request *ac,
522 struct ldb_dn *forward_dn,
523 struct GUID *target_guid, bool active,
524 const struct dsdb_attribute *schema_attr,
525 struct ldb_request *parent)
527 const struct dsdb_attribute *target_attr;
528 struct la_backlink *bl;
530 bl = talloc(ac, struct la_backlink);
532 ldb_module_oom(module);
533 return LDB_ERR_OPERATIONS_ERROR;
536 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
539 * windows 2003 has a broken schema where the
540 * definition of msDS-IsDomainFor is missing (which is
541 * supposed to be the backlink of the
542 * msDS-HasDomainNCs attribute
547 bl->attr_name = target_attr->lDAPDisplayName;
548 bl->forward_dn = talloc_steal(bl, forward_dn);
549 bl->target_guid = *target_guid;
552 DLIST_ADD(ac->la_backlinks, bl);
558 add a backlink to the list of backlinks to add/delete in the prepare
561 static int replmd_add_backlink(struct ldb_module *module,
562 struct replmd_private *replmd_private,
563 const struct dsdb_schema *schema,
564 struct ldb_dn *forward_dn,
565 struct GUID *target_guid, bool active,
566 const struct dsdb_attribute *schema_attr,
567 struct ldb_request *parent)
569 const struct dsdb_attribute *target_attr;
570 struct la_backlink bl;
573 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
576 * windows 2003 has a broken schema where the
577 * definition of msDS-IsDomainFor is missing (which is
578 * supposed to be the backlink of the
579 * msDS-HasDomainNCs attribute
584 bl.attr_name = target_attr->lDAPDisplayName;
585 bl.forward_dn = forward_dn;
586 bl.target_guid = *target_guid;
589 ret = replmd_process_backlink(module, &bl, parent);
595 * Callback for most write operations in this module:
597 * notify the repl task that a object has changed. The notifies are
598 * gathered up in the replmd_private structure then written to the
599 * @REPLCHANGED object in each partition during the prepare_commit
601 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
604 struct replmd_replicated_request *ac =
605 talloc_get_type_abort(req->context, struct replmd_replicated_request);
606 struct replmd_private *replmd_private =
607 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
608 struct nc_entry *modified_partition;
609 struct ldb_control *partition_ctrl;
610 const struct dsdb_control_current_partition *partition;
612 struct ldb_control **controls;
614 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
616 controls = ares->controls;
617 if (ldb_request_get_control(ac->req,
618 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
620 * Remove the current partition control from what we pass up
621 * the chain if it hasn't been requested manually.
623 controls = ldb_controls_except_specified(ares->controls, ares,
627 if (ares->error != LDB_SUCCESS) {
628 struct GUID_txt_buf guid_txt;
629 struct ldb_message *msg = NULL;
632 if (ac->apply_mode == false) {
633 DBG_NOTICE("Originating update failure. Error is: %s\n",
634 ldb_strerror(ares->error));
635 return ldb_module_done(ac->req, controls,
636 ares->response, ares->error);
639 msg = ac->objs->objects[ac->index_current].msg;
641 * Set at DBG_NOTICE as once these start to happe, they
642 * will happen a lot until resolved, due to repeated
643 * replication. The caller will probably print the
644 * ldb error string anyway.
646 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
647 ldb_dn_get_linearized(msg->dn),
648 ldb_strerror(ares->error));
650 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
655 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
656 ac->search_msg == NULL ? "ADD" : "MODIFY",
657 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
661 return ldb_module_done(ac->req, controls,
662 ares->response, ares->error);
665 if (ares->type != LDB_REPLY_DONE) {
666 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
667 return ldb_module_done(ac->req, NULL,
668 NULL, LDB_ERR_OPERATIONS_ERROR);
671 if (ac->apply_mode == false) {
672 struct la_backlink *bl;
674 * process our backlink list after an replmd_add(),
675 * creating and deleting backlinks as necessary (this
676 * code is sync). The other cases are handled inline
679 for (bl=ac->la_backlinks; bl; bl=bl->next) {
680 ret = replmd_process_backlink(ac->module, bl, ac->req);
681 if (ret != LDB_SUCCESS) {
682 return ldb_module_done(ac->req, NULL,
688 if (!partition_ctrl) {
689 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
690 return ldb_module_done(ac->req, NULL,
691 NULL, LDB_ERR_OPERATIONS_ERROR);
694 partition = talloc_get_type_abort(partition_ctrl->data,
695 struct dsdb_control_current_partition);
697 if (ac->seq_num > 0) {
698 for (modified_partition = replmd_private->ncs; modified_partition;
699 modified_partition = modified_partition->next) {
700 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
705 if (modified_partition == NULL) {
706 modified_partition = talloc_zero(replmd_private, struct nc_entry);
707 if (!modified_partition) {
708 ldb_oom(ldb_module_get_ctx(ac->module));
709 return ldb_module_done(ac->req, NULL,
710 NULL, LDB_ERR_OPERATIONS_ERROR);
712 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
713 if (!modified_partition->dn) {
714 ldb_oom(ldb_module_get_ctx(ac->module));
715 return ldb_module_done(ac->req, NULL,
716 NULL, LDB_ERR_OPERATIONS_ERROR);
718 DLIST_ADD(replmd_private->ncs, modified_partition);
721 if (ac->seq_num > modified_partition->mod_usn) {
722 modified_partition->mod_usn = ac->seq_num;
724 modified_partition->mod_usn_urgent = ac->seq_num;
727 if (!ac->apply_mode) {
728 replmd_private->originating_updates = true;
732 if (ac->apply_mode) {
733 ret = replmd_replicated_apply_isDeleted(ac);
734 if (ret != LDB_SUCCESS) {
735 return ldb_module_done(ac->req, NULL, NULL, ret);
739 /* free the partition control container here, for the
740 * common path. Other cases will have it cleaned up
741 * eventually with the ares */
742 talloc_free(partition_ctrl);
743 return ldb_module_done(ac->req, controls,
744 ares->response, LDB_SUCCESS);
750 * update a @REPLCHANGED record in each partition if there have been
751 * any writes of replicated data in the partition
753 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
755 struct replmd_private *replmd_private =
756 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
758 while (replmd_private->ncs) {
760 struct nc_entry *modified_partition = replmd_private->ncs;
762 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
763 modified_partition->mod_usn,
764 modified_partition->mod_usn_urgent, parent);
765 if (ret != LDB_SUCCESS) {
766 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
767 ldb_dn_get_linearized(modified_partition->dn)));
771 if (ldb_dn_compare(modified_partition->dn,
772 replmd_private->schema_dn) == 0) {
773 struct ldb_result *ext_res;
774 ret = dsdb_module_extended(module,
775 replmd_private->schema_dn,
777 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
779 DSDB_FLAG_NEXT_MODULE,
781 if (ret != LDB_SUCCESS) {
784 talloc_free(ext_res);
787 DLIST_REMOVE(replmd_private->ncs, modified_partition);
788 talloc_free(modified_partition);
796 created a replmd_replicated_request context
798 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
799 struct ldb_request *req)
801 struct ldb_context *ldb;
802 struct replmd_replicated_request *ac;
803 const struct GUID *our_invocation_id;
805 ldb = ldb_module_get_ctx(module);
807 ac = talloc_zero(req, struct replmd_replicated_request);
816 ac->schema = dsdb_get_schema(ldb, ac);
818 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
819 "replmd_modify: no dsdb_schema loaded");
820 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
825 /* get our invocationId */
826 our_invocation_id = samdb_ntds_invocation_id(ldb);
827 if (!our_invocation_id) {
828 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
829 "replmd_add: unable to find invocationId\n");
833 ac->our_invocation_id = *our_invocation_id;
839 add a time element to a record
841 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
843 struct ldb_message_element *el;
847 if (ldb_msg_find_element(msg, attr) != NULL) {
851 s = ldb_timestring(msg, t);
853 return LDB_ERR_OPERATIONS_ERROR;
856 ret = ldb_msg_add_string(msg, attr, s);
857 if (ret != LDB_SUCCESS) {
861 el = ldb_msg_find_element(msg, attr);
862 /* always set as replace. This works because on add ops, the flag
864 el->flags = LDB_FLAG_MOD_REPLACE;
870 add a uint64_t element to a record
872 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
873 const char *attr, uint64_t v)
875 struct ldb_message_element *el;
878 if (ldb_msg_find_element(msg, attr) != NULL) {
882 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
883 if (ret != LDB_SUCCESS) {
887 el = ldb_msg_find_element(msg, attr);
888 /* always set as replace. This works because on add ops, the flag
890 el->flags = LDB_FLAG_MOD_REPLACE;
895 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
896 const struct replPropertyMetaData1 *m2,
897 const uint32_t *rdn_attid)
900 * This assignment seems inoccous, but it is critical for the
901 * system, as we need to do the comparisons as a unsigned
902 * quantity, not signed (enums are signed integers)
904 uint32_t attid_1 = m1->attid;
905 uint32_t attid_2 = m2->attid;
907 if (attid_1 == attid_2) {
912 * See above regarding this being an unsigned comparison.
913 * Otherwise when the high bit is set on non-standard
914 * attributes, they would end up first, before objectClass
917 return attid_1 > attid_2 ? 1 : -1;
920 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
921 struct replPropertyMetaDataCtr1 *ctr1,
924 if (ctr1->count == 0) {
925 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
926 "No elements found in replPropertyMetaData for %s!\n",
927 ldb_dn_get_linearized(dn));
928 return LDB_ERR_CONSTRAINT_VIOLATION;
931 /* the objectClass attribute is value 0x00000000, so must be first */
932 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
933 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
934 "No objectClass found in replPropertyMetaData for %s!\n",
935 ldb_dn_get_linearized(dn));
936 return LDB_ERR_OBJECT_CLASS_VIOLATION;
942 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
943 struct replPropertyMetaDataCtr1 *ctr1,
946 /* Note this is O(n^2) for the almost-sorted case, which this is */
947 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
948 replmd_replPropertyMetaData1_attid_sort);
949 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
952 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
953 const struct ldb_message_element *e2,
954 const struct dsdb_schema *schema)
956 const struct dsdb_attribute *a1;
957 const struct dsdb_attribute *a2;
960 * TODO: make this faster by caching the dsdb_attribute pointer
961 * on the ldb_messag_element
964 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
965 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
968 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
972 return strcasecmp(e1->name, e2->name);
974 if (a1->attributeID_id == a2->attributeID_id) {
977 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
980 static void replmd_ldb_message_sort(struct ldb_message *msg,
981 const struct dsdb_schema *schema)
983 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
986 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
987 const struct GUID *invocation_id,
988 uint64_t local_usn, NTTIME nttime);
990 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
992 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
993 struct ldb_message_element *el, struct parsed_dn **pdn,
994 const char *ldap_oid, struct ldb_request *parent);
996 static int check_parsed_dn_duplicates(struct ldb_module *module,
997 struct ldb_message_element *el,
998 struct parsed_dn *pdn);
1001 fix up linked attributes in replmd_add.
1002 This involves setting up the right meta-data in extended DN
1003 components, and creating backlinks to the object
1005 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1006 struct replmd_private *replmd_private,
1007 struct ldb_message_element *el,
1008 struct replmd_replicated_request *ac,
1010 struct ldb_dn *forward_dn,
1011 const struct dsdb_attribute *sa,
1012 struct ldb_request *parent)
1015 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1016 struct ldb_context *ldb = ldb_module_get_ctx(module);
1017 struct parsed_dn *pdn;
1018 /* We will take a reference to the schema in replmd_add_backlink */
1019 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
1020 struct ldb_val *new_values = NULL;
1023 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1024 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1026 ldb_asprintf_errstring(ldb,
1027 "Attribute %s is single valued but "
1028 "more than one value has been supplied",
1030 talloc_free(tmp_ctx);
1031 return LDB_ERR_CONSTRAINT_VIOLATION;
1034 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1035 sa->syntax->ldap_oid, parent);
1036 if (ret != LDB_SUCCESS) {
1037 talloc_free(tmp_ctx);
1041 ret = check_parsed_dn_duplicates(module, el, pdn);
1042 if (ret != LDB_SUCCESS) {
1043 talloc_free(tmp_ctx);
1047 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1048 if (new_values == NULL) {
1049 ldb_module_oom(module);
1050 talloc_free(tmp_ctx);
1051 return LDB_ERR_OPERATIONS_ERROR;
1054 for (i = 0; i < el->num_values; i++) {
1055 struct parsed_dn *p = &pdn[i];
1056 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1057 &ac->our_invocation_id,
1059 if (ret != LDB_SUCCESS) {
1060 talloc_free(tmp_ctx);
1064 ret = replmd_defer_add_backlink(module, replmd_private,
1066 forward_dn, &p->guid, true, sa,
1068 if (ret != LDB_SUCCESS) {
1069 talloc_free(tmp_ctx);
1073 new_values[i] = *p->v;
1075 el->values = talloc_steal(mem_ctx, new_values);
1077 talloc_free(tmp_ctx);
1081 static int replmd_add_make_extended_dn(struct ldb_request *req,
1082 const DATA_BLOB *guid_blob,
1083 struct ldb_dn **_extended_dn)
1086 const DATA_BLOB *sid_blob;
1087 /* Calculate an extended DN for any linked attributes */
1088 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1090 return LDB_ERR_OPERATIONS_ERROR;
1092 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1093 if (ret != LDB_SUCCESS) {
1097 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1098 if (sid_blob != NULL) {
1099 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1100 if (ret != LDB_SUCCESS) {
1104 *_extended_dn = extended_dn;
1109 intercept add requests
1111 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1113 struct ldb_context *ldb;
1114 struct ldb_control *control;
1115 struct replmd_replicated_request *ac;
1116 enum ndr_err_code ndr_err;
1117 struct ldb_request *down_req;
1118 struct ldb_message *msg;
1119 const DATA_BLOB *guid_blob;
1120 DATA_BLOB guid_blob_stack;
1122 uint8_t guid_data[16];
1123 struct replPropertyMetaDataBlob nmd;
1124 struct ldb_val nmd_value;
1125 struct ldb_dn *extended_dn = NULL;
1128 * The use of a time_t here seems odd, but as the NTTIME
1129 * elements are actually declared as NTTIME_1sec in the IDL,
1130 * getting a higher resolution timestamp is not required.
1132 time_t t = time(NULL);
1137 unsigned int functional_level;
1139 bool allow_add_guid = false;
1140 bool remove_current_guid = false;
1141 bool is_urgent = false;
1142 bool is_schema_nc = false;
1143 struct ldb_message_element *objectclass_el;
1144 struct replmd_private *replmd_private =
1145 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1147 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1148 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1150 allow_add_guid = true;
1153 /* do not manipulate our control entries */
1154 if (ldb_dn_is_special(req->op.add.message->dn)) {
1155 return ldb_next_request(module, req);
1158 ldb = ldb_module_get_ctx(module);
1160 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1162 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1163 if (guid_blob != NULL) {
1164 if (!allow_add_guid) {
1165 ldb_set_errstring(ldb,
1166 "replmd_add: it's not allowed to add an object with objectGUID!");
1167 return LDB_ERR_UNWILLING_TO_PERFORM;
1169 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1170 if (!NT_STATUS_IS_OK(status)) {
1171 ldb_set_errstring(ldb,
1172 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1173 return LDB_ERR_UNWILLING_TO_PERFORM;
1175 /* we remove this attribute as it can be a string and
1176 * will not be treated correctly and then we will re-add
1177 * it later on in the good format */
1178 remove_current_guid = true;
1182 guid = GUID_random();
1184 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1186 /* This can't fail */
1187 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1188 (ndr_push_flags_fn_t)ndr_push_GUID);
1189 guid_blob = &guid_blob_stack;
1192 ac = replmd_ctx_init(module, req);
1194 return ldb_module_oom(module);
1197 functional_level = dsdb_functional_level(ldb);
1199 /* Get a sequence number from the backend */
1200 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1201 if (ret != LDB_SUCCESS) {
1206 /* we have to copy the message as the caller might have it as a const */
1207 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1211 return LDB_ERR_OPERATIONS_ERROR;
1214 /* generated times */
1215 unix_to_nt_time(&now, t);
1216 time_str = ldb_timestring(msg, t);
1220 return LDB_ERR_OPERATIONS_ERROR;
1222 if (remove_current_guid) {
1223 ldb_msg_remove_attr(msg,"objectGUID");
1227 * remove autogenerated attributes
1229 ldb_msg_remove_attr(msg, "whenCreated");
1230 ldb_msg_remove_attr(msg, "whenChanged");
1231 ldb_msg_remove_attr(msg, "uSNCreated");
1232 ldb_msg_remove_attr(msg, "uSNChanged");
1233 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1236 * readd replicated attributes
1238 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1239 if (ret != LDB_SUCCESS) {
1245 /* build the replication meta_data */
1248 nmd.ctr.ctr1.count = msg->num_elements;
1249 nmd.ctr.ctr1.array = talloc_array(msg,
1250 struct replPropertyMetaData1,
1251 nmd.ctr.ctr1.count);
1252 if (!nmd.ctr.ctr1.array) {
1255 return LDB_ERR_OPERATIONS_ERROR;
1258 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1260 for (i=0; i < msg->num_elements;) {
1261 struct ldb_message_element *e = &msg->elements[i];
1262 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1263 const struct dsdb_attribute *sa;
1265 if (e->name[0] == '@') {
1270 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1272 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1273 "replmd_add: attribute '%s' not defined in schema\n",
1276 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1279 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1280 /* if the attribute is not replicated (0x00000001)
1281 * or constructed (0x00000004) it has no metadata
1287 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1288 if (extended_dn == NULL) {
1289 ret = replmd_add_make_extended_dn(req,
1292 if (ret != LDB_SUCCESS) {
1299 * Prepare the context for the backlinks and
1300 * create metadata for the forward links. The
1301 * backlinks are created in
1302 * replmd_op_callback() after the successful
1303 * ADD of the object.
1305 ret = replmd_add_fix_la(module, msg->elements,
1310 if (ret != LDB_SUCCESS) {
1314 /* linked attributes are not stored in
1315 replPropertyMetaData in FL above w2k */
1320 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1322 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1323 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1326 if (rdn_val == NULL) {
1329 return LDB_ERR_OPERATIONS_ERROR;
1332 rdn = (const char*)rdn_val->data;
1333 if (strcmp(rdn, "Deleted Objects") == 0) {
1335 * Set the originating_change_time to 29/12/9999 at 23:59:59
1336 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1338 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1340 m->originating_change_time = now;
1343 m->originating_change_time = now;
1345 m->originating_invocation_id = ac->our_invocation_id;
1346 m->originating_usn = ac->seq_num;
1347 m->local_usn = ac->seq_num;
1350 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1355 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1357 if (e->num_values != 0) {
1362 ldb_msg_remove_element(msg, e);
1365 /* fix meta data count */
1366 nmd.ctr.ctr1.count = ni;
1369 * sort meta data array
1371 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1372 if (ret != LDB_SUCCESS) {
1373 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1378 /* generated NDR encoded values */
1379 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1381 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1382 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1385 return LDB_ERR_OPERATIONS_ERROR;
1389 * add the autogenerated values
1391 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1392 if (ret != LDB_SUCCESS) {
1397 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1398 if (ret != LDB_SUCCESS) {
1403 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1404 if (ret != LDB_SUCCESS) {
1409 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1410 if (ret != LDB_SUCCESS) {
1415 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1416 if (ret != LDB_SUCCESS) {
1423 * sort the attributes by attid before storing the object
1425 replmd_ldb_message_sort(msg, ac->schema);
1428 * Assert that we do have an objectClass
1430 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1431 if (objectclass_el == NULL) {
1432 ldb_asprintf_errstring(ldb, __location__
1433 ": objectClass missing on %s\n",
1434 ldb_dn_get_linearized(msg->dn));
1436 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1438 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1439 REPL_URGENT_ON_CREATE);
1441 ac->is_urgent = is_urgent;
1442 ret = ldb_build_add_req(&down_req, ldb, ac,
1445 ac, replmd_op_callback,
1448 LDB_REQ_SET_LOCATION(down_req);
1449 if (ret != LDB_SUCCESS) {
1454 /* current partition control is needed by "replmd_op_callback" */
1455 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1456 ret = ldb_request_add_control(down_req,
1457 DSDB_CONTROL_CURRENT_PARTITION_OID,
1459 if (ret != LDB_SUCCESS) {
1465 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1466 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1467 if (ret != LDB_SUCCESS) {
1473 /* mark the control done */
1475 control->critical = 0;
1477 /* go on with the call chain */
1478 return ldb_next_request(module, down_req);
1483 * update the replPropertyMetaData for one element
1485 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1486 struct ldb_message *msg,
1487 struct ldb_message_element *el,
1488 struct ldb_message_element *old_el,
1489 struct replPropertyMetaDataBlob *omd,
1490 const struct dsdb_schema *schema,
1492 const struct GUID *our_invocation_id,
1495 bool is_forced_rodc,
1496 struct ldb_request *req)
1499 const struct dsdb_attribute *a;
1500 struct replPropertyMetaData1 *md1;
1501 bool may_skip = false;
1504 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1506 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1507 /* allow this to make it possible for dbcheck
1508 to remove bad attributes */
1512 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1514 return LDB_ERR_OPERATIONS_ERROR;
1517 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1519 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1524 * if the attribute's value haven't changed, and this isn't
1525 * just a delete of everything then return LDB_SUCCESS Unless
1526 * we have the provision control or if the attribute is
1527 * interSiteTopologyGenerator as this page explain:
1528 * http://support.microsoft.com/kb/224815 this attribute is
1529 * periodicaly written by the DC responsible for the intersite
1530 * generation in a given site
1532 * Unchanged could be deleting or replacing an already-gone
1533 * thing with an unconstrained delete/empty replace or a
1534 * replace with the same value, but not an add with the same
1535 * value because that could be about adding a duplicate (which
1536 * is for someone else to error out on).
1538 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1539 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1542 } else if (old_el == NULL && el->num_values == 0) {
1543 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1545 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1548 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1549 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1551 * We intentionally skip the version bump when attempting to
1554 * The control is set by dbcheck and expunge-tombstones which
1555 * both attempt to be non-replicating. Otherwise, making an
1556 * alteration to the replication state would trigger a
1557 * broadcast of all expunged objects.
1562 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1564 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1568 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1569 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1571 * allow this to make it possible for dbcheck
1572 * to rebuild broken metadata
1578 for (i=0; i<omd->ctr.ctr1.count; i++) {
1580 * First check if we find it under the msDS-IntID,
1581 * then check if we find it under the OID and
1584 * This allows the administrator to simply re-write
1585 * the attributes and so restore replication, which is
1586 * likely what they will try to do.
1588 if (attid == omd->ctr.ctr1.array[i].attid) {
1592 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1597 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1598 /* linked attributes are not stored in
1599 replPropertyMetaData in FL above w2k, but we do
1600 raise the seqnum for the object */
1601 if (*seq_num == 0 &&
1602 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1603 return LDB_ERR_OPERATIONS_ERROR;
1608 if (i == omd->ctr.ctr1.count) {
1609 /* we need to add a new one */
1610 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1611 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1612 if (omd->ctr.ctr1.array == NULL) {
1614 return LDB_ERR_OPERATIONS_ERROR;
1616 omd->ctr.ctr1.count++;
1617 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1620 /* Get a new sequence number from the backend. We only do this
1621 * if we have a change that requires a new
1622 * replPropertyMetaData element
1624 if (*seq_num == 0) {
1625 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1626 if (ret != LDB_SUCCESS) {
1627 return LDB_ERR_OPERATIONS_ERROR;
1631 md1 = &omd->ctr.ctr1.array[i];
1635 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1636 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1639 if (rdn_val == NULL) {
1641 return LDB_ERR_OPERATIONS_ERROR;
1644 rdn = (const char*)rdn_val->data;
1645 if (strcmp(rdn, "Deleted Objects") == 0) {
1647 * Set the originating_change_time to 29/12/9999 at 23:59:59
1648 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1650 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1652 md1->originating_change_time = now;
1655 md1->originating_change_time = now;
1657 md1->originating_invocation_id = *our_invocation_id;
1658 md1->originating_usn = *seq_num;
1659 md1->local_usn = *seq_num;
1661 if (is_forced_rodc) {
1662 /* Force version to 0 to be overriden later via replication */
1670 * Bump the replPropertyMetaData version on an attribute, and if it
1671 * has changed (or forced by leaving rdn_old NULL), update the value
1674 * This is important, as calling a modify operation may not change the
1675 * version number if the values appear unchanged, but a rename between
1676 * parents bumps this value.
1679 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1680 struct ldb_message *msg,
1681 const struct ldb_val *rdn_new,
1682 const struct ldb_val *rdn_old,
1683 struct replPropertyMetaDataBlob *omd,
1684 struct replmd_replicated_request *ar,
1687 bool is_forced_rodc)
1689 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1690 const struct dsdb_attribute *rdn_attr =
1691 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1692 const char *attr_name = rdn_attr != NULL ?
1693 rdn_attr->lDAPDisplayName :
1695 struct ldb_message_element new_el = {
1696 .flags = LDB_FLAG_MOD_REPLACE,
1699 .values = discard_const_p(struct ldb_val, rdn_new)
1701 struct ldb_message_element old_el = {
1702 .flags = LDB_FLAG_MOD_REPLACE,
1704 .num_values = rdn_old ? 1 : 0,
1705 .values = discard_const_p(struct ldb_val, rdn_old)
1708 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1709 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1710 if (ret != LDB_SUCCESS) {
1711 return ldb_oom(ldb);
1715 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1716 omd, ar->schema, &ar->seq_num,
1717 &ar->our_invocation_id,
1718 now, is_schema_nc, is_forced_rodc,
1723 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1725 uint32_t count = omd.ctr.ctr1.count;
1728 for (i=0; i < count; i++) {
1729 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1730 if (max < m.local_usn) {
1738 * update the replPropertyMetaData object each time we modify an
1739 * object. This is needed for DRS replication, as the merge on the
1740 * client is based on this object
1742 static int replmd_update_rpmd(struct ldb_module *module,
1743 const struct dsdb_schema *schema,
1744 struct ldb_request *req,
1745 const char * const *rename_attrs,
1746 struct ldb_message *msg, uint64_t *seq_num,
1747 time_t t, bool is_schema_nc,
1748 bool *is_urgent, bool *rodc)
1750 const struct ldb_val *omd_value;
1751 enum ndr_err_code ndr_err;
1752 struct replPropertyMetaDataBlob omd;
1755 const struct GUID *our_invocation_id;
1757 const char * const *attrs = NULL;
1758 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1759 struct ldb_result *res;
1760 struct ldb_context *ldb;
1761 struct ldb_message_element *objectclass_el;
1762 enum urgent_situation situation;
1763 bool rmd_is_provided;
1764 bool rmd_is_just_resorted = false;
1765 const char *not_rename_attrs[4 + msg->num_elements];
1766 bool is_forced_rodc = false;
1769 attrs = rename_attrs;
1771 for (i = 0; i < msg->num_elements; i++) {
1772 not_rename_attrs[i] = msg->elements[i].name;
1774 not_rename_attrs[i] = "replPropertyMetaData";
1775 not_rename_attrs[i+1] = "objectClass";
1776 not_rename_attrs[i+2] = "instanceType";
1777 not_rename_attrs[i+3] = NULL;
1778 attrs = not_rename_attrs;
1781 ldb = ldb_module_get_ctx(module);
1783 ret = samdb_rodc(ldb, rodc);
1784 if (ret != LDB_SUCCESS) {
1785 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1790 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1791 is_forced_rodc = true;
1794 our_invocation_id = samdb_ntds_invocation_id(ldb);
1795 if (!our_invocation_id) {
1796 /* this happens during an initial vampire while
1797 updating the schema */
1798 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1802 unix_to_nt_time(&now, t);
1804 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1805 rmd_is_provided = true;
1806 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1807 rmd_is_just_resorted = true;
1810 rmd_is_provided = false;
1813 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1814 * otherwise we consider we are updating */
1815 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1816 situation = REPL_URGENT_ON_DELETE;
1817 } else if (rename_attrs) {
1818 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1820 situation = REPL_URGENT_ON_UPDATE;
1823 if (rmd_is_provided) {
1824 /* In this case the change_replmetadata control was supplied */
1825 /* We check that it's the only attribute that is provided
1826 * (it's a rare case so it's better to keep the code simplier)
1827 * We also check that the highest local_usn is bigger or the same as
1830 if( msg->num_elements != 1 ||
1831 strncmp(msg->elements[0].name,
1832 "replPropertyMetaData", 20) ) {
1833 DEBUG(0,(__location__ ": changereplmetada control called without "\
1834 "a specified replPropertyMetaData attribute or with others\n"));
1835 return LDB_ERR_OPERATIONS_ERROR;
1837 if (situation != REPL_URGENT_ON_UPDATE) {
1838 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1839 return LDB_ERR_OPERATIONS_ERROR;
1841 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1843 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1844 ldb_dn_get_linearized(msg->dn)));
1845 return LDB_ERR_OPERATIONS_ERROR;
1847 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1848 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1849 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1850 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1851 ldb_dn_get_linearized(msg->dn)));
1852 return LDB_ERR_OPERATIONS_ERROR;
1855 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1856 DSDB_FLAG_NEXT_MODULE |
1857 DSDB_SEARCH_SHOW_RECYCLED |
1858 DSDB_SEARCH_SHOW_EXTENDED_DN |
1859 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1860 DSDB_SEARCH_REVEAL_INTERNALS, req);
1862 if (ret != LDB_SUCCESS) {
1866 if (rmd_is_just_resorted == false) {
1867 *seq_num = find_max_local_usn(omd);
1869 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1872 * The test here now allows for a new
1873 * replPropertyMetaData with no change, if was
1874 * just dbcheck re-sorting the values.
1876 if (*seq_num <= db_seq) {
1877 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1878 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1879 (long long)*seq_num, (long long)db_seq));
1880 return LDB_ERR_OPERATIONS_ERROR;
1885 /* search for the existing replPropertyMetaDataBlob. We need
1886 * to use REVEAL and ask for DNs in storage format to support
1887 * the check for values being the same in
1888 * replmd_update_rpmd_element()
1890 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1891 DSDB_FLAG_NEXT_MODULE |
1892 DSDB_SEARCH_SHOW_RECYCLED |
1893 DSDB_SEARCH_SHOW_EXTENDED_DN |
1894 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1895 DSDB_SEARCH_REVEAL_INTERNALS, req);
1896 if (ret != LDB_SUCCESS) {
1900 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1902 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1903 ldb_dn_get_linearized(msg->dn)));
1904 return LDB_ERR_OPERATIONS_ERROR;
1907 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1908 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1909 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1910 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1911 ldb_dn_get_linearized(msg->dn)));
1912 return LDB_ERR_OPERATIONS_ERROR;
1915 if (omd.version != 1) {
1916 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1917 omd.version, ldb_dn_get_linearized(msg->dn)));
1918 return LDB_ERR_OPERATIONS_ERROR;
1921 for (i=0; i<msg->num_elements;) {
1922 struct ldb_message_element *el = &msg->elements[i];
1923 struct ldb_message_element *old_el;
1925 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1926 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1927 &omd, schema, seq_num,
1932 if (ret != LDB_SUCCESS) {
1936 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1937 *is_urgent = replmd_check_urgent_attribute(el);
1940 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1945 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1947 if (el->num_values != 0) {
1952 ldb_msg_remove_element(msg, el);
1957 * Assert that we have an objectClass attribute - this is major
1958 * corruption if we don't have this!
1960 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1961 if (objectclass_el != NULL) {
1963 * Now check if this objectClass means we need to do urgent replication
1965 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1969 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1970 ldb_asprintf_errstring(ldb, __location__
1971 ": objectClass missing on %s\n",
1972 ldb_dn_get_linearized(msg->dn));
1973 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1977 * replmd_update_rpmd_element has done an update if the
1980 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1981 struct ldb_val *md_value;
1982 struct ldb_message_element *el;
1984 /*if we are RODC and this is a DRSR update then its ok*/
1985 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1986 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1987 && !is_forced_rodc) {
1988 unsigned instanceType;
1991 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1992 return LDB_ERR_REFERRAL;
1995 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1996 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1997 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1998 "cannot change replicated attribute on partial replica");
2002 md_value = talloc(msg, struct ldb_val);
2003 if (md_value == NULL) {
2005 return LDB_ERR_OPERATIONS_ERROR;
2008 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2009 if (ret != LDB_SUCCESS) {
2010 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2014 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2015 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2016 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2017 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2018 ldb_dn_get_linearized(msg->dn)));
2019 return LDB_ERR_OPERATIONS_ERROR;
2022 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2023 if (ret != LDB_SUCCESS) {
2024 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2025 ldb_dn_get_linearized(msg->dn)));
2030 el->values = md_value;
2036 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2038 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2040 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2041 &pdn2->dsdb_dn->extra_part);
2047 get a series of message element values as an array of DNs and GUIDs
2048 the result is sorted by GUID
2050 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2051 struct ldb_message_element *el, struct parsed_dn **pdn,
2052 const char *ldap_oid, struct ldb_request *parent)
2055 bool values_are_sorted = true;
2056 struct ldb_context *ldb = ldb_module_get_ctx(module);
2063 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2065 ldb_module_oom(module);
2066 return LDB_ERR_OPERATIONS_ERROR;
2069 for (i=0; i<el->num_values; i++) {
2070 struct ldb_val *v = &el->values[i];
2073 struct parsed_dn *p;
2077 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2078 if (p->dsdb_dn == NULL) {
2079 return LDB_ERR_INVALID_DN_SYNTAX;
2082 dn = p->dsdb_dn->dn;
2084 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2085 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2086 unlikely(GUID_all_zero(&p->guid))) {
2087 /* we got a DN without a GUID - go find the GUID */
2088 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2089 if (ret != LDB_SUCCESS) {
2090 char *dn_str = NULL;
2091 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2093 ldb_asprintf_errstring(ldb,
2094 "Unable to find GUID for DN %s\n",
2096 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2097 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2098 ldb_attr_cmp(el->name, "member") == 0) {
2099 return LDB_ERR_UNWILLING_TO_PERFORM;
2103 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2104 if (ret != LDB_SUCCESS) {
2107 } else if (!NT_STATUS_IS_OK(status)) {
2108 return LDB_ERR_OPERATIONS_ERROR;
2110 if (i > 0 && values_are_sorted) {
2111 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2113 values_are_sorted = false;
2116 /* keep a pointer to the original ldb_val */
2119 if (! values_are_sorted) {
2120 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2126 * Get a series of trusted message element values. The result is sorted by
2127 * GUID, even though the GUIDs might not be known. That works because we trust
2128 * the database to give us the elements like that if the
2129 * replmd_private->sorted_links flag is set.
2131 * We also ensure that the links are in the Functional Level 2003
2132 * linked attributes format.
2134 static int get_parsed_dns_trusted(struct ldb_module *module,
2135 struct replmd_private *replmd_private,
2136 TALLOC_CTX *mem_ctx,
2137 struct ldb_message_element *el,
2138 struct parsed_dn **pdn,
2139 const char *ldap_oid,
2140 struct ldb_request *parent)
2149 if (!replmd_private->sorted_links) {
2150 /* We need to sort the list. This is the slow old path we want
2153 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2155 if (ret != LDB_SUCCESS) {
2159 /* Here we get a list of 'struct parsed_dns' without the parsing */
2160 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2163 ldb_module_oom(module);
2164 return LDB_ERR_OPERATIONS_ERROR;
2167 for (i = 0; i < el->num_values; i++) {
2168 (*pdn)[i].v = &el->values[i];
2173 * This upgrades links to FL2003 style, and sorts the result
2174 * if that was needed.
2176 * TODO: Add a database feature that asserts we have no FL2000
2177 * style links to avoid this check or add a feature that
2178 * uses a similar check to find sorted/unsorted links
2179 * for an on-the-fly upgrade.
2182 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2183 *pdn, el->num_values,
2186 if (ret != LDB_SUCCESS) {
2194 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2195 otherwise an error code. For compatibility the error code differs depending
2196 on whether or not the attribute is "member".
2198 As always, the parsed_dn list is assumed to be sorted.
2200 static int check_parsed_dn_duplicates(struct ldb_module *module,
2201 struct ldb_message_element *el,
2202 struct parsed_dn *pdn)
2205 struct ldb_context *ldb = ldb_module_get_ctx(module);
2207 for (i = 1; i < el->num_values; i++) {
2208 struct parsed_dn *p = &pdn[i];
2209 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2210 ldb_asprintf_errstring(ldb,
2211 "Linked attribute %s has "
2212 "multiple identical values",
2214 if (ldb_attr_cmp(el->name, "member") == 0) {
2215 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2217 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2225 build a new extended DN, including all meta data fields
2227 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2228 RMD_ADDTIME = originating_add_time
2229 RMD_INVOCID = originating_invocation_id
2230 RMD_CHANGETIME = originating_change_time
2231 RMD_ORIGINATING_USN = originating_usn
2232 RMD_LOCAL_USN = local_usn
2233 RMD_VERSION = version
2235 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2236 struct dsdb_dn *dsdb_dn,
2237 const struct GUID *invocation_id,
2238 uint64_t local_usn, NTTIME nttime)
2240 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2241 local_usn, local_usn, nttime,
2242 RMD_VERSION_INITIAL, false);
2245 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2246 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2247 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2251 check if any links need upgrading from w2k format
2253 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2254 struct parsed_dn *dns, uint32_t count,
2255 struct ldb_message_element *el,
2256 const char *ldap_oid)
2259 const struct GUID *invocation_id = NULL;
2260 for (i=0; i<count; i++) {
2264 if (dns[i].dsdb_dn == NULL) {
2265 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2267 if (ret != LDB_SUCCESS) {
2268 return LDB_ERR_INVALID_DN_SYNTAX;
2272 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2273 &version, "RMD_VERSION");
2274 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2276 * We optimistically assume they are all the same; if
2277 * the first one is fixed, they are all fixed.
2279 * If the first one was *not* fixed and we find a
2280 * later one that is, that is an occasion to shout
2286 DEBUG(0, ("Mixed w2k and fixed format "
2287 "linked attributes\n"));
2291 if (invocation_id == NULL) {
2292 invocation_id = samdb_ntds_invocation_id(ldb);
2293 if (invocation_id == NULL) {
2294 return LDB_ERR_OPERATIONS_ERROR;
2299 /* it's an old one that needs upgrading */
2300 ret = replmd_update_la_val(el->values, dns[i].v,
2301 dns[i].dsdb_dn, dns[i].dsdb_dn,
2302 invocation_id, 1, 1, 0, false);
2303 if (ret != LDB_SUCCESS) {
2309 * This sort() is critical for the operation of
2310 * get_parsed_dns_trusted() because callers of this function
2311 * expect a sorted list, and FL2000 style links are not
2312 * sorted. In particular, as well as the upgrade case,
2313 * get_parsed_dns_trusted() is called from
2314 * replmd_delete_remove_link() even in FL2000 mode
2316 * We do not normally pay the cost of the qsort() due to the
2317 * early return in the RMD_VERSION found case.
2319 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2324 Sets the value for a linked attribute, including all meta data fields
2326 see replmd_build_la_val for value names
2328 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2329 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2330 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2331 uint32_t version, bool deleted)
2333 struct ldb_dn *dn = dsdb_dn->dn;
2334 const char *tstring, *usn_string, *flags_string;
2335 struct ldb_val tval;
2337 struct ldb_val usnv, local_usnv;
2338 struct ldb_val vers, flagsv;
2339 const struct ldb_val *old_addtime = NULL;
2342 const char *dnstring;
2344 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2346 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2348 return LDB_ERR_OPERATIONS_ERROR;
2350 tval = data_blob_string_const(tstring);
2352 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2354 return LDB_ERR_OPERATIONS_ERROR;
2356 usnv = data_blob_string_const(usn_string);
2358 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2360 return LDB_ERR_OPERATIONS_ERROR;
2362 local_usnv = data_blob_string_const(usn_string);
2364 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2365 if (!NT_STATUS_IS_OK(status)) {
2366 return LDB_ERR_OPERATIONS_ERROR;
2369 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2370 if (!flags_string) {
2371 return LDB_ERR_OPERATIONS_ERROR;
2373 flagsv = data_blob_string_const(flags_string);
2375 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2376 if (ret != LDB_SUCCESS) return ret;
2378 /* get the ADDTIME from the original */
2379 if (old_dsdb_dn != NULL) {
2380 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2383 if (old_addtime == NULL) {
2384 old_addtime = &tval;
2386 if (dsdb_dn != old_dsdb_dn ||
2387 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2388 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2389 if (ret != LDB_SUCCESS) return ret;
2392 /* use our invocation id */
2393 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2394 if (ret != LDB_SUCCESS) return ret;
2396 /* changetime is the current time */
2397 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2398 if (ret != LDB_SUCCESS) return ret;
2400 /* update the USN */
2401 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2402 if (ret != LDB_SUCCESS) return ret;
2404 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2405 if (ret != LDB_SUCCESS) return ret;
2407 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2408 vers = data_blob_string_const(vstring);
2409 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2410 if (ret != LDB_SUCCESS) return ret;
2412 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2413 if (dnstring == NULL) {
2414 return LDB_ERR_OPERATIONS_ERROR;
2416 *v = data_blob_string_const(dnstring);
2422 * Updates the value for a linked attribute, including all meta data fields
2424 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2425 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2426 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2429 uint32_t old_version;
2430 uint32_t version = RMD_VERSION_INITIAL;
2434 * We're updating the linked attribute locally, so increase the version
2435 * by 1 so that other DCs will see the change when it gets replicated out
2437 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2440 if (NT_STATUS_IS_OK(status)) {
2441 version = old_version + 1;
2444 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2445 usn, local_usn, nttime, version, deleted);
2449 handle adding a linked attribute
2451 static int replmd_modify_la_add(struct ldb_module *module,
2452 struct replmd_private *replmd_private,
2453 struct replmd_replicated_request *ac,
2454 struct ldb_message *msg,
2455 struct ldb_message_element *el,
2456 struct ldb_message_element *old_el,
2457 const struct dsdb_attribute *schema_attr,
2459 struct ldb_dn *msg_dn,
2460 struct ldb_request *parent)
2463 struct parsed_dn *dns, *old_dns;
2464 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2466 struct ldb_val *new_values = NULL;
2467 unsigned old_num_values = old_el ? old_el->num_values : 0;
2468 unsigned num_values = 0;
2469 unsigned max_num_values;
2470 struct ldb_context *ldb = ldb_module_get_ctx(module);
2472 unix_to_nt_time(&now, t);
2474 /* get the DNs to be added, fully parsed.
2476 * We need full parsing because they came off the wire and we don't
2477 * trust them, besides which we need their details to know where to put
2480 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2481 schema_attr->syntax->ldap_oid, parent);
2482 if (ret != LDB_SUCCESS) {
2483 talloc_free(tmp_ctx);
2487 /* get the existing DNs, lazily parsed */
2488 ret = get_parsed_dns_trusted(module, replmd_private,
2489 tmp_ctx, old_el, &old_dns,
2490 schema_attr->syntax->ldap_oid, parent);
2492 if (ret != LDB_SUCCESS) {
2493 talloc_free(tmp_ctx);
2497 max_num_values = old_num_values + el->num_values;
2498 if (max_num_values < old_num_values) {
2499 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2500 "old values: %u, new values: %u, sum: %u\n",
2501 old_num_values, el->num_values, max_num_values));
2502 talloc_free(tmp_ctx);
2503 return LDB_ERR_OPERATIONS_ERROR;
2506 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2508 if (new_values == NULL) {
2509 ldb_module_oom(module);
2510 talloc_free(tmp_ctx);
2511 return LDB_ERR_OPERATIONS_ERROR;
2515 * For each new value, find where it would go in the list. If there is
2516 * a matching GUID there, we update the existing value; otherwise we
2520 for (i = 0; i < el->num_values; i++) {
2521 struct parsed_dn *exact;
2522 struct parsed_dn *next;
2524 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2527 dns[i].dsdb_dn->extra_part, 0,
2529 schema_attr->syntax->ldap_oid,
2531 if (err != LDB_SUCCESS) {
2532 talloc_free(tmp_ctx);
2536 if (ac->fix_link_sid) {
2537 char *fixed_dnstring = NULL;
2538 struct dom_sid tmp_sid = { 0, };
2539 DATA_BLOB sid_blob = data_blob_null;
2540 enum ndr_err_code ndr_err;
2544 if (exact == NULL) {
2545 talloc_free(tmp_ctx);
2546 return ldb_operr(ldb);
2549 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2550 talloc_free(tmp_ctx);
2551 return ldb_operr(ldb);
2555 * Only "<GUID=...><SID=...>" is allowed.
2557 * We get the GUID to just to find the old
2558 * value and the SID in order to add it
2559 * to the found value.
2562 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2564 talloc_free(tmp_ctx);
2565 return ldb_operr(ldb);
2568 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2570 talloc_free(tmp_ctx);
2571 return ldb_operr(ldb);
2574 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2576 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2577 /* this is what we expect */
2578 } else if (NT_STATUS_IS_OK(status)) {
2579 struct GUID_txt_buf guid_str;
2580 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2581 "i[%u] SID NOT MISSING... Attribute %s already "
2582 "exists for target GUID %s, SID %s, DN: %s",
2584 GUID_buf_string(&exact->guid,
2586 dom_sid_string(tmp_ctx, &tmp_sid),
2587 dsdb_dn_get_extended_linearized(tmp_ctx,
2588 exact->dsdb_dn, 1));
2589 talloc_free(tmp_ctx);
2590 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2592 talloc_free(tmp_ctx);
2593 return ldb_operr(ldb);
2596 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2598 if (!NT_STATUS_IS_OK(status)) {
2599 struct GUID_txt_buf guid_str;
2600 ldb_asprintf_errstring(ldb,
2601 "NO SID PROVIDED... Attribute %s already "
2602 "exists for target GUID %s",
2604 GUID_buf_string(&exact->guid,
2606 talloc_free(tmp_ctx);
2607 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2610 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2611 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2612 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2613 talloc_free(tmp_ctx);
2614 return ldb_operr(ldb);
2617 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2618 data_blob_free(&sid_blob);
2619 if (ret != LDB_SUCCESS) {
2620 talloc_free(tmp_ctx);
2624 fixed_dnstring = dsdb_dn_get_extended_linearized(
2625 new_values, exact->dsdb_dn, 1);
2626 if (fixed_dnstring == NULL) {
2627 talloc_free(tmp_ctx);
2628 return ldb_operr(ldb);
2632 * We just replace the existing value...
2634 *exact->v = data_blob_string_const(fixed_dnstring);
2639 if (exact != NULL) {
2641 * We are trying to add one that exists, which is only
2642 * allowed if it was previously deleted.
2644 * When we do undelete a link we change it in place.
2645 * It will be copied across into the right spot in due
2649 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2651 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2652 struct GUID_txt_buf guid_str;
2653 ldb_asprintf_errstring(ldb,
2654 "Attribute %s already "
2655 "exists for target GUID %s",
2657 GUID_buf_string(&exact->guid,
2659 talloc_free(tmp_ctx);
2660 /* error codes for 'member' need to be
2662 if (ldb_attr_cmp(el->name, "member") == 0) {
2663 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2665 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2669 ret = replmd_update_la_val(new_values, exact->v,
2672 &ac->our_invocation_id,
2673 ac->seq_num, ac->seq_num,
2675 if (ret != LDB_SUCCESS) {
2676 talloc_free(tmp_ctx);
2680 ret = replmd_add_backlink(module, replmd_private,
2687 if (ret != LDB_SUCCESS) {
2688 talloc_free(tmp_ctx);
2694 * Here we don't have an exact match.
2696 * If next is NULL, this one goes beyond the end of the
2697 * existing list, so we need to add all of those ones first.
2699 * If next is not NULL, we need to add all the ones before
2703 offset = old_num_values;
2705 /* next should have been parsed, but let's make sure */
2706 if (next->dsdb_dn == NULL) {
2707 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2708 schema_attr->syntax->ldap_oid);
2709 if (ret != LDB_SUCCESS) {
2713 offset = MIN(next - old_dns, old_num_values);
2716 /* put all the old ones before next on the list */
2717 for (; j < offset; j++) {
2718 new_values[num_values] = *old_dns[j].v;
2722 ret = replmd_add_backlink(module, replmd_private,
2727 /* Make the new linked attribute ldb_val. */
2728 ret = replmd_build_la_val(new_values, &new_values[num_values],
2729 dns[i].dsdb_dn, &ac->our_invocation_id,
2731 if (ret != LDB_SUCCESS) {
2732 talloc_free(tmp_ctx);
2736 if (ret != LDB_SUCCESS) {
2737 talloc_free(tmp_ctx);
2741 /* copy the rest of the old ones (if any) */
2742 for (; j < old_num_values; j++) {
2743 new_values[num_values] = *old_dns[j].v;
2747 talloc_steal(msg->elements, new_values);
2748 if (old_el != NULL) {
2749 talloc_steal(msg->elements, old_el->values);
2751 el->values = new_values;
2752 el->num_values = num_values;
2754 talloc_free(tmp_ctx);
2756 /* we now tell the backend to replace all existing values
2757 with the one we have constructed */
2758 el->flags = LDB_FLAG_MOD_REPLACE;
2765 handle deleting all active linked attributes
2767 static int replmd_modify_la_delete(struct ldb_module *module,
2768 struct replmd_private *replmd_private,
2769 struct replmd_replicated_request *ac,
2770 struct ldb_message *msg,
2771 struct ldb_message_element *el,
2772 struct ldb_message_element *old_el,
2773 const struct dsdb_attribute *schema_attr,
2775 struct ldb_dn *msg_dn,
2776 struct ldb_request *parent)
2779 struct parsed_dn *dns, *old_dns;
2780 TALLOC_CTX *tmp_ctx = NULL;
2782 struct ldb_context *ldb = ldb_module_get_ctx(module);
2783 struct ldb_control *vanish_links_ctrl = NULL;
2784 bool vanish_links = false;
2785 unsigned int num_to_delete = el->num_values;
2789 unix_to_nt_time(&now, t);
2791 if (old_el == NULL || old_el->num_values == 0) {
2792 /* there is nothing to delete... */
2793 if (num_to_delete == 0) {
2794 /* and we're deleting nothing, so that's OK */
2797 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2800 tmp_ctx = talloc_new(msg);
2801 if (tmp_ctx == NULL) {
2802 return LDB_ERR_OPERATIONS_ERROR;
2805 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2806 schema_attr->syntax->ldap_oid, parent);
2807 if (ret != LDB_SUCCESS) {
2808 talloc_free(tmp_ctx);
2812 ret = get_parsed_dns_trusted(module, replmd_private,
2813 tmp_ctx, old_el, &old_dns,
2814 schema_attr->syntax->ldap_oid, parent);
2816 if (ret != LDB_SUCCESS) {
2817 talloc_free(tmp_ctx);
2822 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2823 if (vanish_links_ctrl) {
2824 vanish_links = true;
2825 vanish_links_ctrl->critical = false;
2829 /* we empty out el->values here to avoid damage if we return early. */
2834 * If vanish links is set, we are actually removing members of
2835 * old_el->values; otherwise we are just marking them deleted.
2837 * There is a special case when no values are given: we remove them
2838 * all. When we have the vanish_links control we just have to remove
2839 * the backlinks and change our element to replace the existing values
2840 * with the empty list.
2843 if (num_to_delete == 0) {
2844 for (i = 0; i < old_el->num_values; i++) {
2845 struct parsed_dn *p = &old_dns[i];
2846 if (p->dsdb_dn == NULL) {
2847 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2848 schema_attr->syntax->ldap_oid);
2849 if (ret != LDB_SUCCESS) {
2853 ret = replmd_add_backlink(module, replmd_private,
2854 ac->schema, msg_dn, &p->guid,
2857 if (ret != LDB_SUCCESS) {
2858 talloc_free(tmp_ctx);
2865 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2866 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2870 ret = replmd_update_la_val(old_el->values, p->v,
2871 p->dsdb_dn, p->dsdb_dn,
2872 &ac->our_invocation_id,
2873 ac->seq_num, ac->seq_num,
2875 if (ret != LDB_SUCCESS) {
2876 talloc_free(tmp_ctx);
2882 el->flags = LDB_FLAG_MOD_REPLACE;
2883 talloc_free(tmp_ctx);
2889 for (i = 0; i < num_to_delete; i++) {
2890 struct parsed_dn *p = &dns[i];
2891 struct parsed_dn *exact = NULL;
2892 struct parsed_dn *next = NULL;
2893 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2896 p->dsdb_dn->extra_part, 0,
2898 schema_attr->syntax->ldap_oid,
2900 if (ret != LDB_SUCCESS) {
2901 talloc_free(tmp_ctx);
2904 if (exact == NULL) {
2905 struct GUID_txt_buf buf;
2906 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2907 "exist for target GUID %s",
2909 GUID_buf_string(&p->guid, &buf));
2910 if (ldb_attr_cmp(el->name, "member") == 0) {
2911 talloc_free(tmp_ctx);
2912 return LDB_ERR_UNWILLING_TO_PERFORM;
2914 talloc_free(tmp_ctx);
2915 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2920 if (CHECK_DEBUGLVL(5)) {
2921 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2922 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2923 struct GUID_txt_buf buf;
2924 const char *guid_str = \
2925 GUID_buf_string(&p->guid, &buf);
2926 DEBUG(5, ("Deleting deleted linked "
2927 "attribute %s to %s, because "
2928 "vanish_links control is set\n",
2929 el->name, guid_str));
2933 /* remove the backlink */
2934 ret = replmd_add_backlink(module,
2941 if (ret != LDB_SUCCESS) {
2942 talloc_free(tmp_ctx);
2946 /* We flag the deletion and tidy it up later. */
2951 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2953 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2954 struct GUID_txt_buf buf;
2955 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2956 ldb_asprintf_errstring(ldb, "Attribute %s already "
2957 "deleted for target GUID %s",
2958 el->name, guid_str);
2959 if (ldb_attr_cmp(el->name, "member") == 0) {
2960 talloc_free(tmp_ctx);
2961 return LDB_ERR_UNWILLING_TO_PERFORM;
2963 talloc_free(tmp_ctx);
2964 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2968 ret = replmd_update_la_val(old_el->values, exact->v,
2969 exact->dsdb_dn, exact->dsdb_dn,
2970 &ac->our_invocation_id,
2971 ac->seq_num, ac->seq_num,
2973 if (ret != LDB_SUCCESS) {
2974 talloc_free(tmp_ctx);
2977 ret = replmd_add_backlink(module, replmd_private,
2982 if (ret != LDB_SUCCESS) {
2983 talloc_free(tmp_ctx);
2990 struct ldb_val *tmp_vals = NULL;
2992 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2993 old_el->num_values);
2994 if (tmp_vals == NULL) {
2995 talloc_free(tmp_ctx);
2996 return ldb_module_oom(module);
2998 for (i = 0; i < old_el->num_values; i++) {
2999 if (old_dns[i].v == NULL) {
3002 tmp_vals[j] = *old_dns[i].v;
3005 for (i = 0; i < j; i++) {
3006 old_el->values[i] = tmp_vals[i];
3008 old_el->num_values = j;
3011 el->values = talloc_steal(msg->elements, old_el->values);
3012 el->num_values = old_el->num_values;
3014 talloc_free(tmp_ctx);
3016 /* we now tell the backend to replace all existing values
3017 with the one we have constructed */
3018 el->flags = LDB_FLAG_MOD_REPLACE;
3024 handle replacing a linked attribute
3026 static int replmd_modify_la_replace(struct ldb_module *module,
3027 struct replmd_private *replmd_private,
3028 struct replmd_replicated_request *ac,
3029 struct ldb_message *msg,
3030 struct ldb_message_element *el,
3031 struct ldb_message_element *old_el,
3032 const struct dsdb_attribute *schema_attr,
3034 struct ldb_dn *msg_dn,
3035 struct ldb_request *parent)
3037 unsigned int i, old_i, new_i;
3038 struct parsed_dn *dns, *old_dns;
3039 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3041 struct ldb_context *ldb = ldb_module_get_ctx(module);
3042 struct ldb_val *new_values = NULL;
3043 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3044 unsigned int old_num_values;
3045 unsigned int repl_num_values;
3046 unsigned int max_num_values;
3049 unix_to_nt_time(&now, t);
3052 * The replace operation is unlike the replace and delete cases in that
3053 * we need to look at every existing link to see whether it is being
3054 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3056 * As we are trying to combine two sorted lists, the algorithm we use
3057 * is akin to the merge phase of a merge sort. We interleave the two
3058 * lists, doing different things depending on which side the current
3061 * There are three main cases, with some sub-cases.
3063 * - a DN is in the old list but not the new one. It needs to be
3064 * marked as deleted (but left in the list).
3065 * - maybe it is already deleted, and we have less to do.
3067 * - a DN is in both lists. The old data gets replaced by the new,
3068 * and the list doesn't grow. The old link may have been marked as
3069 * deleted, in which case we undelete it.
3071 * - a DN is in the new list only. We add it in the right place.
3074 old_num_values = old_el ? old_el->num_values : 0;
3075 repl_num_values = el->num_values;
3076 max_num_values = old_num_values + repl_num_values;
3078 if (max_num_values == 0) {
3079 /* There is nothing to do! */
3083 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3084 if (ret != LDB_SUCCESS) {
3085 talloc_free(tmp_ctx);
3089 ret = check_parsed_dn_duplicates(module, el, dns);
3090 if (ret != LDB_SUCCESS) {
3091 talloc_free(tmp_ctx);
3095 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3097 if (ret != LDB_SUCCESS) {
3098 talloc_free(tmp_ctx);
3102 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3104 if (ret != LDB_SUCCESS) {
3105 talloc_free(tmp_ctx);
3109 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3110 if (new_values == NULL) {
3111 ldb_module_oom(module);
3112 talloc_free(tmp_ctx);
3113 return LDB_ERR_OPERATIONS_ERROR;
3118 for (i = 0; i < max_num_values; i++) {
3120 struct parsed_dn *old_p, *new_p;
3121 if (old_i < old_num_values && new_i < repl_num_values) {
3122 old_p = &old_dns[old_i];
3123 new_p = &dns[new_i];
3124 cmp = parsed_dn_compare(old_p, new_p);
3125 } else if (old_i < old_num_values) {
3126 /* the new list is empty, read the old list */
3127 old_p = &old_dns[old_i];
3130 } else if (new_i < repl_num_values) {
3131 /* the old list is empty, read new list */
3133 new_p = &dns[new_i];
3141 * An old ones that come before the next replacement
3142 * (if any). We mark it as deleted and add it to the
3145 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3146 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3147 ret = replmd_update_la_val(new_values, old_p->v,
3150 &ac->our_invocation_id,
3151 ac->seq_num, ac->seq_num,
3153 if (ret != LDB_SUCCESS) {
3154 talloc_free(tmp_ctx);
3158 ret = replmd_add_backlink(module, replmd_private,
3161 &old_p->guid, false,
3164 if (ret != LDB_SUCCESS) {
3165 talloc_free(tmp_ctx);
3169 new_values[i] = *old_p->v;
3171 } else if (cmp == 0) {
3173 * We are overwriting one. If it was previously
3174 * deleted, we need to add a backlink.
3176 * Note that if any RMD_FLAGs in an extended new DN
3181 ret = replmd_update_la_val(new_values, old_p->v,
3184 &ac->our_invocation_id,
3185 ac->seq_num, ac->seq_num,
3187 if (ret != LDB_SUCCESS) {
3188 talloc_free(tmp_ctx);
3192 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3193 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3194 ret = replmd_add_backlink(module, replmd_private,
3200 if (ret != LDB_SUCCESS) {
3201 talloc_free(tmp_ctx);
3206 new_values[i] = *old_p->v;
3211 * Replacements that don't match an existing one. We
3212 * just add them to the final list.
3214 ret = replmd_build_la_val(new_values,
3217 &ac->our_invocation_id,
3219 if (ret != LDB_SUCCESS) {
3220 talloc_free(tmp_ctx);
3223 ret = replmd_add_backlink(module, replmd_private,
3229 if (ret != LDB_SUCCESS) {
3230 talloc_free(tmp_ctx);
3233 new_values[i] = *new_p->v;
3237 if (old_el != NULL) {
3238 talloc_steal(msg->elements, old_el->values);
3240 el->values = talloc_steal(msg->elements, new_values);
3242 talloc_free(tmp_ctx);
3244 el->flags = LDB_FLAG_MOD_REPLACE;
3251 handle linked attributes in modify requests
3253 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3254 struct replmd_private *replmd_private,
3255 struct replmd_replicated_request *ac,
3256 struct ldb_message *msg,
3258 struct ldb_request *parent)
3260 struct ldb_result *res;
3263 struct ldb_context *ldb = ldb_module_get_ctx(module);
3264 struct ldb_message *old_msg;
3266 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3268 * Nothing special is required for modifying or vanishing links
3269 * in fl2000 since they are just strings in a multi-valued
3272 struct ldb_control *ctrl = ldb_request_get_control(parent,
3273 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3275 ctrl->critical = false;
3283 * We should restrict this to the intersection of the list of
3284 * linked attributes in the schema and the list of attributes
3287 * This will help performance a little, as otherwise we have
3288 * to allocate the entire object value-by-value.
3290 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3291 DSDB_FLAG_NEXT_MODULE |
3292 DSDB_SEARCH_SHOW_RECYCLED |
3293 DSDB_SEARCH_REVEAL_INTERNALS |
3294 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3296 if (ret != LDB_SUCCESS) {
3300 old_msg = res->msgs[0];
3302 for (i=0; i<msg->num_elements; i++) {
3303 struct ldb_message_element *el = &msg->elements[i];
3304 struct ldb_message_element *old_el, *new_el;
3305 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3306 const struct dsdb_attribute *schema_attr
3307 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3309 ldb_asprintf_errstring(ldb,
3310 "%s: attribute %s is not a valid attribute in schema",
3311 __FUNCTION__, el->name);
3312 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3314 if (schema_attr->linkID == 0) {
3317 if ((schema_attr->linkID & 1) == 1) {
3319 struct ldb_control *ctrl;
3321 ctrl = ldb_request_get_control(parent,
3322 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3324 ctrl->critical = false;
3327 ctrl = ldb_request_get_control(parent,
3328 DSDB_CONTROL_DBCHECK);
3334 /* Odd is for the target. Illegal to modify */
3335 ldb_asprintf_errstring(ldb,
3336 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3337 return LDB_ERR_UNWILLING_TO_PERFORM;
3339 old_el = ldb_msg_find_element(old_msg, el->name);
3341 case LDB_FLAG_MOD_REPLACE:
3342 ret = replmd_modify_la_replace(module, replmd_private,
3343 ac, msg, el, old_el,
3348 case LDB_FLAG_MOD_DELETE:
3349 ret = replmd_modify_la_delete(module, replmd_private,
3350 ac, msg, el, old_el,
3355 case LDB_FLAG_MOD_ADD:
3356 ret = replmd_modify_la_add(module, replmd_private,
3357 ac, msg, el, old_el,
3363 ldb_asprintf_errstring(ldb,
3364 "invalid flags 0x%x for %s linked attribute",
3365 el->flags, el->name);
3366 return LDB_ERR_UNWILLING_TO_PERFORM;
3368 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3369 ldb_asprintf_errstring(ldb,
3370 "Attribute %s is single valued but more than one value has been supplied",
3372 /* Return codes as found on Windows 2012r2 */
3373 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3374 return LDB_ERR_CONSTRAINT_VIOLATION;
3376 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3379 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3382 if (ret != LDB_SUCCESS) {
3386 ldb_msg_remove_attr(old_msg, el->name);
3388 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3389 new_el->num_values = el->num_values;
3390 new_el->values = talloc_steal(msg->elements, el->values);
3392 /* TODO: this relises a bit too heavily on the exact
3393 behaviour of ldb_msg_find_element and
3394 ldb_msg_remove_element */
3395 old_el = ldb_msg_find_element(msg, el->name);
3397 ldb_msg_remove_element(msg, old_el);
3407 static int send_rodc_referral(struct ldb_request *req,
3408 struct ldb_context *ldb,
3411 char *referral = NULL;
3412 struct loadparm_context *lp_ctx = NULL;
3413 struct ldb_dn *fsmo_role_dn = NULL;
3414 struct ldb_dn *role_owner_dn = NULL;
3415 const char *domain = NULL;
3418 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3419 struct loadparm_context);
3421 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3422 &fsmo_role_dn, &role_owner_dn);
3424 if (W_ERROR_IS_OK(werr)) {
3425 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3426 if (server_dn != NULL) {
3427 ldb_dn_remove_child_components(server_dn, 1);
3428 domain = samdb_dn_to_dnshostname(ldb, req,
3433 if (domain == NULL) {
3434 domain = lpcfg_dnsdomain(lp_ctx);
3437 referral = talloc_asprintf(req, "ldap://%s/%s",
3439 ldb_dn_get_linearized(dn));
3440 if (referral == NULL) {
3442 return LDB_ERR_OPERATIONS_ERROR;
3445 return ldb_module_send_referral(req, referral);
3449 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3451 struct ldb_context *ldb;
3452 struct replmd_replicated_request *ac;
3453 struct ldb_request *down_req;
3454 struct ldb_message *msg;
3455 time_t t = time(NULL);
3457 bool is_urgent = false, rodc = false;
3458 bool is_schema_nc = false;
3459 unsigned int functional_level;
3460 const struct ldb_message_element *guid_el = NULL;
3461 struct ldb_control *sd_propagation_control;
3462 struct ldb_control *fix_links_control = NULL;
3463 struct ldb_control *fix_dn_name_control = NULL;
3464 struct ldb_control *fix_dn_sid_control = NULL;
3465 struct replmd_private *replmd_private =
3466 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3468 /* do not manipulate our control entries */
3469 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3470 return ldb_next_request(module, req);
3473 sd_propagation_control = ldb_request_get_control(req,
3474 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3475 if (sd_propagation_control != NULL) {
3476 if (req->op.mod.message->num_elements != 1) {
3477 return ldb_module_operr(module);
3479 ret = strcmp(req->op.mod.message->elements[0].name,
3480 "nTSecurityDescriptor");
3482 return ldb_module_operr(module);
3485 return ldb_next_request(module, req);
3488 ldb = ldb_module_get_ctx(module);
3490 fix_links_control = ldb_request_get_control(req,
3491 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3492 if (fix_links_control != NULL) {
3493 struct dsdb_schema *schema = NULL;
3494 const struct dsdb_attribute *sa = NULL;
3496 if (req->op.mod.message->num_elements != 1) {
3497 return ldb_module_operr(module);
3500 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3501 return ldb_module_operr(module);
3504 schema = dsdb_get_schema(ldb, req);
3505 if (schema == NULL) {
3506 return ldb_module_operr(module);
3509 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3510 req->op.mod.message->elements[0].name);
3512 return ldb_module_operr(module);
3515 if (sa->linkID == 0) {
3516 return ldb_module_operr(module);
3519 fix_links_control->critical = false;
3520 return ldb_next_request(module, req);
3523 fix_dn_name_control = ldb_request_get_control(req,
3524 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3525 if (fix_dn_name_control != NULL) {
3526 struct dsdb_schema *schema = NULL;
3527 const struct dsdb_attribute *sa = NULL;
3529 if (req->op.mod.message->num_elements != 2) {
3530 return ldb_module_operr(module);
3533 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3534 return ldb_module_operr(module);
3537 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3538 return ldb_module_operr(module);
3541 if (req->op.mod.message->elements[0].num_values != 1) {
3542 return ldb_module_operr(module);
3545 if (req->op.mod.message->elements[1].num_values != 1) {
3546 return ldb_module_operr(module);
3549 schema = dsdb_get_schema(ldb, req);
3550 if (schema == NULL) {
3551 return ldb_module_operr(module);
3554 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3555 req->op.mod.message->elements[1].name) != 0) {
3556 return ldb_module_operr(module);
3559 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3560 req->op.mod.message->elements[0].name);
3562 return ldb_module_operr(module);
3565 if (sa->dn_format == DSDB_INVALID_DN) {
3566 return ldb_module_operr(module);
3569 if (sa->linkID != 0) {
3570 return ldb_module_operr(module);
3574 * If we are run from dbcheck and we are not updating
3575 * a link (as these would need to be sorted and so
3576 * can't go via such a simple update, then do not
3577 * trigger replicated updates and a new USN from this
3578 * change, it wasn't a real change, just a new
3579 * (correct) string DN
3582 fix_dn_name_control->critical = false;
3583 return ldb_next_request(module, req);
3586 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3588 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3589 if (guid_el != NULL) {
3590 ldb_set_errstring(ldb,
3591 "replmd_modify: it's not allowed to change the objectGUID!");
3592 return LDB_ERR_CONSTRAINT_VIOLATION;
3595 ac = replmd_ctx_init(module, req);
3597 return ldb_module_oom(module);
3600 functional_level = dsdb_functional_level(ldb);
3602 /* we have to copy the message as the caller might have it as a const */
3603 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3607 return LDB_ERR_OPERATIONS_ERROR;
3610 fix_dn_sid_control = ldb_request_get_control(req,
3611 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3612 if (fix_dn_sid_control != NULL) {
3613 const struct dsdb_attribute *sa = NULL;
3615 if (msg->num_elements != 1) {
3617 return ldb_module_operr(module);
3620 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3622 return ldb_module_operr(module);
3625 if (msg->elements[0].num_values != 1) {
3627 return ldb_module_operr(module);
3630 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3631 msg->elements[0].name);
3634 return ldb_module_operr(module);
3637 if (sa->dn_format != DSDB_NORMAL_DN) {
3639 return ldb_module_operr(module);
3642 fix_dn_sid_control->critical = false;
3643 ac->fix_link_sid = true;
3645 goto handle_linked_attribs;
3648 ldb_msg_remove_attr(msg, "whenChanged");
3649 ldb_msg_remove_attr(msg, "uSNChanged");
3651 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3653 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3654 msg, &ac->seq_num, t, is_schema_nc,
3656 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3657 ret = send_rodc_referral(req, ldb, msg->dn);
3663 if (ret != LDB_SUCCESS) {
3668 handle_linked_attribs:
3669 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3671 if (ret != LDB_SUCCESS) {
3677 * - replace the old object with the newly constructed one
3680 ac->is_urgent = is_urgent;
3682 ret = ldb_build_mod_req(&down_req, ldb, ac,
3685 ac, replmd_op_callback,
3687 LDB_REQ_SET_LOCATION(down_req);
3688 if (ret != LDB_SUCCESS) {
3693 /* current partition control is needed by "replmd_op_callback" */
3694 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3695 ret = ldb_request_add_control(down_req,
3696 DSDB_CONTROL_CURRENT_PARTITION_OID,
3698 if (ret != LDB_SUCCESS) {
3704 /* If we are in functional level 2000, then
3705 * replmd_modify_handle_linked_attribs will have done
3707 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3708 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3709 if (ret != LDB_SUCCESS) {
3715 talloc_steal(down_req, msg);
3717 /* we only change whenChanged and uSNChanged if the seq_num
3719 if (ac->seq_num != 0) {
3720 ret = add_time_element(msg, "whenChanged", t);
3721 if (ret != LDB_SUCCESS) {
3727 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3728 if (ret != LDB_SUCCESS) {
3735 /* go on with the call chain */
3736 return ldb_next_request(module, down_req);
3739 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3742 handle a rename request
3744 On a rename we need to do an extra ldb_modify which sets the
3745 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3747 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3749 struct ldb_context *ldb;
3750 struct replmd_replicated_request *ac;
3752 struct ldb_request *down_req;
3754 /* do not manipulate our control entries */
3755 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3756 return ldb_next_request(module, req);
3759 ldb = ldb_module_get_ctx(module);
3761 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3763 ac = replmd_ctx_init(module, req);
3765 return ldb_module_oom(module);
3768 ret = ldb_build_rename_req(&down_req, ldb, ac,
3769 ac->req->op.rename.olddn,
3770 ac->req->op.rename.newdn,
3772 ac, replmd_rename_callback,
3774 LDB_REQ_SET_LOCATION(down_req);
3775 if (ret != LDB_SUCCESS) {
3780 /* go on with the call chain */
3781 return ldb_next_request(module, down_req);
3784 /* After the rename is compleated, update the whenchanged etc */
3785 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3787 struct ldb_context *ldb;
3788 struct ldb_request *down_req;
3789 struct ldb_message *msg;
3790 const struct dsdb_attribute *rdn_attr;
3791 const char *rdn_name;
3792 const struct ldb_val *rdn_val;
3793 const char *attrs[5] = { NULL, };
3794 time_t t = time(NULL);
3796 bool is_urgent = false, rodc = false;
3798 struct replmd_replicated_request *ac =
3799 talloc_get_type(req->context, struct replmd_replicated_request);
3800 struct replmd_private *replmd_private =
3801 talloc_get_type(ldb_module_get_private(ac->module),
3802 struct replmd_private);
3804 ldb = ldb_module_get_ctx(ac->module);
3806 if (ares->error != LDB_SUCCESS) {
3807 return ldb_module_done(ac->req, ares->controls,
3808 ares->response, ares->error);
3811 if (ares->type != LDB_REPLY_DONE) {
3812 ldb_set_errstring(ldb,
3813 "invalid ldb_reply_type in callback");
3815 return ldb_module_done(ac->req, NULL, NULL,
3816 LDB_ERR_OPERATIONS_ERROR);
3820 * - replace the old object with the newly constructed one
3823 msg = ldb_msg_new(ac);
3826 return LDB_ERR_OPERATIONS_ERROR;
3829 msg->dn = ac->req->op.rename.newdn;
3831 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3833 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3834 if (rdn_name == NULL) {
3836 return ldb_module_done(ac->req, NULL, NULL,
3840 /* normalize the rdn attribute name */
3841 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3842 if (rdn_attr == NULL) {
3844 return ldb_module_done(ac->req, NULL, NULL,
3847 rdn_name = rdn_attr->lDAPDisplayName;
3849 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3850 if (rdn_val == NULL) {
3852 return ldb_module_done(ac->req, NULL, NULL,
3856 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3858 return ldb_module_done(ac->req, NULL, NULL,
3861 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3863 return ldb_module_done(ac->req, NULL, NULL,
3866 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3868 return ldb_module_done(ac->req, NULL, NULL,
3871 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3873 return ldb_module_done(ac->req, NULL, NULL,
3878 * here we let replmd_update_rpmd() only search for
3879 * the existing "replPropertyMetaData" and rdn_name attributes.
3881 * We do not want the existing "name" attribute as
3882 * the "name" attribute needs to get the version
3883 * updated on rename even if the rdn value hasn't changed.
3885 * This is the diff of the meta data, for a moved user
3886 * on a w2k8r2 server:
3889 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3890 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3891 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3892 * version : 0x00000001 (1)
3893 * reserved : 0x00000000 (0)
3894 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3895 * local_usn : 0x00000000000037a5 (14245)
3896 * array: struct replPropertyMetaData1
3897 * attid : DRSUAPI_ATTID_name (0x90001)
3898 * - version : 0x00000001 (1)
3899 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3900 * + version : 0x00000002 (2)
3901 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3902 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3903 * - originating_usn : 0x00000000000037a5 (14245)
3904 * - local_usn : 0x00000000000037a5 (14245)
3905 * + originating_usn : 0x0000000000003834 (14388)
3906 * + local_usn : 0x0000000000003834 (14388)
3907 * array: struct replPropertyMetaData1
3908 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3909 * version : 0x00000004 (4)
3911 attrs[0] = "replPropertyMetaData";
3912 attrs[1] = "objectClass";
3913 attrs[2] = "instanceType";
3914 attrs[3] = rdn_name;
3917 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3918 msg, &ac->seq_num, t,
3919 is_schema_nc, &is_urgent, &rodc);
3920 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3921 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3923 return ldb_module_done(req, NULL, NULL, ret);
3926 if (ret != LDB_SUCCESS) {
3928 return ldb_module_done(ac->req, NULL, NULL, ret);
3931 if (ac->seq_num == 0) {
3933 return ldb_module_done(ac->req, NULL, NULL,
3935 "internal error seq_num == 0"));
3937 ac->is_urgent = is_urgent;
3939 ret = ldb_build_mod_req(&down_req, ldb, ac,
3942 ac, replmd_op_callback,
3944 LDB_REQ_SET_LOCATION(down_req);
3945 if (ret != LDB_SUCCESS) {
3950 /* current partition control is needed by "replmd_op_callback" */
3951 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3952 ret = ldb_request_add_control(down_req,
3953 DSDB_CONTROL_CURRENT_PARTITION_OID,
3955 if (ret != LDB_SUCCESS) {
3961 talloc_steal(down_req, msg);
3963 ret = add_time_element(msg, "whenChanged", t);
3964 if (ret != LDB_SUCCESS) {
3970 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3971 if (ret != LDB_SUCCESS) {
3977 /* go on with the call chain - do the modify after the rename */
3978 return ldb_next_request(ac->module, down_req);
3982 * remove links from objects that point at this object when an object
3983 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3984 * RemoveObj which states that link removal due to the object being
3985 * deleted is NOT an originating update - they just go away!
3988 static int replmd_delete_remove_link(struct ldb_module *module,
3989 const struct dsdb_schema *schema,
3990 struct replmd_private *replmd_private,
3993 struct ldb_message_element *el,
3994 const struct dsdb_attribute *sa,
3995 struct ldb_request *parent)
3998 TALLOC_CTX *tmp_ctx = talloc_new(module);
3999 struct ldb_context *ldb = ldb_module_get_ctx(module);
4001 for (i=0; i<el->num_values; i++) {
4002 struct dsdb_dn *dsdb_dn;
4004 struct ldb_message *msg;
4005 const struct dsdb_attribute *target_attr;
4006 struct ldb_message_element *el2;
4008 struct ldb_val dn_val;
4009 uint32_t dsdb_flags = 0;
4010 const char *attrs[] = { NULL, NULL };
4011 struct ldb_result *link_res;
4012 struct ldb_message *link_msg;
4013 struct ldb_message_element *link_el;
4014 struct parsed_dn *link_dns;
4015 struct parsed_dn *p = NULL, *unused = NULL;
4017 if (dsdb_dn_is_deleted_val(&el->values[i])) {
4021 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4023 talloc_free(tmp_ctx);
4024 return LDB_ERR_OPERATIONS_ERROR;
4027 /* remove the link */
4028 msg = ldb_msg_new(tmp_ctx);
4030 ldb_module_oom(module);
4031 talloc_free(tmp_ctx);
4032 return LDB_ERR_OPERATIONS_ERROR;
4036 msg->dn = dsdb_dn->dn;
4038 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4039 if (target_attr == NULL) {
4042 attrs[0] = target_attr->lDAPDisplayName;
4044 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4045 LDB_FLAG_MOD_DELETE, &el2);
4046 if (ret != LDB_SUCCESS) {
4047 ldb_module_oom(module);
4048 talloc_free(tmp_ctx);
4049 return LDB_ERR_OPERATIONS_ERROR;
4052 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4054 DSDB_FLAG_NEXT_MODULE |
4055 DSDB_SEARCH_SHOW_EXTENDED_DN |
4056 DSDB_SEARCH_SHOW_RECYCLED,
4059 if (ret != LDB_SUCCESS) {
4060 talloc_free(tmp_ctx);
4064 link_msg = link_res->msgs[0];
4065 link_el = ldb_msg_find_element(link_msg,
4066 target_attr->lDAPDisplayName);
4067 if (link_el == NULL) {
4068 talloc_free(tmp_ctx);
4069 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4073 * This call 'upgrades' the links in link_dns, but we
4074 * do not commit the result back into the database, so
4075 * this is safe to call in FL2000 or on databases that
4076 * have been run at that level in the past.
4078 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
4080 target_attr->syntax->ldap_oid, parent);
4081 if (ret != LDB_SUCCESS) {
4082 talloc_free(tmp_ctx);
4086 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4090 target_attr->syntax->ldap_oid, false);
4091 if (ret != LDB_SUCCESS) {
4092 talloc_free(tmp_ctx);
4097 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4098 "Failed to find forward link on %s "
4099 "as %s to remove backlink %s on %s",
4100 ldb_dn_get_linearized(msg->dn),
4101 target_attr->lDAPDisplayName,
4102 sa->lDAPDisplayName,
4103 ldb_dn_get_linearized(dn));
4104 talloc_free(tmp_ctx);
4105 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4109 /* This needs to get the Binary DN, by first searching */
4110 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4113 dn_val = data_blob_string_const(dn_str);
4114 el2->values = &dn_val;
4115 el2->num_values = 1;
4118 * Ensure that we tell the modification to vanish any linked
4119 * attributes (not simply mark them as isDeleted = TRUE)
4121 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4123 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4124 if (ret != LDB_SUCCESS) {
4125 talloc_free(tmp_ctx);
4129 talloc_free(tmp_ctx);
4135 handle update of replication meta data for deletion of objects
4137 This also handles the mapping of delete to a rename operation
4138 to allow deletes to be replicated.
4140 It also handles the incoming deleted objects, to ensure they are
4141 fully deleted here. In that case re_delete is true, and we do not
4142 use this as a signal to change the deleted state, just reinforce it.
4145 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4147 int ret = LDB_ERR_OTHER;
4148 bool retb, disallow_move_on_delete;
4149 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4150 const char *rdn_name;
4151 const struct ldb_val *rdn_value, *new_rdn_value;
4153 struct ldb_context *ldb = ldb_module_get_ctx(module);
4154 const struct dsdb_schema *schema;
4155 struct ldb_message *msg, *old_msg;
4156 struct ldb_message_element *el;
4157 TALLOC_CTX *tmp_ctx;
4158 struct ldb_result *res, *parent_res;
4159 static const char * const preserved_attrs[] = {
4160 /* yes, this really is a hard coded list. See MS-ADTS
4161 section 3.1.1.5.5.1.1 */
4164 "dNReferenceUpdate",
4175 "msDS-LastKnownRDN",
4181 "distinguishedName",
4185 "proxiedObjectName",
4187 "nTSecurityDescriptor",
4188 "replPropertyMetaData",
4190 "securityIdentifier",
4198 "userAccountControl",
4205 static const char * const all_attrs[] = {
4206 DSDB_SECRET_ATTRIBUTES,
4210 static const struct ldb_val true_val = {
4211 .data = discard_const_p(uint8_t, "TRUE"),
4216 uint32_t dsdb_flags = 0;
4217 struct replmd_private *replmd_private;
4218 enum deletion_state deletion_state, next_deletion_state;
4220 if (ldb_dn_is_special(req->op.del.dn)) {
4221 return ldb_next_request(module, req);
4225 * We have to allow dbcheck to remove an object that
4226 * is beyond repair, and to do so totally. This could
4227 * mean we we can get a partial object from the other
4228 * DC, causing havoc, so dbcheck suggests
4229 * re-replication first. dbcheck sets both DBCHECK
4230 * and RELAX in this situation.
4232 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4233 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4234 /* really, really remove it */
4235 return ldb_next_request(module, req);
4238 tmp_ctx = talloc_new(ldb);
4241 return LDB_ERR_OPERATIONS_ERROR;
4244 schema = dsdb_get_schema(ldb, tmp_ctx);
4246 talloc_free(tmp_ctx);
4247 return LDB_ERR_OPERATIONS_ERROR;
4250 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4252 /* we need the complete msg off disk, so we can work out which
4253 attributes need to be removed */
4254 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4255 DSDB_FLAG_NEXT_MODULE |
4256 DSDB_SEARCH_SHOW_RECYCLED |
4257 DSDB_SEARCH_REVEAL_INTERNALS |
4258 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4259 if (ret != LDB_SUCCESS) {
4260 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4261 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4262 re_delete ? "re-delete" : "delete",
4263 ldb_dn_get_linearized(old_dn),
4264 ldb_errstring(ldb_module_get_ctx(module)));
4265 talloc_free(tmp_ctx);
4268 old_msg = res->msgs[0];
4270 replmd_deletion_state(module, old_msg,
4272 &next_deletion_state);
4274 /* This supports us noticing an incoming isDeleted and acting on it */
4276 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4277 next_deletion_state = deletion_state;
4280 if (next_deletion_state == OBJECT_REMOVED) {
4282 * We have to prevent objects being deleted, even if
4283 * the administrator really wants them gone, as
4284 * without the tombstone, we can get a partial object
4285 * from the other DC, causing havoc.
4287 * The only other valid case is when the 180 day
4288 * timeout has expired, when relax is specified.
4290 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4291 /* it is already deleted - really remove it this time */
4292 talloc_free(tmp_ctx);
4293 return ldb_next_request(module, req);
4296 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4297 "This check is to prevent corruption of the replicated state.",
4298 ldb_dn_get_linearized(old_msg->dn));
4299 return LDB_ERR_UNWILLING_TO_PERFORM;
4302 rdn_name = ldb_dn_get_rdn_name(old_dn);
4303 rdn_value = ldb_dn_get_rdn_val(old_dn);
4304 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4305 talloc_free(tmp_ctx);
4306 return ldb_operr(ldb);
4309 msg = ldb_msg_new(tmp_ctx);
4311 ldb_module_oom(module);
4312 talloc_free(tmp_ctx);
4313 return LDB_ERR_OPERATIONS_ERROR;
4318 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4319 disallow_move_on_delete =
4320 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4321 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4323 /* work out where we will be renaming this object to */
4324 if (!disallow_move_on_delete) {
4325 struct ldb_dn *deleted_objects_dn;
4326 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4327 &deleted_objects_dn);
4330 * We should not move objects if we can't find the
4331 * deleted objects DN. Not moving (or otherwise
4332 * harming) the Deleted Objects DN itself is handled
4335 if (re_delete && (ret != LDB_SUCCESS)) {
4336 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4337 if (new_dn == NULL) {
4338 ldb_module_oom(module);
4339 talloc_free(tmp_ctx);
4340 return LDB_ERR_OPERATIONS_ERROR;
4342 } else if (ret != LDB_SUCCESS) {
4343 /* this is probably an attempted delete on a partition
4344 * that doesn't allow delete operations, such as the
4345 * schema partition */
4346 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4347 ldb_dn_get_linearized(old_dn));
4348 talloc_free(tmp_ctx);
4349 return LDB_ERR_UNWILLING_TO_PERFORM;
4351 new_dn = deleted_objects_dn;
4354 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4355 if (new_dn == NULL) {
4356 ldb_module_oom(module);
4357 talloc_free(tmp_ctx);
4358 return LDB_ERR_OPERATIONS_ERROR;
4362 /* get the objects GUID from the search we just did */
4363 guid = samdb_result_guid(old_msg, "objectGUID");
4365 if (deletion_state == OBJECT_NOT_DELETED) {
4366 struct ldb_message_element *is_deleted_el;
4368 ret = replmd_make_deleted_child_dn(tmp_ctx,
4371 rdn_name, rdn_value,
4374 if (ret != LDB_SUCCESS) {
4375 talloc_free(tmp_ctx);
4379 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4381 if (ret != LDB_SUCCESS) {
4382 ldb_asprintf_errstring(ldb, __location__
4383 ": Failed to add isDeleted string to the msg");
4384 talloc_free(tmp_ctx);
4387 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4390 * No matter what has happened with other renames etc, try again to
4391 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4394 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4395 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4397 ldb_asprintf_errstring(ldb, __location__
4398 ": Unable to add a prepare rdn of %s",
4399 ldb_dn_get_linearized(rdn));
4400 talloc_free(tmp_ctx);
4401 return LDB_ERR_OPERATIONS_ERROR;
4403 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4405 retb = ldb_dn_add_child(new_dn, rdn);
4407 ldb_asprintf_errstring(ldb, __location__
4408 ": Unable to add rdn %s to base dn: %s",
4409 ldb_dn_get_linearized(rdn),
4410 ldb_dn_get_linearized(new_dn));
4411 talloc_free(tmp_ctx);
4412 return LDB_ERR_OPERATIONS_ERROR;
4417 now we need to modify the object in the following ways:
4419 - add isDeleted=TRUE
4420 - update rDN and name, with new rDN
4421 - remove linked attributes
4422 - remove objectCategory and sAMAccountType
4423 - remove attribs not on the preserved list
4424 - preserved if in above list, or is rDN
4425 - remove all linked attribs from this object
4426 - remove all links from other objects to this object
4427 (note we use the backlinks to do this, so we won't find one-way
4428 links that still point to this object, or deactivated two-way
4429 links, i.e. 'member' after the user has been removed from the
4431 - add lastKnownParent
4432 - update replPropertyMetaData?
4434 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4437 if (deletion_state == OBJECT_NOT_DELETED) {
4438 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4439 char *parent_dn_str = NULL;
4440 struct ldb_message_element *p_el;
4442 /* we need the storage form of the parent GUID */
4443 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4445 DSDB_FLAG_NEXT_MODULE |
4446 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4447 DSDB_SEARCH_REVEAL_INTERNALS|
4448 DSDB_SEARCH_SHOW_RECYCLED, req);
4449 if (ret != LDB_SUCCESS) {
4450 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4451 "repmd_delete: Failed to %s %s, "
4452 "because we failed to find it's parent (%s): %s",
4453 re_delete ? "re-delete" : "delete",
4454 ldb_dn_get_linearized(old_dn),
4455 ldb_dn_get_linearized(parent_dn),
4456 ldb_errstring(ldb_module_get_ctx(module)));
4457 talloc_free(tmp_ctx);
4462 * Now we can use the DB version,
4463 * it will have the extended DN info in it
4465 parent_dn = parent_res->msgs[0]->dn;
4466 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4469 if (parent_dn_str == NULL) {
4470 talloc_free(tmp_ctx);
4471 return ldb_module_oom(module);
4474 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4476 if (ret != LDB_SUCCESS) {
4477 ldb_asprintf_errstring(ldb, __location__
4478 ": Failed to add lastKnownParent "
4479 "string when deleting %s",
4480 ldb_dn_get_linearized(old_dn));
4481 talloc_free(tmp_ctx);
4484 p_el = ldb_msg_find_element(msg,
4487 talloc_free(tmp_ctx);
4488 return ldb_module_operr(module);
4490 p_el->flags = LDB_FLAG_MOD_REPLACE;
4492 if (next_deletion_state == OBJECT_DELETED) {
4493 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4494 if (ret != LDB_SUCCESS) {
4495 ldb_asprintf_errstring(ldb, __location__
4496 ": Failed to add msDS-LastKnownRDN "
4497 "string when deleting %s",
4498 ldb_dn_get_linearized(old_dn));
4499 talloc_free(tmp_ctx);
4502 p_el = ldb_msg_find_element(msg,
4503 "msDS-LastKnownRDN");
4505 talloc_free(tmp_ctx);
4506 return ldb_module_operr(module);
4508 p_el->flags = LDB_FLAG_MOD_ADD;
4512 switch (next_deletion_state) {
4514 case OBJECT_RECYCLED:
4515 case OBJECT_TOMBSTONE:
4518 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4519 * describes what must be removed from a tombstone
4522 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4523 * describes what must be removed from a recycled
4529 * we also mark it as recycled, meaning this object can't be
4530 * recovered (we are stripping its attributes).
4531 * This is done only if we have this schema object of course ...
4532 * This behavior is identical to the one of Windows 2008R2 which
4533 * always set the isRecycled attribute, even if the recycle-bin is
4534 * not activated and what ever the forest level is.
4536 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4537 struct ldb_message_element *is_recycled_el;
4539 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4541 if (ret != LDB_SUCCESS) {
4542 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4543 ldb_module_oom(module);
4544 talloc_free(tmp_ctx);
4547 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4550 replmd_private = talloc_get_type(ldb_module_get_private(module),
4551 struct replmd_private);
4552 /* work out which of the old attributes we will be removing */
4553 for (i=0; i<old_msg->num_elements; i++) {
4554 const struct dsdb_attribute *sa;
4555 el = &old_msg->elements[i];
4556 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4558 talloc_free(tmp_ctx);
4559 return LDB_ERR_OPERATIONS_ERROR;
4561 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4562 /* don't remove the rDN */
4566 if (sa->linkID & 1) {
4568 * we have a backlink in this object
4569 * that needs to be removed. We're not
4570 * allowed to remove it directly
4571 * however, so we instead setup a
4572 * modify to delete the corresponding
4575 ret = replmd_delete_remove_link(module, schema,
4579 if (ret == LDB_SUCCESS) {
4581 * now we continue, which means we
4582 * won't remove this backlink
4588 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4589 const char *old_dn_str
4590 = ldb_dn_get_linearized(old_dn);
4591 ldb_asprintf_errstring(ldb,
4593 ": Failed to remove backlink of "
4594 "%s when deleting %s: %s",
4597 ldb_errstring(ldb));
4598 talloc_free(tmp_ctx);
4599 return LDB_ERR_OPERATIONS_ERROR;
4603 * Otherwise vanish the link, we are
4604 * out of sync and the controlling
4605 * object does not have the source
4609 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4611 } else if (sa->linkID == 0) {
4612 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4615 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4620 * Ensure that we tell the modification to vanish any linked
4621 * attributes (not simply mark them as isDeleted = TRUE)
4623 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4625 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4626 if (ret != LDB_SUCCESS) {
4627 talloc_free(tmp_ctx);
4628 ldb_module_oom(module);
4635 case OBJECT_DELETED:
4637 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4638 * describes what must be removed from a deleted
4642 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4643 if (ret != LDB_SUCCESS) {
4644 talloc_free(tmp_ctx);
4645 ldb_module_oom(module);
4649 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4650 if (ret != LDB_SUCCESS) {
4651 talloc_free(tmp_ctx);
4652 ldb_module_oom(module);
4662 if (deletion_state == OBJECT_NOT_DELETED) {
4663 const struct dsdb_attribute *sa;
4665 /* work out what the new rdn value is, for updating the
4666 rDN and name fields */
4667 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4668 if (new_rdn_value == NULL) {
4669 talloc_free(tmp_ctx);
4670 return ldb_operr(ldb);
4673 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4675 talloc_free(tmp_ctx);
4676 return LDB_ERR_OPERATIONS_ERROR;
4679 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4681 if (ret != LDB_SUCCESS) {
4682 talloc_free(tmp_ctx);
4685 el->flags = LDB_FLAG_MOD_REPLACE;
4687 el = ldb_msg_find_element(old_msg, "name");
4689 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4690 if (ret != LDB_SUCCESS) {
4691 talloc_free(tmp_ctx);
4694 el->flags = LDB_FLAG_MOD_REPLACE;
4699 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4704 * No matter what has happned with other renames, try again to
4705 * get this to be under the deleted DN.
4707 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4708 /* now rename onto the new DN */
4709 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4710 if (ret != LDB_SUCCESS){
4711 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4712 ldb_dn_get_linearized(old_dn),
4713 ldb_dn_get_linearized(new_dn),
4714 ldb_errstring(ldb)));
4715 talloc_free(tmp_ctx);
4721 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4722 if (ret != LDB_SUCCESS) {
4723 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4724 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4725 talloc_free(tmp_ctx);
4729 talloc_free(tmp_ctx);
4731 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4734 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4736 return replmd_delete_internals(module, req, false);
4740 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4745 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4747 int ret = LDB_ERR_OTHER;
4748 /* TODO: do some error mapping */
4750 /* Let the caller know the full WERROR */
4751 ar->objs->error = status;
4757 static struct replPropertyMetaData1 *
4758 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4759 enum drsuapi_DsAttributeId attid)
4762 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4764 for (i = 0; i < rpmd_ctr->count; i++) {
4765 if (rpmd_ctr->array[i].attid == attid) {
4766 return &rpmd_ctr->array[i];
4774 return true if an update is newer than an existing entry
4775 see section 5.11 of MS-ADTS
4777 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4778 const struct GUID *update_invocation_id,
4779 uint32_t current_version,
4780 uint32_t update_version,
4781 NTTIME current_change_time,
4782 NTTIME update_change_time)
4784 if (update_version != current_version) {
4785 return update_version > current_version;
4787 if (update_change_time != current_change_time) {
4788 return update_change_time > current_change_time;
4790 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4793 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4794 struct replPropertyMetaData1 *new_m)
4796 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4797 &new_m->originating_invocation_id,
4800 cur_m->originating_change_time,
4801 new_m->originating_change_time);
4804 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4805 struct replPropertyMetaData1 *cur_m,
4806 struct replPropertyMetaData1 *new_m)
4811 * If the new replPropertyMetaData entry for this attribute is
4812 * not provided (this happens in the case where we look for
4813 * ATTID_name, but the name was not changed), then the local
4814 * state is clearly still current, as the remote
4815 * server didn't send it due to being older the high watermark
4818 if (new_m == NULL) {
4822 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4824 * if we compare equal then do an
4825 * update. This is used when a client
4826 * asks for a FULL_SYNC, and can be
4827 * used to recover a corrupt
4830 * This call is a bit tricky, what we
4831 * are doing it turning the 'is_newer'
4832 * call into a 'not is older' by
4833 * swapping cur_m and new_m, and negating the
4836 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4839 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4847 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4849 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4850 struct ldb_context *ldb,
4852 const char *four_char_prefix,
4853 const char *rdn_name,
4854 const struct ldb_val *rdn_value,
4857 struct ldb_val deleted_child_rdn_val;
4858 struct GUID_txt_buf guid_str;
4862 GUID_buf_string(&guid, &guid_str);
4864 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4866 ldb_asprintf_errstring(ldb, __location__
4867 ": Unable to add a formatted child to dn: %s",
4868 ldb_dn_get_linearized(dn));
4869 return LDB_ERR_OPERATIONS_ERROR;
4873 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4874 * we should truncate this value to ensure the RDN is not more than 255 chars.
4876 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4878 * "Naming constraints are not enforced for replicated
4879 * updates." so this is safe and we don't have to work out not
4880 * splitting a UTF8 char right now.
4882 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4885 * sizeof(guid_str.buf) will always be longer than
4886 * strlen(guid_str.buf) but we allocate using this and
4887 * waste the trailing bytes to avoid scaring folks
4888 * with memcpy() using strlen() below
4891 deleted_child_rdn_val.data
4892 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4894 rdn_value->length + 5
4895 + sizeof(guid_str.buf));
4896 if (!deleted_child_rdn_val.data) {
4897 ldb_asprintf_errstring(ldb, __location__
4898 ": Unable to add a formatted child to dn: %s",
4899 ldb_dn_get_linearized(dn));
4900 return LDB_ERR_OPERATIONS_ERROR;
4903 deleted_child_rdn_val.length =
4904 rdn_value->length + 5
4905 + strlen(guid_str.buf);
4907 SMB_ASSERT(deleted_child_rdn_val.length <
4908 talloc_get_size(deleted_child_rdn_val.data));
4911 * talloc won't allocate more than 256MB so we can't
4912 * overflow but just to be sure
4914 if (deleted_child_rdn_val.length < rdn_value->length) {
4915 return LDB_ERR_OPERATIONS_ERROR;
4918 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4919 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4920 four_char_prefix, 4);
4921 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4923 sizeof(guid_str.buf));
4925 /* Now set the value into the RDN, without parsing it */
4926 ret = ldb_dn_set_component(
4930 deleted_child_rdn_val);
4939 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4940 struct ldb_context *ldb,
4944 const struct ldb_val *rdn_val;
4945 const char *rdn_name;
4946 struct ldb_dn *new_dn;
4949 rdn_val = ldb_dn_get_rdn_val(dn);
4950 rdn_name = ldb_dn_get_rdn_name(dn);
4951 if (!rdn_val || !rdn_name) {
4955 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4960 ret = replmd_make_prefix_child_dn(mem_ctx,
4966 if (ret != LDB_SUCCESS) {
4975 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4976 struct ldb_context *ldb,
4978 const char *rdn_name,
4979 const struct ldb_val *rdn_value,
4982 return replmd_make_prefix_child_dn(tmp_ctx,
4992 perform a modify operation which sets the rDN and name attributes to
4993 their current values. This has the effect of changing these
4994 attributes to have been last updated by the current DC. This is
4995 needed to ensure that renames performed as part of conflict
4996 resolution are propagated to other DCs
4998 static int replmd_name_modify(struct replmd_replicated_request *ar,
4999 struct ldb_request *req, struct ldb_dn *dn)
5001 struct ldb_message *msg;
5002 const char *rdn_name;
5003 const struct ldb_val *rdn_val;
5004 const struct dsdb_attribute *rdn_attr;
5007 msg = ldb_msg_new(req);
5013 rdn_name = ldb_dn_get_rdn_name(dn);
5014 if (rdn_name == NULL) {
5018 /* normalize the rdn attribute name */
5019 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5020 if (rdn_attr == NULL) {
5023 rdn_name = rdn_attr->lDAPDisplayName;
5025 rdn_val = ldb_dn_get_rdn_val(dn);
5026 if (rdn_val == NULL) {
5030 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5033 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
5036 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5039 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
5044 * We have to mark this as a replicated update otherwise
5045 * schema_data may reject a rename in the schema partition
5048 ret = dsdb_module_modify(ar->module, msg,
5049 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5051 if (ret != LDB_SUCCESS) {
5052 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5053 ldb_dn_get_linearized(dn),
5054 ldb_errstring(ldb_module_get_ctx(ar->module))));
5064 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5065 ldb_dn_get_linearized(dn)));
5066 return LDB_ERR_OPERATIONS_ERROR;
5071 callback for conflict DN handling where we have renamed the incoming
5072 record. After renaming it, we need to ensure the change of name and
5073 rDN for the incoming record is seen as an originating update by this DC.
5075 This also handles updating lastKnownParent for entries sent to lostAndFound
5077 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5079 struct replmd_replicated_request *ar =
5080 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5081 struct ldb_dn *conflict_dn = NULL;
5084 if (ares->error != LDB_SUCCESS) {
5085 /* call the normal callback for everything except success */
5086 return replmd_op_callback(req, ares);
5089 switch (req->operation) {
5091 conflict_dn = req->op.add.message->dn;
5094 conflict_dn = req->op.mod.message->dn;
5097 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5100 /* perform a modify of the rDN and name of the record */
5101 ret = replmd_name_modify(ar, req, conflict_dn);
5102 if (ret != LDB_SUCCESS) {
5104 return replmd_op_callback(req, ares);
5107 if (ar->objs->objects[ar->index_current].last_known_parent) {
5108 struct ldb_message *msg = ldb_msg_new(req);
5110 ldb_module_oom(ar->module);
5111 return LDB_ERR_OPERATIONS_ERROR;
5114 msg->dn = req->op.add.message->dn;
5116 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5117 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5118 if (ret != LDB_SUCCESS) {
5119 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5120 ldb_module_oom(ar->module);
5123 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5125 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5126 if (ret != LDB_SUCCESS) {
5127 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5128 ldb_dn_get_linearized(msg->dn),
5129 ldb_errstring(ldb_module_get_ctx(ar->module))));
5135 return replmd_op_callback(req, ares);
5139 callback for replmd_replicated_apply_add()
5140 This copes with the creation of conflict records in the case where
5141 the DN exists, but with a different objectGUID
5143 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))
5145 struct ldb_dn *conflict_dn;
5146 struct replmd_replicated_request *ar =
5147 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5148 struct ldb_result *res;
5149 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5151 const struct ldb_val *omd_value;
5152 struct replPropertyMetaDataBlob omd, *rmd;
5153 enum ndr_err_code ndr_err;
5154 bool rename_incoming_record, rodc;
5155 struct replPropertyMetaData1 *rmd_name, *omd_name;
5156 struct ldb_message *msg;
5157 struct ldb_request *down_req = NULL;
5159 /* call the normal callback for success */
5160 if (ares->error == LDB_SUCCESS) {
5161 return callback(req, ares);
5165 * we have a conflict, and need to decide if we will keep the
5166 * new record or the old record
5169 msg = ar->objs->objects[ar->index_current].msg;
5170 conflict_dn = msg->dn;
5172 /* For failures other than conflicts, fail the whole operation here */
5173 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5174 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5175 ldb_dn_get_linearized(conflict_dn),
5176 ldb_errstring(ldb_module_get_ctx(ar->module)));
5178 return ldb_module_done(ar->req, NULL, NULL,
5179 LDB_ERR_OPERATIONS_ERROR);
5182 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5183 if (ret != LDB_SUCCESS) {
5184 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)));
5185 return ldb_module_done(ar->req, NULL, NULL,
5186 LDB_ERR_OPERATIONS_ERROR);
5192 * We are on an RODC, or were a GC for this
5193 * partition, so we have to fail this until
5194 * someone who owns the partition sorts it
5197 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5198 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5199 " - We must fail the operation until a master for this partition resolves the conflict",
5200 ldb_dn_get_linearized(conflict_dn));
5201 ret = LDB_ERR_OPERATIONS_ERROR;
5206 * first we need the replPropertyMetaData attribute from the
5207 * local, conflicting record
5209 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5211 DSDB_FLAG_NEXT_MODULE |
5212 DSDB_SEARCH_SHOW_DELETED |
5213 DSDB_SEARCH_SHOW_RECYCLED, req);
5214 if (ret != LDB_SUCCESS) {
5215 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5216 ldb_dn_get_linearized(conflict_dn)));
5220 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5221 if (omd_value == NULL) {
5222 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5223 ldb_dn_get_linearized(conflict_dn)));
5227 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5228 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5229 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5230 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5231 ldb_dn_get_linearized(conflict_dn)));
5235 rmd = ar->objs->objects[ar->index_current].meta_data;
5238 * we decide which is newer based on the RPMD on the name
5239 * attribute. See [MS-DRSR] ResolveNameConflict.
5241 * We expect omd_name to be present, as this is from a local
5242 * search, but while rmd_name should have been given to us by
5243 * the remote server, if it is missing we just prefer the
5245 * replmd_replPropertyMetaData1_new_should_be_taken()
5247 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5248 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5250 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5251 ldb_dn_get_linearized(conflict_dn)));
5256 * Should we preserve the current record, and so rename the
5257 * incoming record to be a conflict?
5259 rename_incoming_record
5260 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5261 omd_name, rmd_name);
5263 if (rename_incoming_record) {
5265 struct ldb_dn *new_dn;
5267 guid = samdb_result_guid(msg, "objectGUID");
5268 if (GUID_all_zero(&guid)) {
5269 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5270 ldb_dn_get_linearized(conflict_dn)));
5273 new_dn = replmd_conflict_dn(req,
5274 ldb_module_get_ctx(ar->module),
5275 conflict_dn, &guid);
5276 if (new_dn == NULL) {
5277 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5278 ldb_dn_get_linearized(conflict_dn)));
5282 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5283 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5285 /* re-submit the request, but with the new DN */
5286 callback = replmd_op_name_modify_callback;
5289 /* we are renaming the existing record */
5291 struct ldb_dn *new_dn;
5293 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5294 if (GUID_all_zero(&guid)) {
5295 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5296 ldb_dn_get_linearized(conflict_dn)));
5300 new_dn = replmd_conflict_dn(req,
5301 ldb_module_get_ctx(ar->module),
5302 conflict_dn, &guid);
5303 if (new_dn == NULL) {
5304 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5305 ldb_dn_get_linearized(conflict_dn)));
5309 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5310 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5312 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5313 DSDB_FLAG_OWN_MODULE, req);
5314 if (ret != LDB_SUCCESS) {
5315 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5316 ldb_dn_get_linearized(conflict_dn),
5317 ldb_dn_get_linearized(new_dn),
5318 ldb_errstring(ldb_module_get_ctx(ar->module))));
5323 * now we need to ensure that the rename is seen as an
5324 * originating update. We do that with a modify.
5326 ret = replmd_name_modify(ar, req, new_dn);
5327 if (ret != LDB_SUCCESS) {
5331 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5332 ldb_dn_get_linearized(req->op.add.message->dn)));
5335 ret = ldb_build_add_req(&down_req,
5336 ldb_module_get_ctx(ar->module),
5343 if (ret != LDB_SUCCESS) {
5346 LDB_REQ_SET_LOCATION(down_req);
5348 /* current partition control needed by "repmd_op_callback" */
5349 ret = ldb_request_add_control(down_req,
5350 DSDB_CONTROL_CURRENT_PARTITION_OID,
5352 if (ret != LDB_SUCCESS) {
5353 return replmd_replicated_request_error(ar, ret);
5356 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5357 /* this tells the partition module to make it a
5358 partial replica if creating an NC */
5359 ret = ldb_request_add_control(down_req,
5360 DSDB_CONTROL_PARTIAL_REPLICA,
5362 if (ret != LDB_SUCCESS) {
5363 return replmd_replicated_request_error(ar, ret);
5368 * Finally we re-run the add, otherwise the new record won't
5369 * exist, as we are here because of that exact failure!
5371 return ldb_next_request(ar->module, down_req);
5374 /* on failure make the caller get the error. This means
5375 * replication will stop with an error, but there is not much
5378 if (ret == LDB_SUCCESS) {
5379 ret = LDB_ERR_OPERATIONS_ERROR;
5381 return ldb_module_done(ar->req, NULL, NULL,
5386 callback for replmd_replicated_apply_add()
5387 This copes with the creation of conflict records in the case where
5388 the DN exists, but with a different objectGUID
5390 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5392 struct replmd_replicated_request *ar =
5393 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5395 if (ar->objs->objects[ar->index_current].last_known_parent) {
5396 /* This is like a conflict DN, where we put the object in LostAndFound
5397 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5398 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5401 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5405 this is called when a new object comes in over DRS
5407 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5409 struct ldb_context *ldb;
5410 struct ldb_request *change_req;
5411 enum ndr_err_code ndr_err;
5412 struct ldb_message *msg;
5413 struct replPropertyMetaDataBlob *md;
5414 struct ldb_val md_value;
5417 bool remote_isDeleted = false;
5420 time_t t = time(NULL);
5421 const struct ldb_val *rdn_val;
5422 struct replmd_private *replmd_private =
5423 talloc_get_type(ldb_module_get_private(ar->module),
5424 struct replmd_private);
5425 unix_to_nt_time(&now, t);
5427 ldb = ldb_module_get_ctx(ar->module);
5428 msg = ar->objs->objects[ar->index_current].msg;
5429 md = ar->objs->objects[ar->index_current].meta_data;
5430 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5432 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5433 if (ret != LDB_SUCCESS) {
5434 return replmd_replicated_request_error(ar, ret);
5437 ret = dsdb_msg_add_guid(msg,
5438 &ar->objs->objects[ar->index_current].object_guid,
5440 if (ret != LDB_SUCCESS) {
5441 return replmd_replicated_request_error(ar, ret);
5444 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5445 if (ret != LDB_SUCCESS) {
5446 return replmd_replicated_request_error(ar, ret);
5449 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5450 if (ret != LDB_SUCCESS) {
5451 return replmd_replicated_request_error(ar, ret);
5454 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5455 if (ret != LDB_SUCCESS) {
5456 return replmd_replicated_request_error(ar, ret);
5459 /* remove any message elements that have zero values */
5460 for (i=0; i<msg->num_elements; i++) {
5461 struct ldb_message_element *el = &msg->elements[i];
5463 if (el->num_values == 0) {
5464 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5465 ldb_asprintf_errstring(ldb, __location__
5466 ": empty objectClass sent on %s, aborting replication\n",
5467 ldb_dn_get_linearized(msg->dn));
5468 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5471 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5473 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5474 msg->num_elements--;
5481 struct GUID_txt_buf guid_txt;
5483 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5486 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5487 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5490 } else if (DEBUGLVL(4)) {
5491 struct GUID_txt_buf guid_txt;
5492 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5493 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5494 ldb_dn_get_linearized(msg->dn)));
5496 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5497 "isDeleted", false);
5500 * the meta data array is already sorted by the caller, except
5501 * for the RDN, which needs to be added.
5505 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5506 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5507 md, ar, now, is_schema_nc,
5509 if (ret != LDB_SUCCESS) {
5510 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5511 return replmd_replicated_request_error(ar, ret);
5514 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5515 if (ret != LDB_SUCCESS) {
5516 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5517 return replmd_replicated_request_error(ar, ret);
5520 for (i=0; i < md->ctr.ctr1.count; i++) {
5521 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5523 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5524 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5525 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5526 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5527 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5529 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5530 if (ret != LDB_SUCCESS) {
5531 return replmd_replicated_request_error(ar, ret);
5534 replmd_ldb_message_sort(msg, ar->schema);
5536 if (!remote_isDeleted) {
5537 ret = dsdb_module_schedule_sd_propagation(ar->module,
5538 ar->objs->partition_dn,
5540 if (ret != LDB_SUCCESS) {
5541 return replmd_replicated_request_error(ar, ret);
5545 ar->isDeleted = remote_isDeleted;
5547 ret = ldb_build_add_req(&change_req,
5553 replmd_op_add_callback,
5555 LDB_REQ_SET_LOCATION(change_req);
5556 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5558 /* current partition control needed by "repmd_op_callback" */
5559 ret = ldb_request_add_control(change_req,
5560 DSDB_CONTROL_CURRENT_PARTITION_OID,
5562 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5564 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5565 /* this tells the partition module to make it a
5566 partial replica if creating an NC */
5567 ret = ldb_request_add_control(change_req,
5568 DSDB_CONTROL_PARTIAL_REPLICA,
5570 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5573 return ldb_next_request(ar->module, change_req);
5576 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5577 struct ldb_reply *ares)
5579 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5580 struct replmd_replicated_request);
5584 return ldb_module_done(ar->req, NULL, NULL,
5585 LDB_ERR_OPERATIONS_ERROR);
5589 * The error NO_SUCH_OBJECT is not expected, unless the search
5590 * base is the partition DN, and that case doesn't happen here
5591 * because then we wouldn't get a parent_guid_value in any
5594 if (ares->error != LDB_SUCCESS) {
5595 return ldb_module_done(ar->req, ares->controls,
5596 ares->response, ares->error);
5599 switch (ares->type) {
5600 case LDB_REPLY_ENTRY:
5602 struct ldb_message *parent_msg = ares->message;
5603 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5604 struct ldb_dn *parent_dn = NULL;
5607 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5608 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5609 /* Per MS-DRSR 4.1.10.6.10
5610 * FindBestParentObject we need to move this
5611 * new object under a deleted object to
5613 struct ldb_dn *nc_root;
5615 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5616 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5617 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5618 "No suitable NC root found for %s. "
5619 "We need to move this object because parent object %s "
5620 "is deleted, but this object is not.",
5621 ldb_dn_get_linearized(msg->dn),
5622 ldb_dn_get_linearized(parent_msg->dn));
5623 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5624 } else if (ret != LDB_SUCCESS) {
5625 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5626 "Unable to find NC root for %s: %s. "
5627 "We need to move this object because parent object %s "
5628 "is deleted, but this object is not.",
5629 ldb_dn_get_linearized(msg->dn),
5630 ldb_errstring(ldb_module_get_ctx(ar->module)),
5631 ldb_dn_get_linearized(parent_msg->dn));
5632 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5635 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5637 DS_GUID_LOSTANDFOUND_CONTAINER,
5639 if (ret != LDB_SUCCESS) {
5640 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5641 "Unable to find LostAndFound Container for %s "
5642 "in partition %s: %s. "
5643 "We need to move this object because parent object %s "
5644 "is deleted, but this object is not.",
5645 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5646 ldb_errstring(ldb_module_get_ctx(ar->module)),
5647 ldb_dn_get_linearized(parent_msg->dn));
5648 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5650 ar->objs->objects[ar->index_current].last_known_parent
5651 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5655 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5658 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5660 comp_num = ldb_dn_get_comp_num(msg->dn);
5662 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5664 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5667 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5669 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5673 case LDB_REPLY_REFERRAL:
5674 /* we ignore referrals */
5677 case LDB_REPLY_DONE:
5679 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5680 struct GUID_txt_buf str_buf;
5681 if (ar->search_msg != NULL) {
5682 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5683 "No parent with GUID %s found for object locally known as %s",
5684 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5685 ldb_dn_get_linearized(ar->search_msg->dn));
5687 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5688 "No parent with GUID %s found for object remotely known as %s",
5689 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5690 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5694 * This error code is really important, as it
5695 * is the flag back to the callers to retry
5696 * this with DRSUAPI_DRS_GET_ANC, and so get
5697 * the parent objects before the child
5700 return ldb_module_done(ar->req, NULL, NULL,
5701 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5704 if (ar->search_msg != NULL) {
5705 ret = replmd_replicated_apply_merge(ar);
5707 ret = replmd_replicated_apply_add(ar);
5709 if (ret != LDB_SUCCESS) {
5710 return ldb_module_done(ar->req, NULL, NULL, ret);
5719 * Look for the parent object, so we put the new object in the right
5720 * place This is akin to NameObject in MS-DRSR - this routine and the
5721 * callbacks find the right parent name, and correct name for this
5725 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5727 struct ldb_context *ldb;
5731 struct ldb_request *search_req;
5732 static const char *attrs[] = {"isDeleted", NULL};
5733 struct GUID_txt_buf guid_str_buf;
5735 ldb = ldb_module_get_ctx(ar->module);
5737 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5738 if (ar->search_msg != NULL) {
5739 return replmd_replicated_apply_merge(ar);
5741 return replmd_replicated_apply_add(ar);
5745 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5748 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5749 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5751 ret = ldb_build_search_req(&search_req,
5754 ar->objs->partition_dn,
5760 replmd_replicated_apply_search_for_parent_callback,
5762 LDB_REQ_SET_LOCATION(search_req);
5764 ret = dsdb_request_add_controls(search_req,
5765 DSDB_SEARCH_SHOW_RECYCLED|
5766 DSDB_SEARCH_SHOW_DELETED|
5767 DSDB_SEARCH_SHOW_EXTENDED_DN);
5768 if (ret != LDB_SUCCESS) {
5772 return ldb_next_request(ar->module, search_req);
5776 handle renames that come in over DRS replication
5778 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5779 struct ldb_message *msg,
5780 struct ldb_request *parent,
5784 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5785 struct ldb_result *res;
5786 struct ldb_dn *conflict_dn;
5787 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5788 const struct ldb_val *omd_value;
5789 struct replPropertyMetaDataBlob omd, *rmd;
5790 enum ndr_err_code ndr_err;
5791 bool rename_incoming_record, rodc;
5792 struct replPropertyMetaData1 *rmd_name, *omd_name;
5793 struct ldb_dn *new_dn;
5796 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5797 ldb_dn_get_linearized(ar->search_msg->dn),
5798 ldb_dn_get_linearized(msg->dn)));
5801 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5802 DSDB_FLAG_NEXT_MODULE, ar->req);
5803 if (ret == LDB_SUCCESS) {
5804 talloc_free(tmp_ctx);
5809 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5810 talloc_free(tmp_ctx);
5811 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5812 ldb_dn_get_linearized(ar->search_msg->dn),
5813 ldb_dn_get_linearized(msg->dn),
5814 ldb_errstring(ldb_module_get_ctx(ar->module)));
5818 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5819 if (ret != LDB_SUCCESS) {
5820 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5821 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5822 ldb_errstring(ldb_module_get_ctx(ar->module)));
5823 return LDB_ERR_OPERATIONS_ERROR;
5826 * we have a conflict, and need to decide if we will keep the
5827 * new record or the old record
5830 conflict_dn = msg->dn;
5834 * We are on an RODC, or were a GC for this
5835 * partition, so we have to fail this until
5836 * someone who owns the partition sorts it
5839 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5840 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5841 " - We must fail the operation until a master for this partition resolves the conflict",
5842 ldb_dn_get_linearized(conflict_dn));
5843 ret = LDB_ERR_OPERATIONS_ERROR;
5848 * first we need the replPropertyMetaData attribute from the
5851 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5853 DSDB_FLAG_NEXT_MODULE |
5854 DSDB_SEARCH_SHOW_DELETED |
5855 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5856 if (ret != LDB_SUCCESS) {
5857 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5858 ldb_dn_get_linearized(conflict_dn)));
5862 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5863 if (omd_value == NULL) {
5864 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5865 ldb_dn_get_linearized(conflict_dn)));
5869 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5870 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5871 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5872 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5873 ldb_dn_get_linearized(conflict_dn)));
5877 rmd = ar->objs->objects[ar->index_current].meta_data;
5880 * we decide which is newer based on the RPMD on the name
5881 * attribute. See [MS-DRSR] ResolveNameConflict.
5883 * We expect omd_name to be present, as this is from a local
5884 * search, but while rmd_name should have been given to us by
5885 * the remote server, if it is missing we just prefer the
5887 * replmd_replPropertyMetaData1_new_should_be_taken()
5889 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5890 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5892 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5893 ldb_dn_get_linearized(conflict_dn)));
5898 * Should we preserve the current record, and so rename the
5899 * incoming record to be a conflict?
5901 rename_incoming_record =
5902 !replmd_replPropertyMetaData1_new_should_be_taken(
5903 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5904 omd_name, rmd_name);
5906 if (rename_incoming_record) {
5908 new_dn = replmd_conflict_dn(msg,
5909 ldb_module_get_ctx(ar->module),
5911 &ar->objs->objects[ar->index_current].object_guid);
5912 if (new_dn == NULL) {
5913 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5914 "Failed to form conflict DN for %s\n",
5915 ldb_dn_get_linearized(msg->dn));
5917 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5920 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5921 DSDB_FLAG_NEXT_MODULE, ar->req);
5922 if (ret != LDB_SUCCESS) {
5923 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5924 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5925 ldb_dn_get_linearized(conflict_dn),
5926 ldb_dn_get_linearized(ar->search_msg->dn),
5927 ldb_dn_get_linearized(new_dn),
5928 ldb_errstring(ldb_module_get_ctx(ar->module)));
5929 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5937 /* we are renaming the existing record */
5939 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5940 if (GUID_all_zero(&guid)) {
5941 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5942 ldb_dn_get_linearized(conflict_dn)));
5946 new_dn = replmd_conflict_dn(tmp_ctx,
5947 ldb_module_get_ctx(ar->module),
5948 conflict_dn, &guid);
5949 if (new_dn == NULL) {
5950 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5951 ldb_dn_get_linearized(conflict_dn)));
5955 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5956 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5958 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5959 DSDB_FLAG_OWN_MODULE, ar->req);
5960 if (ret != LDB_SUCCESS) {
5961 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5962 ldb_dn_get_linearized(conflict_dn),
5963 ldb_dn_get_linearized(new_dn),
5964 ldb_errstring(ldb_module_get_ctx(ar->module))));
5969 * now we need to ensure that the rename is seen as an
5970 * originating update. We do that with a modify.
5972 ret = replmd_name_modify(ar, ar->req, new_dn);
5973 if (ret != LDB_SUCCESS) {
5977 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5978 ldb_dn_get_linearized(ar->search_msg->dn),
5979 ldb_dn_get_linearized(msg->dn)));
5982 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5983 DSDB_FLAG_NEXT_MODULE, ar->req);
5984 if (ret != LDB_SUCCESS) {
5985 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5986 ldb_dn_get_linearized(ar->search_msg->dn),
5987 ldb_dn_get_linearized(msg->dn),
5988 ldb_errstring(ldb_module_get_ctx(ar->module))));
5992 talloc_free(tmp_ctx);
5996 * On failure make the caller get the error
5997 * This means replication will stop with an error,
5998 * but there is not much else we can do. In the
5999 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6002 if (ret == LDB_SUCCESS) {
6003 ret = LDB_ERR_OPERATIONS_ERROR;
6006 talloc_free(tmp_ctx);
6011 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6013 struct ldb_context *ldb;
6014 struct ldb_request *change_req;
6015 enum ndr_err_code ndr_err;
6016 struct ldb_message *msg;
6017 struct replPropertyMetaDataBlob *rmd;
6018 struct replPropertyMetaDataBlob omd;
6019 const struct ldb_val *omd_value;
6020 struct replPropertyMetaDataBlob nmd;
6021 struct ldb_val nmd_value;
6022 struct GUID remote_parent_guid;
6025 unsigned int removed_attrs = 0;
6027 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6028 bool isDeleted = false;
6029 bool local_isDeleted = false;
6030 bool remote_isDeleted = false;
6031 bool take_remote_isDeleted = false;
6032 bool sd_updated = false;
6033 bool renamed = false;
6034 bool is_schema_nc = false;
6036 const struct ldb_val *old_rdn, *new_rdn;
6037 struct replmd_private *replmd_private =
6038 talloc_get_type(ldb_module_get_private(ar->module),
6039 struct replmd_private);
6041 time_t t = time(NULL);
6042 unix_to_nt_time(&now, t);
6044 ldb = ldb_module_get_ctx(ar->module);
6045 msg = ar->objs->objects[ar->index_current].msg;
6047 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6049 rmd = ar->objs->objects[ar->index_current].meta_data;
6053 /* find existing meta data */
6054 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6056 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6057 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6058 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6059 nt_status = ndr_map_error2ntstatus(ndr_err);
6060 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6063 if (omd.version != 1) {
6064 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6069 struct GUID_txt_buf guid_txt;
6071 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6072 LDB_CHANGETYPE_MODIFY, msg);
6073 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6076 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6078 ndr_print_struct_string(s,
6079 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6080 "existing replPropertyMetaData",
6082 ndr_print_struct_string(s,
6083 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6084 "incoming replPropertyMetaData",
6087 } else if (DEBUGLVL(4)) {
6088 struct GUID_txt_buf guid_txt;
6090 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6091 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6093 ldb_dn_get_linearized(msg->dn)));
6096 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6097 "isDeleted", false);
6098 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6099 "isDeleted", false);
6102 * Fill in the remote_parent_guid with the GUID or an all-zero
6105 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6106 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6108 remote_parent_guid = GUID_zero();
6112 * To ensure we follow a complex rename chain around, we have
6113 * to confirm that the DN is the same (mostly to confirm the
6114 * RDN) and the parentGUID is the same.
6116 * This ensures we keep things under the correct parent, which
6117 * replmd_replicated_handle_rename() will do.
6120 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6121 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6125 * handle renames, even just by case that come in over
6126 * DRS. Changes in the parent DN don't hit us here,
6127 * because the search for a parent will clean up those
6130 * We also have already filtered out the case where
6131 * the peer has an older name to what we have (see
6132 * replmd_replicated_apply_search_callback())
6134 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6137 if (ret != LDB_SUCCESS) {
6138 ldb_debug(ldb, LDB_DEBUG_FATAL,
6139 "replmd_replicated_request rename %s => %s failed - %s\n",
6140 ldb_dn_get_linearized(ar->search_msg->dn),
6141 ldb_dn_get_linearized(msg->dn),
6142 ldb_errstring(ldb));
6143 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6146 if (renamed == true) {
6148 * Set the callback to one that will fix up the name
6149 * metadata on the new conflict DN
6151 callback = replmd_op_name_modify_callback;
6156 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6157 nmd.ctr.ctr1.array = talloc_array(ar,
6158 struct replPropertyMetaData1,
6159 nmd.ctr.ctr1.count);
6160 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6162 /* first copy the old meta data */
6163 for (i=0; i < omd.ctr.ctr1.count; i++) {
6164 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6169 /* now merge in the new meta data */
6170 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6173 for (j=0; j < ni; j++) {
6176 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6180 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6181 ar->objs->dsdb_repl_flags,
6182 &nmd.ctr.ctr1.array[j],
6183 &rmd->ctr.ctr1.array[i]);
6185 /* replace the entry */
6186 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6187 if (ar->seq_num == 0) {
6188 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6189 if (ret != LDB_SUCCESS) {
6190 return replmd_replicated_request_error(ar, ret);
6193 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6194 switch (nmd.ctr.ctr1.array[j].attid) {
6195 case DRSUAPI_ATTID_ntSecurityDescriptor:
6198 case DRSUAPI_ATTID_isDeleted:
6199 take_remote_isDeleted = true;
6208 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6209 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6210 msg->elements[i-removed_attrs].name,
6211 ldb_dn_get_linearized(msg->dn),
6212 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6215 /* we don't want to apply this change so remove the attribute */
6216 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6223 if (found) continue;
6225 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6226 if (ar->seq_num == 0) {
6227 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6228 if (ret != LDB_SUCCESS) {
6229 return replmd_replicated_request_error(ar, ret);
6232 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6233 switch (nmd.ctr.ctr1.array[ni].attid) {
6234 case DRSUAPI_ATTID_ntSecurityDescriptor:
6237 case DRSUAPI_ATTID_isDeleted:
6238 take_remote_isDeleted = true;
6247 * finally correct the size of the meta_data array
6249 nmd.ctr.ctr1.count = ni;
6251 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6252 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6255 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6256 &nmd, ar, now, is_schema_nc,
6258 if (ret != LDB_SUCCESS) {
6259 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6260 return replmd_replicated_request_error(ar, ret);
6264 * sort the new meta data array
6266 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6267 if (ret != LDB_SUCCESS) {
6268 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6273 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6276 * This also controls SD propagation below
6278 if (take_remote_isDeleted) {
6279 isDeleted = remote_isDeleted;
6281 isDeleted = local_isDeleted;
6284 ar->isDeleted = isDeleted;
6287 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6289 if (msg->num_elements == 0) {
6290 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6293 return replmd_replicated_apply_isDeleted(ar);
6296 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6297 ar->index_current, msg->num_elements);
6303 if (sd_updated && !isDeleted) {
6304 ret = dsdb_module_schedule_sd_propagation(ar->module,
6305 ar->objs->partition_dn,
6307 if (ret != LDB_SUCCESS) {
6308 return ldb_operr(ldb);
6312 /* create the meta data value */
6313 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6314 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6315 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6316 nt_status = ndr_map_error2ntstatus(ndr_err);
6317 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6321 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6322 * and replPopertyMetaData attributes
6324 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6325 if (ret != LDB_SUCCESS) {
6326 return replmd_replicated_request_error(ar, ret);
6328 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6329 if (ret != LDB_SUCCESS) {
6330 return replmd_replicated_request_error(ar, ret);
6332 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6333 if (ret != LDB_SUCCESS) {
6334 return replmd_replicated_request_error(ar, ret);
6337 replmd_ldb_message_sort(msg, ar->schema);
6339 /* we want to replace the old values */
6340 for (i=0; i < msg->num_elements; i++) {
6341 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6342 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6343 if (msg->elements[i].num_values == 0) {
6344 ldb_asprintf_errstring(ldb, __location__
6345 ": objectClass removed on %s, aborting replication\n",
6346 ldb_dn_get_linearized(msg->dn));
6347 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6353 struct GUID_txt_buf guid_txt;
6355 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6356 LDB_CHANGETYPE_MODIFY,
6358 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6359 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6363 } else if (DEBUGLVL(4)) {
6364 struct GUID_txt_buf guid_txt;
6366 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6367 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6369 ldb_dn_get_linearized(msg->dn)));
6372 ret = ldb_build_mod_req(&change_req,
6380 LDB_REQ_SET_LOCATION(change_req);
6381 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6383 /* current partition control needed by "repmd_op_callback" */
6384 ret = ldb_request_add_control(change_req,
6385 DSDB_CONTROL_CURRENT_PARTITION_OID,
6387 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6389 return ldb_next_request(ar->module, change_req);
6392 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6393 struct ldb_reply *ares)
6395 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6396 struct replmd_replicated_request);
6400 return ldb_module_done(ar->req, NULL, NULL,
6401 LDB_ERR_OPERATIONS_ERROR);
6403 if (ares->error != LDB_SUCCESS &&
6404 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6405 return ldb_module_done(ar->req, ares->controls,
6406 ares->response, ares->error);
6409 switch (ares->type) {
6410 case LDB_REPLY_ENTRY:
6411 ar->search_msg = talloc_steal(ar, ares->message);
6414 case LDB_REPLY_REFERRAL:
6415 /* we ignore referrals */
6418 case LDB_REPLY_DONE:
6420 struct replPropertyMetaData1 *md_remote;
6421 struct replPropertyMetaData1 *md_local;
6423 struct replPropertyMetaDataBlob omd;
6424 const struct ldb_val *omd_value;
6425 struct replPropertyMetaDataBlob *rmd;
6426 struct ldb_message *msg;
6428 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6429 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6432 * This is the ADD case, find the appropriate parent,
6433 * as this object doesn't exist locally:
6435 if (ar->search_msg == NULL) {
6436 ret = replmd_replicated_apply_search_for_parent(ar);
6437 if (ret != LDB_SUCCESS) {
6438 return ldb_module_done(ar->req, NULL, NULL, ret);
6445 * Otherwise, in the MERGE case, work out if we are
6446 * attempting a rename, and if so find the parent the
6447 * newly renamed object wants to belong under (which
6448 * may not be the parent in it's attached string DN
6450 rmd = ar->objs->objects[ar->index_current].meta_data;
6454 /* find existing meta data */
6455 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6457 enum ndr_err_code ndr_err;
6458 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6459 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6461 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6462 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6465 if (omd.version != 1) {
6466 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6470 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6472 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6473 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6474 && GUID_all_zero(&ar->local_parent_guid)) {
6475 DEBUG(0, ("Refusing to replicate new version of %s "
6476 "as local object has an all-zero parentGUID attribute, "
6477 "despite not being an NC root\n",
6478 ldb_dn_get_linearized(ar->search_msg->dn)));
6479 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6483 * now we need to check for double renames. We could have a
6484 * local rename pending which our replication partner hasn't
6485 * received yet. We choose which one wins by looking at the
6486 * attribute stamps on the two objects, the newer one wins.
6488 * This also simply applies the correct algorithms for
6489 * determining if a change was made to name at all, or
6490 * if the object has just been renamed under the same
6493 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6494 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6496 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6497 ldb_dn_get_linearized(ar->search_msg->dn)));
6498 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6502 * if there is no name attribute given then we have to assume the
6503 * object we've received has the older name
6505 if (replmd_replPropertyMetaData1_new_should_be_taken(
6506 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6507 md_local, md_remote)) {
6508 struct GUID_txt_buf p_guid_local;
6509 struct GUID_txt_buf p_guid_remote;
6510 msg = ar->objs->objects[ar->index_current].msg;
6512 /* Merge on the existing object, with rename */
6514 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6515 "as incoming object changing to %s under %s\n",
6516 ldb_dn_get_linearized(ar->search_msg->dn),
6517 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6518 ldb_dn_get_linearized(msg->dn),
6519 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6521 ret = replmd_replicated_apply_search_for_parent(ar);
6523 struct GUID_txt_buf p_guid_local;
6524 struct GUID_txt_buf p_guid_remote;
6525 msg = ar->objs->objects[ar->index_current].msg;
6528 * Merge on the existing object, force no
6529 * rename (code below just to explain why in
6533 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6534 ldb_dn_get_linearized(msg->dn)) == 0) {
6535 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6536 GUID_equal(&ar->local_parent_guid,
6537 ar->objs->objects[ar->index_current].parent_guid)
6539 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6540 "despite incoming object changing parent to %s\n",
6541 ldb_dn_get_linearized(ar->search_msg->dn),
6542 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6543 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6547 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6548 " and rejecting older rename to %s under %s\n",
6549 ldb_dn_get_linearized(ar->search_msg->dn),
6550 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6551 ldb_dn_get_linearized(msg->dn),
6552 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6556 * This assignment ensures that the strcmp()
6557 * and GUID_equal() calls in
6558 * replmd_replicated_apply_merge() avoids the
6561 ar->objs->objects[ar->index_current].parent_guid =
6562 &ar->local_parent_guid;
6564 msg->dn = ar->search_msg->dn;
6565 ret = replmd_replicated_apply_merge(ar);
6567 if (ret != LDB_SUCCESS) {
6568 return ldb_module_done(ar->req, NULL, NULL, ret);
6578 * Returns true if we can group together processing this link attribute,
6579 * i.e. it has the same source-object and attribute ID as other links
6580 * already in the group
6582 static bool la_entry_matches_group(struct la_entry *la_entry,
6583 struct la_group *la_group)
6585 struct la_entry *prev = la_group->la_entries;
6587 return (la_entry->la->attid == prev->la->attid &&
6588 GUID_equal(&la_entry->la->identifier->guid,
6589 &prev->la->identifier->guid));
6593 * Creates a new la_entry to store replication info for a single
6596 static struct la_entry *
6597 create_la_entry(struct replmd_private *replmd_private,
6598 struct drsuapi_DsReplicaLinkedAttribute *la,
6599 uint32_t dsdb_repl_flags)
6601 struct la_entry *la_entry;
6603 if (replmd_private->la_ctx == NULL) {
6604 replmd_private->la_ctx = talloc_new(replmd_private);
6606 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6607 if (la_entry == NULL) {
6610 la_entry->la = talloc(la_entry,
6611 struct drsuapi_DsReplicaLinkedAttribute);
6612 if (la_entry->la == NULL) {
6613 talloc_free(la_entry);
6616 *la_entry->la = *la;
6617 la_entry->dsdb_repl_flags = dsdb_repl_flags;
6620 * we need to steal the non-scalars so they stay
6621 * around until the end of the transaction
6623 talloc_steal(la_entry->la, la_entry->la->identifier);
6624 talloc_steal(la_entry->la, la_entry->la->value.blob);
6630 * Stores the linked attributes received in the replication chunk - these get
6631 * applied at the end of the transaction. We also check that each linked
6632 * attribute is valid, i.e. source and target objects are known.
6634 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6636 int ret = LDB_SUCCESS;
6638 struct ldb_module *module = ar->module;
6639 struct replmd_private *replmd_private =
6640 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6641 struct la_group *la_group = NULL;
6642 struct ldb_context *ldb;
6643 TALLOC_CTX *tmp_ctx = NULL;
6644 struct ldb_message *src_msg = NULL;
6645 const struct dsdb_attribute *attr = NULL;
6647 ldb = ldb_module_get_ctx(module);
6649 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6651 /* save away the linked attributes for the end of the transaction */
6652 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6653 struct la_entry *la_entry;
6656 /* create an entry to store the received link attribute info */
6657 la_entry = create_la_entry(replmd_private,
6658 &ar->objs->linked_attributes[i],
6659 ar->objs->dsdb_repl_flags);
6660 if (la_entry == NULL) {
6662 return LDB_ERR_OPERATIONS_ERROR;
6666 * check if we're still dealing with the same source object
6669 new_srcobj = (la_group == NULL ||
6670 !la_entry_matches_group(la_entry, la_group));
6674 /* get a new mem_ctx to lookup the source object */
6675 TALLOC_FREE(tmp_ctx);
6676 tmp_ctx = talloc_new(ar);
6677 if (tmp_ctx == NULL) {
6679 return LDB_ERR_OPERATIONS_ERROR;
6682 /* verify the link source exists */
6683 ret = replmd_get_la_entry_source(module, la_entry,
6688 * When we fail to find the source object, the error
6689 * code we pass back here is really important. It flags
6690 * back to the callers to retry this request with
6691 * DRSUAPI_DRS_GET_ANC. This case should never happen
6692 * if we're replicating from a Samba DC, but it is
6693 * needed to talk to a Windows DC
6695 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6696 WERROR err = WERR_DS_DRA_MISSING_PARENT;
6697 ret = replmd_replicated_request_werror(ar,
6703 ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6705 if (ret != LDB_SUCCESS) {
6709 /* group the links together by source-object for efficiency */
6711 la_group = talloc_zero(replmd_private->la_ctx,
6713 if (la_group == NULL) {
6715 return LDB_ERR_OPERATIONS_ERROR;
6717 DLIST_ADD(replmd_private->la_list, la_group);
6719 DLIST_ADD(la_group->la_entries, la_entry);
6720 replmd_private->total_links++;
6723 TALLOC_FREE(tmp_ctx);
6727 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6729 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6731 struct ldb_context *ldb;
6735 struct ldb_request *search_req;
6736 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6737 "parentGUID", "instanceType",
6738 "replPropertyMetaData", "nTSecurityDescriptor",
6739 "isDeleted", NULL };
6740 struct GUID_txt_buf guid_str_buf;
6742 if (ar->index_current >= ar->objs->num_objects) {
6745 * Now that we've applied all the objects, check the new linked
6746 * attributes and store them (we apply them in .prepare_commit)
6748 ret = replmd_store_linked_attributes(ar);
6750 if (ret != LDB_SUCCESS) {
6754 /* done applying objects, move on to the next stage */
6755 return replmd_replicated_uptodate_vector(ar);
6758 ldb = ldb_module_get_ctx(ar->module);
6759 ar->search_msg = NULL;
6760 ar->isDeleted = false;
6762 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6765 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6766 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6768 ret = ldb_build_search_req(&search_req,
6771 ar->objs->partition_dn,
6777 replmd_replicated_apply_search_callback,
6779 LDB_REQ_SET_LOCATION(search_req);
6781 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6783 if (ret != LDB_SUCCESS) {
6787 return ldb_next_request(ar->module, search_req);
6791 * This is essentially a wrapper for replmd_replicated_apply_next()
6793 * This is needed to ensure that both codepaths call this handler.
6795 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6797 struct ldb_dn *deleted_objects_dn;
6798 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6799 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6800 &deleted_objects_dn);
6801 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6803 * Do a delete here again, so that if there is
6804 * anything local that conflicts with this
6805 * object being deleted, it is removed. This
6806 * includes links. See MS-DRSR 4.1.10.6.9
6809 * If the object is already deleted, and there
6810 * is no more work required, it doesn't do
6814 /* This has been updated to point to the DN we eventually did the modify on */
6816 struct ldb_request *del_req;
6817 struct ldb_result *res;
6819 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6821 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6825 res = talloc_zero(tmp_ctx, struct ldb_result);
6827 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6828 talloc_free(tmp_ctx);
6832 /* Build a delete request, which hopefully will artually turn into nothing */
6833 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6837 ldb_modify_default_callback,
6839 LDB_REQ_SET_LOCATION(del_req);
6840 if (ret != LDB_SUCCESS) {
6841 talloc_free(tmp_ctx);
6846 * This is the guts of the call, call back
6847 * into our delete code, but setting the
6848 * re_delete flag so we delete anything that
6849 * shouldn't be there on a deleted or recycled
6852 ret = replmd_delete_internals(ar->module, del_req, true);
6853 if (ret == LDB_SUCCESS) {
6854 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6857 talloc_free(tmp_ctx);
6858 if (ret != LDB_SUCCESS) {
6863 ar->index_current++;
6864 return replmd_replicated_apply_next(ar);
6867 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6868 struct ldb_reply *ares)
6870 struct ldb_context *ldb;
6871 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6872 struct replmd_replicated_request);
6873 ldb = ldb_module_get_ctx(ar->module);
6876 return ldb_module_done(ar->req, NULL, NULL,
6877 LDB_ERR_OPERATIONS_ERROR);
6879 if (ares->error != LDB_SUCCESS) {
6880 return ldb_module_done(ar->req, ares->controls,
6881 ares->response, ares->error);
6884 if (ares->type != LDB_REPLY_DONE) {
6885 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6886 return ldb_module_done(ar->req, NULL, NULL,
6887 LDB_ERR_OPERATIONS_ERROR);
6892 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6895 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6897 struct ldb_context *ldb;
6898 struct ldb_request *change_req;
6899 enum ndr_err_code ndr_err;
6900 struct ldb_message *msg;
6901 struct replUpToDateVectorBlob ouv;
6902 const struct ldb_val *ouv_value;
6903 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6904 struct replUpToDateVectorBlob nuv;
6905 struct ldb_val nuv_value;
6906 struct ldb_message_element *nuv_el = NULL;
6907 struct ldb_message_element *orf_el = NULL;
6908 struct repsFromToBlob nrf;
6909 struct ldb_val *nrf_value = NULL;
6910 struct ldb_message_element *nrf_el = NULL;
6914 time_t t = time(NULL);
6917 uint32_t instanceType;
6919 ldb = ldb_module_get_ctx(ar->module);
6920 ruv = ar->objs->uptodateness_vector;
6926 unix_to_nt_time(&now, t);
6928 if (ar->search_msg == NULL) {
6929 /* this happens for a REPL_OBJ call where we are
6930 creating the target object by replicating it. The
6931 subdomain join code does this for the partition DN
6933 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6934 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6937 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6938 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6939 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6940 ldb_dn_get_linearized(ar->search_msg->dn)));
6941 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6945 * first create the new replUpToDateVector
6947 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6949 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6950 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6951 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6952 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6953 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6956 if (ouv.version != 2) {
6957 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6962 * the new uptodateness vector will at least
6963 * contain 1 entry, one for the source_dsa
6965 * plus optional values from our old vector and the one from the source_dsa
6967 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6968 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6969 nuv.ctr.ctr2.cursors = talloc_array(ar,
6970 struct drsuapi_DsReplicaCursor2,
6971 nuv.ctr.ctr2.count);
6972 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6974 /* first copy the old vector */
6975 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6976 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6980 /* merge in the source_dsa vector is available */
6981 for (i=0; (ruv && i < ruv->count); i++) {
6984 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6985 &ar->our_invocation_id)) {
6989 for (j=0; j < ni; j++) {
6990 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6991 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6997 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6998 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7003 if (found) continue;
7005 /* if it's not there yet, add it */
7006 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7011 * finally correct the size of the cursors array
7013 nuv.ctr.ctr2.count = ni;
7018 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7021 * create the change ldb_message
7023 msg = ldb_msg_new(ar);
7024 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7025 msg->dn = ar->search_msg->dn;
7027 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7028 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7029 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7030 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7031 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7033 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7034 if (ret != LDB_SUCCESS) {
7035 return replmd_replicated_request_error(ar, ret);
7037 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7040 * now create the new repsFrom value from the given repsFromTo1 structure
7044 nrf.ctr.ctr1 = *ar->objs->source_dsa;
7045 nrf.ctr.ctr1.last_attempt = now;
7046 nrf.ctr.ctr1.last_success = now;
7047 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7050 * first see if we already have a repsFrom value for the current source dsa
7051 * if so we'll later replace this value
7053 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7055 for (i=0; i < orf_el->num_values; i++) {
7056 struct repsFromToBlob *trf;
7058 trf = talloc(ar, struct repsFromToBlob);
7059 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7061 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7062 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7063 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7064 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7065 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7068 if (trf->version != 1) {
7069 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7073 * we compare the source dsa objectGUID not the invocation_id
7074 * because we want only one repsFrom value per source dsa
7075 * and when the invocation_id of the source dsa has changed we don't need
7076 * the old repsFrom with the old invocation_id
7078 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7079 &ar->objs->source_dsa->source_dsa_obj_guid)) {
7085 nrf_value = &orf_el->values[i];
7090 * copy over all old values to the new ldb_message
7092 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7093 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7098 * if we haven't found an old repsFrom value for the current source dsa
7099 * we'll add a new value
7102 struct ldb_val zero_value;
7103 ZERO_STRUCT(zero_value);
7104 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7105 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7107 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7110 /* we now fill the value which is already attached to ldb_message */
7111 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7113 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7114 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7115 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7116 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7120 * the ldb_message_element for the attribute, has all the old values and the new one
7121 * so we'll replace the whole attribute with all values
7123 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7125 if (CHECK_DEBUGLVL(4)) {
7126 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7127 LDB_CHANGETYPE_MODIFY,
7129 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7133 /* prepare the ldb_modify() request */
7134 ret = ldb_build_mod_req(&change_req,
7140 replmd_replicated_uptodate_modify_callback,
7142 LDB_REQ_SET_LOCATION(change_req);
7143 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7145 return ldb_next_request(ar->module, change_req);
7148 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7149 struct ldb_reply *ares)
7151 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7152 struct replmd_replicated_request);
7156 return ldb_module_done(ar->req, NULL, NULL,
7157 LDB_ERR_OPERATIONS_ERROR);
7159 if (ares->error != LDB_SUCCESS &&
7160 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7161 return ldb_module_done(ar->req, ares->controls,
7162 ares->response, ares->error);
7165 switch (ares->type) {
7166 case LDB_REPLY_ENTRY:
7167 ar->search_msg = talloc_steal(ar, ares->message);
7170 case LDB_REPLY_REFERRAL:
7171 /* we ignore referrals */
7174 case LDB_REPLY_DONE:
7175 ret = replmd_replicated_uptodate_modify(ar);
7176 if (ret != LDB_SUCCESS) {
7177 return ldb_module_done(ar->req, NULL, NULL, ret);
7186 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7188 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7189 struct replmd_private *replmd_private =
7190 talloc_get_type_abort(ldb_module_get_private(ar->module),
7191 struct replmd_private);
7193 static const char *attrs[] = {
7194 "replUpToDateVector",
7199 struct ldb_request *search_req;
7201 ar->search_msg = NULL;
7204 * Let the caller know that we did an originating updates
7206 ar->objs->originating_updates = replmd_private->originating_updates;
7208 ret = ldb_build_search_req(&search_req,
7211 ar->objs->partition_dn,
7217 replmd_replicated_uptodate_search_callback,
7219 LDB_REQ_SET_LOCATION(search_req);
7220 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7222 return ldb_next_request(ar->module, search_req);
7227 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7229 struct ldb_context *ldb;
7230 struct dsdb_extended_replicated_objects *objs;
7231 struct replmd_replicated_request *ar;
7232 struct ldb_control **ctrls;
7235 ldb = ldb_module_get_ctx(module);
7237 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7239 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7241 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7242 return LDB_ERR_PROTOCOL_ERROR;
7245 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7246 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7247 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7248 return LDB_ERR_PROTOCOL_ERROR;
7251 ar = replmd_ctx_init(module, req);
7253 return LDB_ERR_OPERATIONS_ERROR;
7255 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7256 ar->apply_mode = true;
7258 ar->schema = dsdb_get_schema(ldb, ar);
7260 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7262 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7263 return LDB_ERR_CONSTRAINT_VIOLATION;
7266 ctrls = req->controls;
7268 if (req->controls) {
7269 req->controls = talloc_memdup(ar, req->controls,
7270 talloc_get_size(req->controls));
7271 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7274 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7275 if (ret != LDB_SUCCESS) {
7279 /* If this change contained linked attributes in the body
7280 * (rather than in the links section) we need to update
7281 * backlinks in linked_attributes */
7282 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7283 if (ret != LDB_SUCCESS) {
7287 ar->controls = req->controls;
7288 req->controls = ctrls;
7290 return replmd_replicated_apply_next(ar);
7294 * Checks how to handle an missing target - either we need to fail the
7295 * replication and retry with GET_TGT, ignore the link and continue, or try to
7296 * add a partial link to an unknown target.
7298 static int replmd_allow_missing_target(struct ldb_module *module,
7299 TALLOC_CTX *mem_ctx,
7300 struct ldb_dn *target_dn,
7301 struct ldb_dn *source_dn,
7304 uint32_t dsdb_repl_flags,
7306 const char * missing_str)
7308 struct ldb_context *ldb = ldb_module_get_ctx(module);
7312 * we may not be able to resolve link targets properly when
7313 * dealing with subsets of objects, e.g. the source is a
7314 * critical object and the target isn't
7317 * When we implement Trusted Domains we need to consider
7318 * whether they get treated as an incomplete replica here or not
7320 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7323 * Ignore the link. We don't increase the highwater-mark in
7324 * the object subset cases, so subsequent replications should
7325 * resolve any missing links
7327 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7328 ldb_dn_get_linearized(target_dn),
7329 ldb_dn_get_linearized(source_dn)));
7330 *ignore_link = true;
7334 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7337 * target should already be up-to-date so there's no point in
7338 * retrying. This could be due to bad timing, or if a target
7339 * on a one-way link was deleted. We ignore the link rather
7340 * than failing the replication cycle completely
7342 *ignore_link = true;
7343 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7344 ldb_dn_get_linearized(target_dn), missing_str,
7345 ldb_dn_get_linearized(source_dn));
7349 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7353 if (is_in_same_nc) {
7354 /* fail the replication and retry with GET_TGT */
7355 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7357 ldb_dn_get_linearized(target_dn),
7358 GUID_string(mem_ctx, guid),
7359 ldb_dn_get_linearized(source_dn));
7360 return LDB_ERR_NO_SUCH_OBJECT;
7364 * The target of the cross-partition link is missing. Continue
7365 * and try to at least add the forward-link. This isn't great,
7366 * but a partial link can be fixed by dbcheck, so it's better
7367 * than dropping the link completely.
7369 *ignore_link = false;
7371 if (is_obj_commit) {
7374 * Only log this when we're actually committing the objects.
7375 * This avoids spurious logs, i.e. if we're just verifying the
7376 * received link during a join.
7378 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7379 missing_str, ldb_dn_get_linearized(target_dn),
7380 ldb_dn_get_linearized(source_dn));
7387 * Checks that the target object for a linked attribute exists.
7388 * @param guid returns the target object's GUID (is returned)if it exists)
7389 * @param ignore_link set to true if the linked attribute should be ignored
7390 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7392 static int replmd_check_target_exists(struct ldb_module *module,
7393 struct dsdb_dn *dsdb_dn,
7394 struct la_entry *la_entry,
7395 struct ldb_dn *source_dn,
7400 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7401 struct ldb_context *ldb = ldb_module_get_ctx(module);
7402 struct ldb_result *target_res;
7403 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7404 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7407 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7408 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7410 *ignore_link = false;
7411 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7413 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7416 * This strange behaviour (allowing a NULL/missing
7417 * GUID) originally comes from:
7419 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7420 * Author: Andrew Tridgell <tridge@samba.org>
7421 * Date: Mon Dec 21 21:21:55 2009 +1100
7423 * s4-drs: cope better with NULL GUIDS from DRS
7425 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7426 * need to match by DN if possible when seeing if we should update an
7429 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7431 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7433 DSDB_FLAG_NEXT_MODULE |
7434 DSDB_SEARCH_SHOW_RECYCLED |
7435 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7436 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7438 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7439 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7441 ldb_dn_get_linearized(dsdb_dn->dn),
7442 ldb_dn_get_linearized(source_dn));
7443 talloc_free(tmp_ctx);
7444 return LDB_ERR_OPERATIONS_ERROR;
7446 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7447 NULL, LDB_SCOPE_SUBTREE,
7449 DSDB_FLAG_NEXT_MODULE |
7450 DSDB_SEARCH_SHOW_RECYCLED |
7451 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7452 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7455 GUID_string(tmp_ctx, guid));
7458 if (ret != LDB_SUCCESS) {
7459 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7460 GUID_string(tmp_ctx, guid),
7461 ldb_errstring(ldb));
7462 talloc_free(tmp_ctx);
7466 if (target_res->count == 0) {
7469 * target object is unknown. Check whether to ignore the link,
7470 * fail the replication, or add a partial link
7472 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7473 source_dn, is_obj_commit, guid,
7474 la_entry->dsdb_repl_flags,
7475 ignore_link, "Unknown");
7477 } else if (target_res->count != 1) {
7478 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7479 GUID_string(tmp_ctx, guid));
7480 ret = LDB_ERR_OPERATIONS_ERROR;
7482 struct ldb_message *target_msg = target_res->msgs[0];
7484 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7486 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7487 replmd_deletion_state(module, target_msg,
7488 &target_deletion_state, NULL);
7491 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7492 * ProcessLinkValue(). Link updates should not be sent for
7493 * recycled and tombstone objects (deleting the links should
7494 * happen when we delete the object). This probably means our
7495 * copy of the target object isn't up to date.
7497 if (target_deletion_state >= OBJECT_RECYCLED) {
7500 * target object is deleted. Check whether to ignore the
7501 * link, fail the replication, or add a partial link
7503 ret = replmd_allow_missing_target(module, tmp_ctx,
7504 dsdb_dn->dn, source_dn,
7505 is_obj_commit, guid,
7506 la_entry->dsdb_repl_flags,
7507 ignore_link, "Deleted");
7511 talloc_free(tmp_ctx);
7516 * Extracts the key details about the source object for a
7517 * linked-attribute entry.
7518 * This returns the following details:
7519 * @param ret_attr the schema details for the linked attribute
7520 * @param source_msg the search result for the source object
7522 static int replmd_get_la_entry_source(struct ldb_module *module,
7523 struct la_entry *la_entry,
7524 TALLOC_CTX *mem_ctx,
7525 const struct dsdb_attribute **ret_attr,
7526 struct ldb_message **source_msg)
7528 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7529 struct ldb_context *ldb = ldb_module_get_ctx(module);
7530 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7532 const struct dsdb_attribute *attr;
7533 struct ldb_result *res;
7534 const char *attrs[4];
7537 linked_attributes[0]:
7538 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7540 identifier: struct drsuapi_DsReplicaObjectIdentifier
7541 __ndr_size : 0x0000003a (58)
7542 __ndr_size_sid : 0x00000000 (0)
7543 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7545 __ndr_size_dn : 0x00000000 (0)
7547 attid : DRSUAPI_ATTID_member (0x1F)
7548 value: struct drsuapi_DsAttributeValue
7549 __ndr_size : 0x0000007e (126)
7551 blob : DATA_BLOB length=126
7552 flags : 0x00000001 (1)
7553 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7554 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7555 meta_data: struct drsuapi_DsReplicaMetaData
7556 version : 0x00000015 (21)
7557 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7558 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7559 originating_usn : 0x000000000001e19c (123292)
7561 (for cases where the link is to a normal DN)
7562 &target: struct drsuapi_DsReplicaObjectIdentifier3
7563 __ndr_size : 0x0000007e (126)
7564 __ndr_size_sid : 0x0000001c (28)
7565 guid : 7639e594-db75-4086-b0d4-67890ae46031
7566 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7567 __ndr_size_dn : 0x00000022 (34)
7568 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7571 /* find the attribute being modified */
7572 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7574 struct GUID_txt_buf guid_str;
7575 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7577 GUID_buf_string(&la->identifier->guid,
7579 return LDB_ERR_OPERATIONS_ERROR;
7583 * All attributes listed here must be dealt with in some way
7584 * by replmd_process_linked_attribute() otherwise in the case
7585 * of isDeleted: FALSE the modify will fail with:
7587 * Failed to apply linked attribute change 'attribute 'isDeleted':
7588 * invalid modify flags on
7589 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7592 * This is becaue isDeleted is a Boolean, so FALSE is a
7593 * legitimate value (set by Samba's deletetest.py)
7595 attrs[0] = attr->lDAPDisplayName;
7596 attrs[1] = "isDeleted";
7597 attrs[2] = "isRecycled";
7601 * get the existing message from the db for the object with
7602 * this GUID, returning attribute being modified. We will then
7603 * use this msg as the basis for a modify call
7605 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7606 DSDB_FLAG_NEXT_MODULE |
7607 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7608 DSDB_SEARCH_SHOW_RECYCLED |
7609 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7610 DSDB_SEARCH_REVEAL_INTERNALS,
7612 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7613 if (ret != LDB_SUCCESS) {
7616 if (res->count != 1) {
7617 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7618 GUID_string(mem_ctx, &la->identifier->guid));
7619 return LDB_ERR_NO_SUCH_OBJECT;
7622 *source_msg = res->msgs[0];
7629 * Verifies the target object is known for a linked attribute
7631 static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7632 TALLOC_CTX *mem_ctx,
7633 struct la_entry *la_entry,
7634 struct ldb_dn *src_dn,
7635 const struct dsdb_attribute *attr)
7637 int ret = LDB_SUCCESS;
7638 struct ldb_module *module = ar->module;
7639 struct dsdb_dn *tgt_dsdb_dn = NULL;
7640 struct GUID guid = GUID_zero();
7643 struct ldb_context *ldb = ldb_module_get_ctx(module);
7644 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7645 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7647 /* the value blob for the attribute holds the target object DN */
7648 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7649 la->value.blob, &tgt_dsdb_dn);
7650 if (!W_ERROR_IS_OK(status)) {
7651 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7652 attr->lDAPDisplayName,
7653 ldb_dn_get_linearized(src_dn),
7654 win_errstr(status));
7655 return LDB_ERR_OPERATIONS_ERROR;
7659 * We can skip the target object checks if we're only syncing critical
7660 * objects, or we know the target is up-to-date. If either case, we
7661 * still continue even if the target doesn't exist
7663 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7664 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7666 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7667 src_dn, false, &guid, &dummy);
7671 * When we fail to find the target object, the error code we pass
7672 * back here is really important. It flags back to the callers to
7673 * retry this request with DRSUAPI_DRS_GET_TGT
7675 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7676 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7683 * Finds the current active Parsed-DN value for a single-valued linked
7684 * attribute, if one exists.
7685 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7686 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7689 static int replmd_get_active_singleval_link(struct ldb_module *module,
7690 TALLOC_CTX *mem_ctx,
7691 struct parsed_dn pdn_list[],
7693 const struct dsdb_attribute *attr,
7694 struct parsed_dn **ret_pdn)
7700 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7702 /* nothing to do for multi-valued linked attributes */
7706 for (i = 0; i < count; i++) {
7707 int ret = LDB_SUCCESS;
7708 struct parsed_dn *pdn = &pdn_list[i];
7710 /* skip any inactive links */
7711 if (dsdb_dn_is_deleted_val(pdn->v)) {
7715 /* we've found an active value for this attribute */
7718 if (pdn->dsdb_dn == NULL) {
7719 struct ldb_context *ldb = ldb_module_get_ctx(module);
7721 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7722 attr->syntax->ldap_oid);
7728 /* no active link found */
7733 * @returns true if the replication linked attribute info is newer than we
7734 * already have in our DB
7735 * @param pdn the existing linked attribute info in our DB
7736 * @param la the new linked attribute info received during replication
7738 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7739 struct drsuapi_DsReplicaLinkedAttribute *la)
7741 /* see if this update is newer than what we have already */
7742 struct GUID invocation_id = GUID_zero();
7743 uint32_t version = 0;
7744 NTTIME change_time = 0;
7748 /* no existing info so update is newer */
7752 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7753 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7754 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7756 return replmd_update_is_newer(&invocation_id,
7757 &la->meta_data.originating_invocation_id,
7759 la->meta_data.version,
7761 la->meta_data.originating_change_time);
7765 * Marks an existing linked attribute value as deleted in the DB
7766 * @param pdn the parsed-DN of the target-value to delete
7768 static int replmd_delete_link_value(struct ldb_module *module,
7769 struct replmd_private *replmd_private,
7770 TALLOC_CTX *mem_ctx,
7771 struct ldb_dn *src_obj_dn,
7772 const struct dsdb_schema *schema,
7773 const struct dsdb_attribute *attr,
7776 struct GUID *target_guid,
7777 struct dsdb_dn *target_dsdb_dn,
7778 struct ldb_val *output_val)
7780 struct ldb_context *ldb = ldb_module_get_ctx(module);
7783 const struct GUID *invocation_id = NULL;
7787 unix_to_nt_time(&now, t);
7789 invocation_id = samdb_ntds_invocation_id(ldb);
7790 if (invocation_id == NULL) {
7791 return LDB_ERR_OPERATIONS_ERROR;
7794 /* if the existing link is active, remove its backlink */
7798 * NOTE WELL: After this we will never (at runtime) be
7799 * able to find this forward link (for instant
7800 * removal) if/when the link target is deleted.
7802 * We have dbcheck rules to cover this and cope otherwise
7803 * by filtering at runtime (i.e. in the extended_dn module).
7805 ret = replmd_add_backlink(module, replmd_private, schema,
7806 src_obj_dn, target_guid, false,
7808 if (ret != LDB_SUCCESS) {
7813 /* mark the existing value as deleted */
7814 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7815 target_dsdb_dn, invocation_id, seq_num,
7816 seq_num, now, true);
7821 * Checks for a conflict in single-valued link attributes, and tries to
7822 * resolve the problem if possible.
7824 * Single-valued links should only ever have one active value. If we already
7825 * have an active link value, and during replication we receive an active link
7826 * value for a different target DN, then we need to resolve this inconsistency
7827 * and determine which value should be active. If the received info is better/
7828 * newer than the existing link attribute, then we need to set our existing
7829 * link as deleted. If the received info is worse/older, then we should continue
7830 * to add it, but set it as an inactive link.
7832 * Note that this is a corner-case that is unlikely to happen (but if it does
7833 * happen, we don't want it to break replication completely).
7835 * @param pdn_being_modified the parsed DN corresponding to the received link
7836 * target (note this is NULL if the link does not already exist in our DB)
7837 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7838 * any existing active or inactive values for the attribute in our DB.
7839 * @param dsdb_dn the target DN for the received link attribute
7840 * @param add_as_inactive gets set to true if the received link is worse than
7841 * the existing link - it should still be added, but as an inactive link.
7843 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7844 struct replmd_private *replmd_private,
7845 TALLOC_CTX *mem_ctx,
7846 struct ldb_dn *src_obj_dn,
7847 struct drsuapi_DsReplicaLinkedAttribute *la,
7848 struct dsdb_dn *dsdb_dn,
7849 struct parsed_dn *pdn_being_modified,
7850 struct parsed_dn *pdn_list,
7851 struct ldb_message_element *old_el,
7852 const struct dsdb_schema *schema,
7853 const struct dsdb_attribute *attr,
7855 bool *add_as_inactive)
7857 struct parsed_dn *active_pdn = NULL;
7858 bool update_is_newer = false;
7862 * check if there's a conflict for single-valued links, i.e. an active
7863 * linked attribute already exists, but it has a different target value
7865 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7866 old_el->num_values, attr,
7869 if (ret != LDB_SUCCESS) {
7874 * If no active value exists (or the received info is for the currently
7875 * active value), then no conflict exists
7877 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7881 DBG_WARNING("Link conflict for %s attribute on %s\n",
7882 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7884 /* Work out how to resolve the conflict based on which info is better */
7885 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7887 if (update_is_newer) {
7888 DBG_WARNING("Using received value %s, over existing target %s\n",
7889 ldb_dn_get_linearized(dsdb_dn->dn),
7890 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7893 * Delete our existing active link. The received info will then
7894 * be added (through normal link processing) as the active value
7896 ret = replmd_delete_link_value(module, replmd_private, old_el,
7897 src_obj_dn, schema, attr,
7898 seq_num, true, &active_pdn->guid,
7899 active_pdn->dsdb_dn,
7902 if (ret != LDB_SUCCESS) {
7906 DBG_WARNING("Using existing target %s, over received value %s\n",
7907 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7908 ldb_dn_get_linearized(dsdb_dn->dn));
7911 * we want to keep our existing active link and add the
7912 * received link as inactive
7914 *add_as_inactive = true;
7921 process one linked attribute structure
7923 static int replmd_process_linked_attribute(struct ldb_module *module,
7924 TALLOC_CTX *mem_ctx,
7925 struct replmd_private *replmd_private,
7926 struct ldb_message *msg,
7927 const struct dsdb_attribute *attr,
7928 struct la_entry *la_entry,
7929 struct ldb_request *parent)
7931 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7932 struct ldb_context *ldb = ldb_module_get_ctx(module);
7933 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7935 struct dsdb_dn *dsdb_dn = NULL;
7936 uint64_t seq_num = 0;
7937 struct ldb_message_element *old_el;
7938 time_t t = time(NULL);
7939 struct parsed_dn *pdn_list, *pdn, *next;
7940 struct GUID guid = GUID_zero();
7941 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7943 struct dsdb_dn *old_dsdb_dn = NULL;
7944 struct ldb_val *val_to_update = NULL;
7945 bool add_as_inactive = false;
7948 /* the value blob for the attribute holds the target object DN */
7949 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7950 la->value.blob, &dsdb_dn);
7951 if (!W_ERROR_IS_OK(status)) {
7952 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7953 attr->lDAPDisplayName,
7954 ldb_dn_get_linearized(msg->dn),
7955 win_errstr(status));
7956 return LDB_ERR_OPERATIONS_ERROR;
7959 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7960 if (old_el == NULL) {
7961 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7962 if (ret != LDB_SUCCESS) {
7963 ldb_module_oom(module);
7964 return LDB_ERR_OPERATIONS_ERROR;
7967 old_el->flags = LDB_FLAG_MOD_REPLACE;
7970 /* parse the existing links */
7971 ret = get_parsed_dns_trusted(module, replmd_private, mem_ctx, old_el, &pdn_list,
7972 attr->syntax->ldap_oid, parent);
7974 if (ret != LDB_SUCCESS) {
7978 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7979 true, &guid, &ignore_link);
7981 if (ret != LDB_SUCCESS) {
7986 * there are some cases where the target object doesn't exist, but it's
7987 * OK to ignore the linked attribute
7993 /* see if this link already exists */
7994 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7997 dsdb_dn->extra_part, 0,
7999 attr->syntax->ldap_oid,
8001 if (ret != LDB_SUCCESS) {
8005 if (!replmd_link_update_is_newer(pdn, la)) {
8006 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8007 old_el->name, ldb_dn_get_linearized(msg->dn),
8008 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8012 /* get a seq_num for this change */
8013 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8014 if (ret != LDB_SUCCESS) {
8019 * check for single-valued link conflicts, i.e. an active linked
8020 * attribute already exists, but it has a different target value
8023 ret = replmd_check_singleval_la_conflict(module, replmd_private,
8024 mem_ctx, msg->dn, la,
8025 dsdb_dn, pdn, pdn_list,
8026 old_el, schema, attr,
8029 if (ret != LDB_SUCCESS) {
8035 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8037 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8038 /* remove the existing backlink */
8039 ret = replmd_add_backlink(module, replmd_private,
8042 &pdn->guid, false, attr,
8044 if (ret != LDB_SUCCESS) {
8049 val_to_update = pdn->v;
8050 old_dsdb_dn = pdn->dsdb_dn;
8056 * We know where the new one needs to be, from the *next
8057 * pointer into pdn_list.
8060 offset = old_el->num_values;
8062 if (next->dsdb_dn == NULL) {
8063 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8064 attr->syntax->ldap_oid);
8065 if (ret != LDB_SUCCESS) {
8069 offset = next - pdn_list;
8070 if (offset > old_el->num_values) {
8071 return LDB_ERR_OPERATIONS_ERROR;
8075 old_el->values = talloc_realloc(msg->elements, old_el->values,
8076 struct ldb_val, old_el->num_values+1);
8077 if (!old_el->values) {
8078 ldb_module_oom(module);
8079 return LDB_ERR_OPERATIONS_ERROR;
8082 if (offset != old_el->num_values) {
8083 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8084 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8087 old_el->num_values++;
8089 val_to_update = &old_el->values[offset];
8093 /* set the link attribute's value to the info that was received */
8094 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8095 &la->meta_data.originating_invocation_id,
8096 la->meta_data.originating_usn, seq_num,
8097 la->meta_data.originating_change_time,
8098 la->meta_data.version,
8100 if (ret != LDB_SUCCESS) {
8104 if (add_as_inactive) {
8106 /* Set the new link as inactive/deleted to avoid conflicts */
8107 ret = replmd_delete_link_value(module, replmd_private, old_el,
8108 msg->dn, schema, attr, seq_num,
8109 false, &guid, dsdb_dn,
8112 if (ret != LDB_SUCCESS) {
8116 } else if (active) {
8118 /* if the new link is active, then add the new backlink */
8119 ret = replmd_add_backlink(module, replmd_private,
8124 if (ret != LDB_SUCCESS) {
8129 /* we only change whenChanged and uSNChanged if the seq_num
8131 ldb_msg_remove_attr(msg, "whenChanged");
8132 ldb_msg_remove_attr(msg, "uSNChanged");
8133 ret = add_time_element(msg, "whenChanged", t);
8134 if (ret != LDB_SUCCESS) {
8139 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8140 if (ret != LDB_SUCCESS) {
8145 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8146 if (old_el == NULL) {
8147 return ldb_operr(ldb);
8150 ret = dsdb_check_single_valued_link(attr, old_el);
8151 if (ret != LDB_SUCCESS) {
8155 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8160 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8162 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8163 return replmd_extended_replicated_objects(module, req);
8166 return ldb_next_request(module, req);
8171 we hook into the transaction operations to allow us to
8172 perform the linked attribute updates at the end of the whole
8173 transaction. This allows a forward linked attribute to be created
8174 before the object is created. During a vampire, w2k8 sends us linked
8175 attributes before the objects they are part of.
8177 static int replmd_start_transaction(struct ldb_module *module)
8179 /* create our private structure for this transaction */
8180 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8181 struct replmd_private);
8182 replmd_txn_cleanup(replmd_private);
8184 /* free any leftover mod_usn records from cancelled
8186 while (replmd_private->ncs) {
8187 struct nc_entry *e = replmd_private->ncs;
8188 DLIST_REMOVE(replmd_private->ncs, e);
8192 replmd_private->originating_updates = false;
8194 return ldb_next_start_trans(module);
8198 * Processes a group of linked attributes that apply to the same source-object
8201 static int replmd_process_la_group(struct ldb_module *module,
8202 struct replmd_private *replmd_private,
8203 struct la_group *la_group)
8205 struct la_entry *la = NULL;
8206 struct la_entry *prev = NULL;
8208 TALLOC_CTX *tmp_ctx = talloc_new(la_group);
8209 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8210 struct ldb_message *msg = NULL;
8211 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8212 struct ldb_context *ldb = ldb_module_get_ctx(module);
8213 const struct dsdb_attribute *attr = NULL;
8216 * get the attribute being modified and the search result for the
8219 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8222 if (ret != LDB_SUCCESS) {
8227 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8228 * ProcessLinkValue, because link updates are not applied to
8229 * recycled and tombstone objects. We don't have to delete
8230 * any existing link, that should have happened when the
8231 * object deletion was replicated or initiated.
8233 * This needs isDeleted and isRecycled to be included as
8234 * attributes in the search and so in msg if set.
8236 replmd_deletion_state(module, msg, &deletion_state, NULL);
8238 if (deletion_state >= OBJECT_RECYCLED) {
8239 TALLOC_FREE(tmp_ctx);
8244 * Now that we know the deletion_state, remove the extra
8245 * attributes added for that purpose. We need to do this
8246 * otherwise in the case of isDeleted: FALSE the modify will
8249 * Failed to apply linked attribute change 'attribute 'isDeleted':
8250 * invalid modify flags on
8251 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8254 * This is becaue isDeleted is a Boolean, so FALSE is a
8255 * legitimate value (set by Samba's deletetest.py)
8257 ldb_msg_remove_attr(msg, "isDeleted");
8258 ldb_msg_remove_attr(msg, "isRecycled");
8260 /* go through and process the link targets for this source object */
8261 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8262 prev = DLIST_PREV(la);
8263 DLIST_REMOVE(la_group->la_entries, la);
8264 ret = replmd_process_linked_attribute(module, tmp_ctx,
8266 msg, attr, la, NULL);
8267 if (ret != LDB_SUCCESS) {
8268 replmd_txn_cleanup(replmd_private);
8272 if ((++replmd_private->num_processed % 8192) == 0) {
8273 DBG_NOTICE("Processed %u/%u linked attributes\n",
8274 replmd_private->num_processed,
8275 replmd_private->total_links);
8279 /* apply the link changes to the source object */
8280 ret = linked_attr_modify(module, msg, NULL);
8281 if (ret != LDB_SUCCESS) {
8282 ldb_debug(ldb, LDB_DEBUG_WARNING,
8283 "Failed to apply linked attribute change '%s'\n%s\n",
8285 ldb_ldif_message_redacted_string(ldb,
8287 LDB_CHANGETYPE_MODIFY,
8289 TALLOC_FREE(tmp_ctx);
8293 TALLOC_FREE(tmp_ctx);
8298 on prepare commit we loop over our queued la_context structures and
8301 static int replmd_prepare_commit(struct ldb_module *module)
8303 struct replmd_private *replmd_private =
8304 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8305 struct la_group *la_group, *prev;
8308 if (replmd_private->la_list != NULL) {
8309 DBG_NOTICE("Processing linked attributes\n");
8313 * Walk the list of linked attributes from DRS replication.
8315 * We walk backwards, to do the first entry first, as we
8316 * added the entries with DLIST_ADD() which puts them at the
8319 * Links are grouped together so we process links for the same
8320 * source object in one go.
8322 for (la_group = DLIST_TAIL(replmd_private->la_list);
8326 prev = DLIST_PREV(la_group);
8327 DLIST_REMOVE(replmd_private->la_list, la_group);
8328 ret = replmd_process_la_group(module, replmd_private,
8330 if (ret != LDB_SUCCESS) {
8331 replmd_txn_cleanup(replmd_private);
8336 replmd_txn_cleanup(replmd_private);
8338 /* possibly change @REPLCHANGED */
8339 ret = replmd_notify_store(module, NULL);
8340 if (ret != LDB_SUCCESS) {
8344 return ldb_next_prepare_commit(module);
8347 static int replmd_del_transaction(struct ldb_module *module)
8349 struct replmd_private *replmd_private =
8350 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8351 replmd_txn_cleanup(replmd_private);
8353 return ldb_next_del_trans(module);
8357 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8358 .name = "repl_meta_data",
8359 .init_context = replmd_init,
8361 .modify = replmd_modify,
8362 .rename = replmd_rename,
8363 .del = replmd_delete,
8364 .extended = replmd_extended,
8365 .start_transaction = replmd_start_transaction,
8366 .prepare_commit = replmd_prepare_commit,
8367 .del_transaction = replmd_del_transaction,
8370 int ldb_repl_meta_data_module_init(const char *version)
8372 LDB_MODULE_CHECK_VERSION(version);
8373 return ldb_register_module(&ldb_repl_meta_data_module_ops);