4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 Copyright (C) Andrew Tridgell 2005-2009
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "dsdb/common/util.h"
43 #include "../libds/common/flags.h"
44 #include "librpc/gen_ndr/irpc.h"
45 #include "librpc/gen_ndr/ndr_misc.h"
46 #include "librpc/gen_ndr/ndr_drsuapi.h"
47 #include "librpc/gen_ndr/ndr_drsblobs.h"
48 #include "param/param.h"
49 #include "libcli/security/security.h"
50 #include "lib/util/dlinklist.h"
51 #include "dsdb/samdb/ldb_modules/util.h"
52 #include "lib/util/tsort.h"
55 #define DBGC_CLASS DBGC_DRS_REPL
57 /* the RMD_VERSION for linked attributes starts from 1 */
58 #define RMD_VERSION_INITIAL 1
61 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
62 * Deleted Objects Container
64 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
66 struct replmd_private {
68 struct la_entry *la_list;
70 struct nc_entry *prev, *next;
73 uint64_t mod_usn_urgent;
75 struct ldb_dn *schema_dn;
76 bool originating_updates;
81 struct la_entry *next, *prev;
82 struct drsuapi_DsReplicaLinkedAttribute *la;
83 uint32_t dsdb_repl_flags;
86 struct replmd_replicated_request {
87 struct ldb_module *module;
88 struct ldb_request *req;
90 const struct dsdb_schema *schema;
91 struct GUID our_invocation_id;
93 /* the controls we pass down */
94 struct ldb_control **controls;
97 * Backlinks for the replmd_add() case (we want to create
98 * backlinks after creating the user, but before the end of
101 struct la_backlink *la_backlinks;
103 /* details for the mode where we apply a bunch of inbound replication meessages */
105 uint32_t index_current;
106 struct dsdb_extended_replicated_objects *objs;
108 struct ldb_message *search_msg;
109 struct GUID local_parent_guid;
117 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
118 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
119 static int replmd_check_upgrade_links(struct ldb_context *ldb,
120 struct parsed_dn *dns, uint32_t count,
121 struct ldb_message_element *el,
122 const char *ldap_oid);
123 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
124 struct la_entry *la);
125 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
126 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
127 uint64_t usn, uint64_t local_usn, NTTIME nttime,
128 uint32_t version, bool deleted);
130 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
131 struct ldb_context *ldb,
133 const char *rdn_name,
134 const struct ldb_val *rdn_value,
137 enum urgent_situation {
138 REPL_URGENT_ON_CREATE = 1,
139 REPL_URGENT_ON_UPDATE = 2,
140 REPL_URGENT_ON_DELETE = 4
143 enum deletion_state {
144 OBJECT_NOT_DELETED=1,
151 static void replmd_deletion_state(struct ldb_module *module,
152 const struct ldb_message *msg,
153 enum deletion_state *current_state,
154 enum deletion_state *next_state)
157 bool enabled = false;
160 *current_state = OBJECT_REMOVED;
161 if (next_state != NULL) {
162 *next_state = OBJECT_REMOVED;
167 ret = dsdb_recyclebin_enabled(module, &enabled);
168 if (ret != LDB_SUCCESS) {
172 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
174 *current_state = OBJECT_TOMBSTONE;
175 if (next_state != NULL) {
176 *next_state = OBJECT_REMOVED;
181 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
182 *current_state = OBJECT_RECYCLED;
183 if (next_state != NULL) {
184 *next_state = OBJECT_REMOVED;
189 *current_state = OBJECT_DELETED;
190 if (next_state != NULL) {
191 *next_state = OBJECT_RECYCLED;
196 *current_state = OBJECT_NOT_DELETED;
197 if (next_state == NULL) {
202 *next_state = OBJECT_DELETED;
204 *next_state = OBJECT_TOMBSTONE;
208 static const struct {
209 const char *update_name;
210 enum urgent_situation repl_situation;
211 } urgent_objects[] = {
212 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
213 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
214 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
215 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
216 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
217 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
221 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
222 static const char *urgent_attrs[] = {
225 "userAccountControl",
230 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
231 enum urgent_situation situation)
234 for (i=0; urgent_objects[i].update_name; i++) {
236 if ((situation & urgent_objects[i].repl_situation) == 0) {
240 for (j=0; j<objectclass_el->num_values; j++) {
241 const struct ldb_val *v = &objectclass_el->values[j];
242 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
250 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
252 if (ldb_attr_in_list(urgent_attrs, el->name)) {
258 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
261 initialise the module
262 allocate the private structure and build the list
263 of partition DNs for use by replmd_notify()
265 static int replmd_init(struct ldb_module *module)
267 struct replmd_private *replmd_private;
268 struct ldb_context *ldb = ldb_module_get_ctx(module);
269 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
270 struct ldb_dn *samba_dsdb_dn;
271 struct ldb_result *res;
273 TALLOC_CTX *frame = talloc_stackframe();
274 replmd_private = talloc_zero(module, struct replmd_private);
275 if (replmd_private == NULL) {
278 return LDB_ERR_OPERATIONS_ERROR;
280 ldb_module_set_private(module, replmd_private);
282 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
284 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
285 if (!samba_dsdb_dn) {
290 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
291 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
292 if (ret == LDB_SUCCESS) {
293 replmd_private->sorted_links
294 = ldb_msg_check_string_attribute(res->msgs[0],
295 SAMBA_COMPATIBLE_FEATURES_ATTR,
296 SAMBA_SORTED_LINKS_FEATURE);
300 return ldb_next_init(module);
304 cleanup our per-transaction contexts
306 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
308 talloc_free(replmd_private->la_ctx);
309 replmd_private->la_list = NULL;
310 replmd_private->la_ctx = NULL;
316 struct la_backlink *next, *prev;
317 const char *attr_name;
318 struct ldb_dn *forward_dn;
319 struct GUID target_guid;
324 a ldb_modify request operating on modules below the
327 static int linked_attr_modify(struct ldb_module *module,
328 const struct ldb_message *message,
329 struct ldb_request *parent)
331 struct ldb_request *mod_req;
333 struct ldb_context *ldb = ldb_module_get_ctx(module);
334 TALLOC_CTX *tmp_ctx = talloc_new(module);
335 struct ldb_result *res;
337 res = talloc_zero(tmp_ctx, struct ldb_result);
339 talloc_free(tmp_ctx);
340 return ldb_oom(ldb_module_get_ctx(module));
343 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
347 ldb_modify_default_callback,
349 LDB_REQ_SET_LOCATION(mod_req);
350 if (ret != LDB_SUCCESS) {
351 talloc_free(tmp_ctx);
355 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
357 if (ret != LDB_SUCCESS) {
361 /* Run the new request */
362 ret = ldb_next_request(module, mod_req);
364 if (ret == LDB_SUCCESS) {
365 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
368 talloc_free(tmp_ctx);
373 process a backlinks we accumulated during a transaction, adding and
374 deleting the backlinks from the target objects
376 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
378 struct ldb_dn *target_dn, *source_dn;
380 struct ldb_context *ldb = ldb_module_get_ctx(module);
381 struct ldb_message *msg;
382 TALLOC_CTX *frame = talloc_stackframe();
388 - construct ldb_message
389 - either an add or a delete
391 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
392 if (ret != LDB_SUCCESS) {
393 struct GUID_txt_buf guid_str;
394 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
395 GUID_buf_string(&bl->target_guid, &guid_str));
396 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
401 msg = ldb_msg_new(frame);
403 ldb_module_oom(module);
405 return LDB_ERR_OPERATIONS_ERROR;
408 source_dn = ldb_dn_copy(frame, bl->forward_dn);
410 ldb_module_oom(module);
412 return LDB_ERR_OPERATIONS_ERROR;
414 /* Filter down to the attributes we want in the backlink */
415 const char *accept[] = { "GUID", "SID", NULL };
416 ldb_dn_extended_filter(source_dn, accept);
419 /* construct a ldb_message for adding/deleting the backlink */
421 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
423 ldb_module_oom(module);
425 return LDB_ERR_OPERATIONS_ERROR;
427 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
428 if (ret != LDB_SUCCESS) {
432 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
434 /* a backlink should never be single valued. Unfortunately the
435 exchange schema has a attribute
436 msExchBridgeheadedLocalConnectorsDNBL which is single
437 valued and a backlink. We need to cope with that by
438 ignoring the single value flag */
439 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
441 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
442 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
443 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
444 cope with possible corruption where the backlink has
445 already been removed */
446 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
447 ldb_dn_get_linearized(target_dn),
448 ldb_dn_get_linearized(source_dn),
449 ldb_errstring(ldb)));
451 } else if (ret != LDB_SUCCESS) {
452 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
453 bl->active?"add":"remove",
454 ldb_dn_get_linearized(source_dn),
455 ldb_dn_get_linearized(target_dn),
465 add a backlink to the list of backlinks to add/delete in the prepare
468 forward_dn is stolen onto the defereed context
470 static int replmd_defer_add_backlink(struct ldb_module *module,
471 struct replmd_private *replmd_private,
472 const struct dsdb_schema *schema,
473 struct replmd_replicated_request *ac,
474 struct ldb_dn *forward_dn,
475 struct GUID *target_guid, bool active,
476 const struct dsdb_attribute *schema_attr,
477 struct ldb_request *parent)
479 const struct dsdb_attribute *target_attr;
480 struct la_backlink *bl;
482 bl = talloc(ac, struct la_backlink);
484 ldb_module_oom(module);
485 return LDB_ERR_OPERATIONS_ERROR;
488 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
491 * windows 2003 has a broken schema where the
492 * definition of msDS-IsDomainFor is missing (which is
493 * supposed to be the backlink of the
494 * msDS-HasDomainNCs attribute
499 bl->attr_name = target_attr->lDAPDisplayName;
500 bl->forward_dn = talloc_steal(bl, forward_dn);
501 bl->target_guid = *target_guid;
504 DLIST_ADD(ac->la_backlinks, bl);
510 add a backlink to the list of backlinks to add/delete in the prepare
513 static int replmd_add_backlink(struct ldb_module *module,
514 struct replmd_private *replmd_private,
515 const struct dsdb_schema *schema,
516 struct ldb_dn *forward_dn,
517 struct GUID *target_guid, bool active,
518 const struct dsdb_attribute *schema_attr,
519 struct ldb_request *parent)
521 const struct dsdb_attribute *target_attr;
522 struct la_backlink bl;
525 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
528 * windows 2003 has a broken schema where the
529 * definition of msDS-IsDomainFor is missing (which is
530 * supposed to be the backlink of the
531 * msDS-HasDomainNCs attribute
536 bl.attr_name = target_attr->lDAPDisplayName;
537 bl.forward_dn = forward_dn;
538 bl.target_guid = *target_guid;
541 ret = replmd_process_backlink(module, &bl, parent);
547 * Callback for most write operations in this module:
549 * notify the repl task that a object has changed. The notifies are
550 * gathered up in the replmd_private structure then written to the
551 * @REPLCHANGED object in each partition during the prepare_commit
553 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
556 struct replmd_replicated_request *ac =
557 talloc_get_type_abort(req->context, struct replmd_replicated_request);
558 struct replmd_private *replmd_private =
559 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
560 struct nc_entry *modified_partition;
561 struct ldb_control *partition_ctrl;
562 const struct dsdb_control_current_partition *partition;
564 struct ldb_control **controls;
566 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
568 controls = ares->controls;
569 if (ldb_request_get_control(ac->req,
570 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
572 * Remove the current partition control from what we pass up
573 * the chain if it hasn't been requested manually.
575 controls = ldb_controls_except_specified(ares->controls, ares,
579 if (ares->error != LDB_SUCCESS) {
580 struct GUID_txt_buf guid_txt;
581 struct ldb_message *msg = NULL;
584 if (ac->apply_mode == false) {
585 DBG_NOTICE("Originating update failure. Error is: %s\n",
586 ldb_strerror(ares->error));
587 return ldb_module_done(ac->req, controls,
588 ares->response, ares->error);
591 msg = ac->objs->objects[ac->index_current].msg;
593 * Set at DBG_NOTICE as once these start to happe, they
594 * will happen a lot until resolved, due to repeated
595 * replication. The caller will probably print the
596 * ldb error string anyway.
598 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
599 ldb_dn_get_linearized(msg->dn),
600 ldb_strerror(ares->error));
602 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
607 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
608 ac->search_msg == NULL ? "ADD" : "MODIFY",
609 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
613 return ldb_module_done(ac->req, controls,
614 ares->response, ares->error);
617 if (ares->type != LDB_REPLY_DONE) {
618 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
619 return ldb_module_done(ac->req, NULL,
620 NULL, LDB_ERR_OPERATIONS_ERROR);
623 if (ac->apply_mode == false) {
624 struct la_backlink *bl;
626 * process our backlink list after an replmd_add(),
627 * creating and deleting backlinks as necessary (this
628 * code is sync). The other cases are handled inline
631 for (bl=ac->la_backlinks; bl; bl=bl->next) {
632 ret = replmd_process_backlink(ac->module, bl, ac->req);
633 if (ret != LDB_SUCCESS) {
634 return ldb_module_done(ac->req, NULL,
640 if (!partition_ctrl) {
641 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
642 return ldb_module_done(ac->req, NULL,
643 NULL, LDB_ERR_OPERATIONS_ERROR);
646 partition = talloc_get_type_abort(partition_ctrl->data,
647 struct dsdb_control_current_partition);
649 if (ac->seq_num > 0) {
650 for (modified_partition = replmd_private->ncs; modified_partition;
651 modified_partition = modified_partition->next) {
652 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
657 if (modified_partition == NULL) {
658 modified_partition = talloc_zero(replmd_private, struct nc_entry);
659 if (!modified_partition) {
660 ldb_oom(ldb_module_get_ctx(ac->module));
661 return ldb_module_done(ac->req, NULL,
662 NULL, LDB_ERR_OPERATIONS_ERROR);
664 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
665 if (!modified_partition->dn) {
666 ldb_oom(ldb_module_get_ctx(ac->module));
667 return ldb_module_done(ac->req, NULL,
668 NULL, LDB_ERR_OPERATIONS_ERROR);
670 DLIST_ADD(replmd_private->ncs, modified_partition);
673 if (ac->seq_num > modified_partition->mod_usn) {
674 modified_partition->mod_usn = ac->seq_num;
676 modified_partition->mod_usn_urgent = ac->seq_num;
679 if (!ac->apply_mode) {
680 replmd_private->originating_updates = true;
684 if (ac->apply_mode) {
685 ret = replmd_replicated_apply_isDeleted(ac);
686 if (ret != LDB_SUCCESS) {
687 return ldb_module_done(ac->req, NULL, NULL, ret);
691 /* free the partition control container here, for the
692 * common path. Other cases will have it cleaned up
693 * eventually with the ares */
694 talloc_free(partition_ctrl);
695 return ldb_module_done(ac->req, controls,
696 ares->response, LDB_SUCCESS);
702 * update a @REPLCHANGED record in each partition if there have been
703 * any writes of replicated data in the partition
705 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
707 struct replmd_private *replmd_private =
708 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
710 while (replmd_private->ncs) {
712 struct nc_entry *modified_partition = replmd_private->ncs;
714 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
715 modified_partition->mod_usn,
716 modified_partition->mod_usn_urgent, parent);
717 if (ret != LDB_SUCCESS) {
718 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
719 ldb_dn_get_linearized(modified_partition->dn)));
723 if (ldb_dn_compare(modified_partition->dn,
724 replmd_private->schema_dn) == 0) {
725 struct ldb_result *ext_res;
726 ret = dsdb_module_extended(module,
727 replmd_private->schema_dn,
729 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
731 DSDB_FLAG_NEXT_MODULE,
733 if (ret != LDB_SUCCESS) {
736 talloc_free(ext_res);
739 DLIST_REMOVE(replmd_private->ncs, modified_partition);
740 talloc_free(modified_partition);
748 created a replmd_replicated_request context
750 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
751 struct ldb_request *req)
753 struct ldb_context *ldb;
754 struct replmd_replicated_request *ac;
755 const struct GUID *our_invocation_id;
757 ldb = ldb_module_get_ctx(module);
759 ac = talloc_zero(req, struct replmd_replicated_request);
768 ac->schema = dsdb_get_schema(ldb, ac);
770 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
771 "replmd_modify: no dsdb_schema loaded");
772 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
777 /* get our invocationId */
778 our_invocation_id = samdb_ntds_invocation_id(ldb);
779 if (!our_invocation_id) {
780 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
781 "replmd_add: unable to find invocationId\n");
785 ac->our_invocation_id = *our_invocation_id;
791 add a time element to a record
793 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
795 struct ldb_message_element *el;
799 if (ldb_msg_find_element(msg, attr) != NULL) {
803 s = ldb_timestring(msg, t);
805 return LDB_ERR_OPERATIONS_ERROR;
808 ret = ldb_msg_add_string(msg, attr, s);
809 if (ret != LDB_SUCCESS) {
813 el = ldb_msg_find_element(msg, attr);
814 /* always set as replace. This works because on add ops, the flag
816 el->flags = LDB_FLAG_MOD_REPLACE;
822 add a uint64_t element to a record
824 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
825 const char *attr, uint64_t v)
827 struct ldb_message_element *el;
830 if (ldb_msg_find_element(msg, attr) != NULL) {
834 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
835 if (ret != LDB_SUCCESS) {
839 el = ldb_msg_find_element(msg, attr);
840 /* always set as replace. This works because on add ops, the flag
842 el->flags = LDB_FLAG_MOD_REPLACE;
847 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
848 const struct replPropertyMetaData1 *m2,
849 const uint32_t *rdn_attid)
852 * This assignment seems inoccous, but it is critical for the
853 * system, as we need to do the comparisons as a unsigned
854 * quantity, not signed (enums are signed integers)
856 uint32_t attid_1 = m1->attid;
857 uint32_t attid_2 = m2->attid;
859 if (attid_1 == attid_2) {
864 * See above regarding this being an unsigned comparison.
865 * Otherwise when the high bit is set on non-standard
866 * attributes, they would end up first, before objectClass
869 return attid_1 > attid_2 ? 1 : -1;
872 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
873 struct replPropertyMetaDataCtr1 *ctr1,
876 if (ctr1->count == 0) {
877 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
878 "No elements found in replPropertyMetaData for %s!\n",
879 ldb_dn_get_linearized(dn));
880 return LDB_ERR_CONSTRAINT_VIOLATION;
883 /* the objectClass attribute is value 0x00000000, so must be first */
884 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
885 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
886 "No objectClass found in replPropertyMetaData for %s!\n",
887 ldb_dn_get_linearized(dn));
888 return LDB_ERR_OBJECT_CLASS_VIOLATION;
894 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
895 struct replPropertyMetaDataCtr1 *ctr1,
898 /* Note this is O(n^2) for the almost-sorted case, which this is */
899 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
900 replmd_replPropertyMetaData1_attid_sort);
901 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
904 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
905 const struct ldb_message_element *e2,
906 const struct dsdb_schema *schema)
908 const struct dsdb_attribute *a1;
909 const struct dsdb_attribute *a2;
912 * TODO: make this faster by caching the dsdb_attribute pointer
913 * on the ldb_messag_element
916 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
917 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
920 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
924 return strcasecmp(e1->name, e2->name);
926 if (a1->attributeID_id == a2->attributeID_id) {
929 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
932 static void replmd_ldb_message_sort(struct ldb_message *msg,
933 const struct dsdb_schema *schema)
935 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
938 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
939 const struct GUID *invocation_id,
940 uint64_t local_usn, NTTIME nttime);
942 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
944 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
945 struct ldb_message_element *el, struct parsed_dn **pdn,
946 const char *ldap_oid, struct ldb_request *parent);
948 static int check_parsed_dn_duplicates(struct ldb_module *module,
949 struct ldb_message_element *el,
950 struct parsed_dn *pdn);
953 fix up linked attributes in replmd_add.
954 This involves setting up the right meta-data in extended DN
955 components, and creating backlinks to the object
957 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
958 struct replmd_private *replmd_private,
959 struct ldb_message_element *el,
960 struct replmd_replicated_request *ac,
962 struct ldb_dn *forward_dn,
963 const struct dsdb_attribute *sa,
964 struct ldb_request *parent)
967 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
968 struct ldb_context *ldb = ldb_module_get_ctx(module);
969 struct parsed_dn *pdn;
970 /* We will take a reference to the schema in replmd_add_backlink */
971 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
972 struct ldb_val *new_values = NULL;
975 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
976 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
978 ldb_asprintf_errstring(ldb,
979 "Attribute %s is single valued but "
980 "more than one value has been supplied",
982 talloc_free(tmp_ctx);
983 return LDB_ERR_CONSTRAINT_VIOLATION;
986 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
987 sa->syntax->ldap_oid, parent);
988 if (ret != LDB_SUCCESS) {
989 talloc_free(tmp_ctx);
993 ret = check_parsed_dn_duplicates(module, el, pdn);
994 if (ret != LDB_SUCCESS) {
995 talloc_free(tmp_ctx);
999 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1000 if (new_values == NULL) {
1001 ldb_module_oom(module);
1002 talloc_free(tmp_ctx);
1003 return LDB_ERR_OPERATIONS_ERROR;
1006 for (i = 0; i < el->num_values; i++) {
1007 struct parsed_dn *p = &pdn[i];
1008 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1009 &ac->our_invocation_id,
1011 if (ret != LDB_SUCCESS) {
1012 talloc_free(tmp_ctx);
1016 ret = replmd_defer_add_backlink(module, replmd_private,
1018 forward_dn, &p->guid, true, sa,
1020 if (ret != LDB_SUCCESS) {
1021 talloc_free(tmp_ctx);
1025 new_values[i] = *p->v;
1027 el->values = talloc_steal(mem_ctx, new_values);
1029 talloc_free(tmp_ctx);
1033 static int replmd_add_make_extended_dn(struct ldb_request *req,
1034 const DATA_BLOB *guid_blob,
1035 struct ldb_dn **_extended_dn)
1038 const DATA_BLOB *sid_blob;
1039 /* Calculate an extended DN for any linked attributes */
1040 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1042 return LDB_ERR_OPERATIONS_ERROR;
1044 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1045 if (ret != LDB_SUCCESS) {
1049 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1050 if (sid_blob != NULL) {
1051 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1052 if (ret != LDB_SUCCESS) {
1056 *_extended_dn = extended_dn;
1061 intercept add requests
1063 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1065 struct ldb_context *ldb;
1066 struct ldb_control *control;
1067 struct replmd_replicated_request *ac;
1068 enum ndr_err_code ndr_err;
1069 struct ldb_request *down_req;
1070 struct ldb_message *msg;
1071 const DATA_BLOB *guid_blob;
1072 DATA_BLOB guid_blob_stack;
1074 uint8_t guid_data[16];
1075 struct replPropertyMetaDataBlob nmd;
1076 struct ldb_val nmd_value;
1077 struct ldb_dn *extended_dn = NULL;
1080 * The use of a time_t here seems odd, but as the NTTIME
1081 * elements are actually declared as NTTIME_1sec in the IDL,
1082 * getting a higher resolution timestamp is not required.
1084 time_t t = time(NULL);
1089 unsigned int functional_level;
1091 bool allow_add_guid = false;
1092 bool remove_current_guid = false;
1093 bool is_urgent = false;
1094 bool is_schema_nc = false;
1095 struct ldb_message_element *objectclass_el;
1096 struct replmd_private *replmd_private =
1097 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1099 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1100 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1102 allow_add_guid = true;
1105 /* do not manipulate our control entries */
1106 if (ldb_dn_is_special(req->op.add.message->dn)) {
1107 return ldb_next_request(module, req);
1110 ldb = ldb_module_get_ctx(module);
1112 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1114 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1115 if (guid_blob != NULL) {
1116 if (!allow_add_guid) {
1117 ldb_set_errstring(ldb,
1118 "replmd_add: it's not allowed to add an object with objectGUID!");
1119 return LDB_ERR_UNWILLING_TO_PERFORM;
1121 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1122 if (!NT_STATUS_IS_OK(status)) {
1123 ldb_set_errstring(ldb,
1124 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1125 return LDB_ERR_UNWILLING_TO_PERFORM;
1127 /* we remove this attribute as it can be a string and
1128 * will not be treated correctly and then we will re-add
1129 * it later on in the good format */
1130 remove_current_guid = true;
1134 guid = GUID_random();
1136 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1138 /* This can't fail */
1139 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1140 (ndr_push_flags_fn_t)ndr_push_GUID);
1141 guid_blob = &guid_blob_stack;
1144 ac = replmd_ctx_init(module, req);
1146 return ldb_module_oom(module);
1149 functional_level = dsdb_functional_level(ldb);
1151 /* Get a sequence number from the backend */
1152 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1153 if (ret != LDB_SUCCESS) {
1158 /* we have to copy the message as the caller might have it as a const */
1159 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1163 return LDB_ERR_OPERATIONS_ERROR;
1166 /* generated times */
1167 unix_to_nt_time(&now, t);
1168 time_str = ldb_timestring(msg, t);
1172 return LDB_ERR_OPERATIONS_ERROR;
1174 if (remove_current_guid) {
1175 ldb_msg_remove_attr(msg,"objectGUID");
1179 * remove autogenerated attributes
1181 ldb_msg_remove_attr(msg, "whenCreated");
1182 ldb_msg_remove_attr(msg, "whenChanged");
1183 ldb_msg_remove_attr(msg, "uSNCreated");
1184 ldb_msg_remove_attr(msg, "uSNChanged");
1185 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1188 * readd replicated attributes
1190 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1191 if (ret != LDB_SUCCESS) {
1197 /* build the replication meta_data */
1200 nmd.ctr.ctr1.count = msg->num_elements;
1201 nmd.ctr.ctr1.array = talloc_array(msg,
1202 struct replPropertyMetaData1,
1203 nmd.ctr.ctr1.count);
1204 if (!nmd.ctr.ctr1.array) {
1207 return LDB_ERR_OPERATIONS_ERROR;
1210 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1212 for (i=0; i < msg->num_elements;) {
1213 struct ldb_message_element *e = &msg->elements[i];
1214 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1215 const struct dsdb_attribute *sa;
1217 if (e->name[0] == '@') {
1222 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1224 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1225 "replmd_add: attribute '%s' not defined in schema\n",
1228 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1231 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1232 /* if the attribute is not replicated (0x00000001)
1233 * or constructed (0x00000004) it has no metadata
1239 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1240 if (extended_dn == NULL) {
1241 ret = replmd_add_make_extended_dn(req,
1244 if (ret != LDB_SUCCESS) {
1251 * Prepare the context for the backlinks and
1252 * create metadata for the forward links. The
1253 * backlinks are created in
1254 * replmd_op_callback() after the successful
1255 * ADD of the object.
1257 ret = replmd_add_fix_la(module, msg->elements,
1262 if (ret != LDB_SUCCESS) {
1266 /* linked attributes are not stored in
1267 replPropertyMetaData in FL above w2k */
1272 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1274 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1275 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1278 if (rdn_val == NULL) {
1281 return LDB_ERR_OPERATIONS_ERROR;
1284 rdn = (const char*)rdn_val->data;
1285 if (strcmp(rdn, "Deleted Objects") == 0) {
1287 * Set the originating_change_time to 29/12/9999 at 23:59:59
1288 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1290 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1292 m->originating_change_time = now;
1295 m->originating_change_time = now;
1297 m->originating_invocation_id = ac->our_invocation_id;
1298 m->originating_usn = ac->seq_num;
1299 m->local_usn = ac->seq_num;
1302 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1307 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1309 if (e->num_values != 0) {
1314 ldb_msg_remove_element(msg, e);
1317 /* fix meta data count */
1318 nmd.ctr.ctr1.count = ni;
1321 * sort meta data array
1323 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1324 if (ret != LDB_SUCCESS) {
1325 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1330 /* generated NDR encoded values */
1331 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1333 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1334 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1337 return LDB_ERR_OPERATIONS_ERROR;
1341 * add the autogenerated values
1343 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1344 if (ret != LDB_SUCCESS) {
1349 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1350 if (ret != LDB_SUCCESS) {
1355 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1356 if (ret != LDB_SUCCESS) {
1361 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1362 if (ret != LDB_SUCCESS) {
1367 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1368 if (ret != LDB_SUCCESS) {
1375 * sort the attributes by attid before storing the object
1377 replmd_ldb_message_sort(msg, ac->schema);
1380 * Assert that we do have an objectClass
1382 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1383 if (objectclass_el == NULL) {
1384 ldb_asprintf_errstring(ldb, __location__
1385 ": objectClass missing on %s\n",
1386 ldb_dn_get_linearized(msg->dn));
1388 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1390 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1391 REPL_URGENT_ON_CREATE);
1393 ac->is_urgent = is_urgent;
1394 ret = ldb_build_add_req(&down_req, ldb, ac,
1397 ac, replmd_op_callback,
1400 LDB_REQ_SET_LOCATION(down_req);
1401 if (ret != LDB_SUCCESS) {
1406 /* current partition control is needed by "replmd_op_callback" */
1407 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1408 ret = ldb_request_add_control(down_req,
1409 DSDB_CONTROL_CURRENT_PARTITION_OID,
1411 if (ret != LDB_SUCCESS) {
1417 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1418 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1419 if (ret != LDB_SUCCESS) {
1425 /* mark the control done */
1427 control->critical = 0;
1429 /* go on with the call chain */
1430 return ldb_next_request(module, down_req);
1435 * update the replPropertyMetaData for one element
1437 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1438 struct ldb_message *msg,
1439 struct ldb_message_element *el,
1440 struct ldb_message_element *old_el,
1441 struct replPropertyMetaDataBlob *omd,
1442 const struct dsdb_schema *schema,
1444 const struct GUID *our_invocation_id,
1447 bool is_forced_rodc,
1448 struct ldb_request *req)
1451 const struct dsdb_attribute *a;
1452 struct replPropertyMetaData1 *md1;
1453 bool may_skip = false;
1456 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1458 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1459 /* allow this to make it possible for dbcheck
1460 to remove bad attributes */
1464 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1466 return LDB_ERR_OPERATIONS_ERROR;
1469 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1471 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1476 * if the attribute's value haven't changed, and this isn't
1477 * just a delete of everything then return LDB_SUCCESS Unless
1478 * we have the provision control or if the attribute is
1479 * interSiteTopologyGenerator as this page explain:
1480 * http://support.microsoft.com/kb/224815 this attribute is
1481 * periodicaly written by the DC responsible for the intersite
1482 * generation in a given site
1484 * Unchanged could be deleting or replacing an already-gone
1485 * thing with an unconstrained delete/empty replace or a
1486 * replace with the same value, but not an add with the same
1487 * value because that could be about adding a duplicate (which
1488 * is for someone else to error out on).
1490 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1491 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1494 } else if (old_el == NULL && el->num_values == 0) {
1495 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1497 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1500 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1501 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1503 * We intentionally skip the version bump when attempting to
1506 * The control is set by dbcheck and expunge-tombstones which
1507 * both attempt to be non-replicating. Otherwise, making an
1508 * alteration to the replication state would trigger a
1509 * broadcast of all expunged objects.
1514 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1516 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1520 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1521 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1523 * allow this to make it possible for dbcheck
1524 * to rebuild broken metadata
1530 for (i=0; i<omd->ctr.ctr1.count; i++) {
1532 * First check if we find it under the msDS-IntID,
1533 * then check if we find it under the OID and
1536 * This allows the administrator to simply re-write
1537 * the attributes and so restore replication, which is
1538 * likely what they will try to do.
1540 if (attid == omd->ctr.ctr1.array[i].attid) {
1544 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1549 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1550 /* linked attributes are not stored in
1551 replPropertyMetaData in FL above w2k, but we do
1552 raise the seqnum for the object */
1553 if (*seq_num == 0 &&
1554 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1555 return LDB_ERR_OPERATIONS_ERROR;
1560 if (i == omd->ctr.ctr1.count) {
1561 /* we need to add a new one */
1562 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1563 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1564 if (omd->ctr.ctr1.array == NULL) {
1566 return LDB_ERR_OPERATIONS_ERROR;
1568 omd->ctr.ctr1.count++;
1569 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1572 /* Get a new sequence number from the backend. We only do this
1573 * if we have a change that requires a new
1574 * replPropertyMetaData element
1576 if (*seq_num == 0) {
1577 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1578 if (ret != LDB_SUCCESS) {
1579 return LDB_ERR_OPERATIONS_ERROR;
1583 md1 = &omd->ctr.ctr1.array[i];
1587 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1588 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1591 if (rdn_val == NULL) {
1593 return LDB_ERR_OPERATIONS_ERROR;
1596 rdn = (const char*)rdn_val->data;
1597 if (strcmp(rdn, "Deleted Objects") == 0) {
1599 * Set the originating_change_time to 29/12/9999 at 23:59:59
1600 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1602 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1604 md1->originating_change_time = now;
1607 md1->originating_change_time = now;
1609 md1->originating_invocation_id = *our_invocation_id;
1610 md1->originating_usn = *seq_num;
1611 md1->local_usn = *seq_num;
1613 if (is_forced_rodc) {
1614 /* Force version to 0 to be overriden later via replication */
1622 * Bump the replPropertyMetaData version on an attribute, and if it
1623 * has changed (or forced by leaving rdn_old NULL), update the value
1626 * This is important, as calling a modify operation may not change the
1627 * version number if the values appear unchanged, but a rename between
1628 * parents bumps this value.
1631 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1632 struct ldb_message *msg,
1633 const struct ldb_val *rdn_new,
1634 const struct ldb_val *rdn_old,
1635 struct replPropertyMetaDataBlob *omd,
1636 struct replmd_replicated_request *ar,
1639 bool is_forced_rodc)
1641 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1642 const struct dsdb_attribute *rdn_attr =
1643 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1644 const char *attr_name = rdn_attr != NULL ?
1645 rdn_attr->lDAPDisplayName :
1647 struct ldb_message_element new_el = {
1648 .flags = LDB_FLAG_MOD_REPLACE,
1651 .values = discard_const_p(struct ldb_val, rdn_new)
1653 struct ldb_message_element old_el = {
1654 .flags = LDB_FLAG_MOD_REPLACE,
1656 .num_values = rdn_old ? 1 : 0,
1657 .values = discard_const_p(struct ldb_val, rdn_old)
1660 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1661 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1662 if (ret != LDB_SUCCESS) {
1663 return ldb_oom(ldb);
1667 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1668 omd, ar->schema, &ar->seq_num,
1669 &ar->our_invocation_id,
1670 now, is_schema_nc, is_forced_rodc,
1675 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1677 uint32_t count = omd.ctr.ctr1.count;
1680 for (i=0; i < count; i++) {
1681 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1682 if (max < m.local_usn) {
1690 * update the replPropertyMetaData object each time we modify an
1691 * object. This is needed for DRS replication, as the merge on the
1692 * client is based on this object
1694 static int replmd_update_rpmd(struct ldb_module *module,
1695 const struct dsdb_schema *schema,
1696 struct ldb_request *req,
1697 const char * const *rename_attrs,
1698 struct ldb_message *msg, uint64_t *seq_num,
1699 time_t t, bool is_schema_nc,
1700 bool *is_urgent, bool *rodc)
1702 const struct ldb_val *omd_value;
1703 enum ndr_err_code ndr_err;
1704 struct replPropertyMetaDataBlob omd;
1707 const struct GUID *our_invocation_id;
1709 const char * const *attrs = NULL;
1710 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1711 struct ldb_result *res;
1712 struct ldb_context *ldb;
1713 struct ldb_message_element *objectclass_el;
1714 enum urgent_situation situation;
1715 bool rmd_is_provided;
1716 bool rmd_is_just_resorted = false;
1717 const char *not_rename_attrs[4 + msg->num_elements];
1718 bool is_forced_rodc = false;
1721 attrs = rename_attrs;
1723 for (i = 0; i < msg->num_elements; i++) {
1724 not_rename_attrs[i] = msg->elements[i].name;
1726 not_rename_attrs[i] = "replPropertyMetaData";
1727 not_rename_attrs[i+1] = "objectClass";
1728 not_rename_attrs[i+2] = "instanceType";
1729 not_rename_attrs[i+3] = NULL;
1730 attrs = not_rename_attrs;
1733 ldb = ldb_module_get_ctx(module);
1735 ret = samdb_rodc(ldb, rodc);
1736 if (ret != LDB_SUCCESS) {
1737 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1742 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1743 is_forced_rodc = true;
1746 our_invocation_id = samdb_ntds_invocation_id(ldb);
1747 if (!our_invocation_id) {
1748 /* this happens during an initial vampire while
1749 updating the schema */
1750 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1754 unix_to_nt_time(&now, t);
1756 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1757 rmd_is_provided = true;
1758 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1759 rmd_is_just_resorted = true;
1762 rmd_is_provided = false;
1765 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1766 * otherwise we consider we are updating */
1767 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1768 situation = REPL_URGENT_ON_DELETE;
1769 } else if (rename_attrs) {
1770 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1772 situation = REPL_URGENT_ON_UPDATE;
1775 if (rmd_is_provided) {
1776 /* In this case the change_replmetadata control was supplied */
1777 /* We check that it's the only attribute that is provided
1778 * (it's a rare case so it's better to keep the code simplier)
1779 * We also check that the highest local_usn is bigger or the same as
1782 if( msg->num_elements != 1 ||
1783 strncmp(msg->elements[0].name,
1784 "replPropertyMetaData", 20) ) {
1785 DEBUG(0,(__location__ ": changereplmetada control called without "\
1786 "a specified replPropertyMetaData attribute or with others\n"));
1787 return LDB_ERR_OPERATIONS_ERROR;
1789 if (situation != REPL_URGENT_ON_UPDATE) {
1790 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1791 return LDB_ERR_OPERATIONS_ERROR;
1793 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1795 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1796 ldb_dn_get_linearized(msg->dn)));
1797 return LDB_ERR_OPERATIONS_ERROR;
1799 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1800 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1801 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1802 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1803 ldb_dn_get_linearized(msg->dn)));
1804 return LDB_ERR_OPERATIONS_ERROR;
1807 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1808 DSDB_FLAG_NEXT_MODULE |
1809 DSDB_SEARCH_SHOW_RECYCLED |
1810 DSDB_SEARCH_SHOW_EXTENDED_DN |
1811 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1812 DSDB_SEARCH_REVEAL_INTERNALS, req);
1814 if (ret != LDB_SUCCESS) {
1818 if (rmd_is_just_resorted == false) {
1819 *seq_num = find_max_local_usn(omd);
1821 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1824 * The test here now allows for a new
1825 * replPropertyMetaData with no change, if was
1826 * just dbcheck re-sorting the values.
1828 if (*seq_num <= db_seq) {
1829 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1830 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1831 (long long)*seq_num, (long long)db_seq));
1832 return LDB_ERR_OPERATIONS_ERROR;
1837 /* search for the existing replPropertyMetaDataBlob. We need
1838 * to use REVEAL and ask for DNs in storage format to support
1839 * the check for values being the same in
1840 * replmd_update_rpmd_element()
1842 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1843 DSDB_FLAG_NEXT_MODULE |
1844 DSDB_SEARCH_SHOW_RECYCLED |
1845 DSDB_SEARCH_SHOW_EXTENDED_DN |
1846 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1847 DSDB_SEARCH_REVEAL_INTERNALS, req);
1848 if (ret != LDB_SUCCESS) {
1852 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1854 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1855 ldb_dn_get_linearized(msg->dn)));
1856 return LDB_ERR_OPERATIONS_ERROR;
1859 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1860 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1861 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1862 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1863 ldb_dn_get_linearized(msg->dn)));
1864 return LDB_ERR_OPERATIONS_ERROR;
1867 if (omd.version != 1) {
1868 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1869 omd.version, ldb_dn_get_linearized(msg->dn)));
1870 return LDB_ERR_OPERATIONS_ERROR;
1873 for (i=0; i<msg->num_elements;) {
1874 struct ldb_message_element *el = &msg->elements[i];
1875 struct ldb_message_element *old_el;
1877 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1878 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1879 &omd, schema, seq_num,
1884 if (ret != LDB_SUCCESS) {
1888 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1889 *is_urgent = replmd_check_urgent_attribute(el);
1892 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1897 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1899 if (el->num_values != 0) {
1904 ldb_msg_remove_element(msg, el);
1909 * Assert that we have an objectClass attribute - this is major
1910 * corruption if we don't have this!
1912 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1913 if (objectclass_el != NULL) {
1915 * Now check if this objectClass means we need to do urgent replication
1917 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1921 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1922 ldb_asprintf_errstring(ldb, __location__
1923 ": objectClass missing on %s\n",
1924 ldb_dn_get_linearized(msg->dn));
1925 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1929 * replmd_update_rpmd_element has done an update if the
1932 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1933 struct ldb_val *md_value;
1934 struct ldb_message_element *el;
1936 /*if we are RODC and this is a DRSR update then its ok*/
1937 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1938 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1939 && !is_forced_rodc) {
1940 unsigned instanceType;
1943 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1944 return LDB_ERR_REFERRAL;
1947 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1948 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1949 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1950 "cannot change replicated attribute on partial replica");
1954 md_value = talloc(msg, struct ldb_val);
1955 if (md_value == NULL) {
1957 return LDB_ERR_OPERATIONS_ERROR;
1960 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1961 if (ret != LDB_SUCCESS) {
1962 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1966 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1967 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1968 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1969 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1970 ldb_dn_get_linearized(msg->dn)));
1971 return LDB_ERR_OPERATIONS_ERROR;
1974 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1975 if (ret != LDB_SUCCESS) {
1976 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1977 ldb_dn_get_linearized(msg->dn)));
1982 el->values = md_value;
1988 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1990 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
1992 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
1993 &pdn2->dsdb_dn->extra_part);
1999 get a series of message element values as an array of DNs and GUIDs
2000 the result is sorted by GUID
2002 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2003 struct ldb_message_element *el, struct parsed_dn **pdn,
2004 const char *ldap_oid, struct ldb_request *parent)
2007 bool values_are_sorted = true;
2008 struct ldb_context *ldb = ldb_module_get_ctx(module);
2015 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2017 ldb_module_oom(module);
2018 return LDB_ERR_OPERATIONS_ERROR;
2021 for (i=0; i<el->num_values; i++) {
2022 struct ldb_val *v = &el->values[i];
2025 struct parsed_dn *p;
2029 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2030 if (p->dsdb_dn == NULL) {
2031 return LDB_ERR_INVALID_DN_SYNTAX;
2034 dn = p->dsdb_dn->dn;
2036 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2037 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2038 unlikely(GUID_all_zero(&p->guid))) {
2039 /* we got a DN without a GUID - go find the GUID */
2040 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2041 if (ret != LDB_SUCCESS) {
2042 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
2043 ldb_dn_get_linearized(dn));
2044 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2045 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2046 ldb_attr_cmp(el->name, "member") == 0) {
2047 return LDB_ERR_UNWILLING_TO_PERFORM;
2051 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2052 if (ret != LDB_SUCCESS) {
2055 } else if (!NT_STATUS_IS_OK(status)) {
2056 return LDB_ERR_OPERATIONS_ERROR;
2058 if (i > 0 && values_are_sorted) {
2059 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2061 values_are_sorted = false;
2064 /* keep a pointer to the original ldb_val */
2067 if (! values_are_sorted) {
2068 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2074 * Get a series of trusted message element values. The result is sorted by
2075 * GUID, even though the GUIDs might not be known. That works because we trust
2076 * the database to give us the elements like that if the
2077 * replmd_private->sorted_links flag is set.
2079 * We also ensure that the links are in the Functional Level 2003
2080 * linked attributes format.
2082 static int get_parsed_dns_trusted(struct ldb_module *module,
2083 struct replmd_private *replmd_private,
2084 TALLOC_CTX *mem_ctx,
2085 struct ldb_message_element *el,
2086 struct parsed_dn **pdn,
2087 const char *ldap_oid,
2088 struct ldb_request *parent)
2097 if (!replmd_private->sorted_links) {
2098 /* We need to sort the list. This is the slow old path we want
2101 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2103 if (ret != LDB_SUCCESS) {
2107 /* Here we get a list of 'struct parsed_dns' without the parsing */
2108 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2111 ldb_module_oom(module);
2112 return LDB_ERR_OPERATIONS_ERROR;
2115 for (i = 0; i < el->num_values; i++) {
2116 (*pdn)[i].v = &el->values[i];
2121 * This upgrades links to FL2003 style, and sorts the result
2122 * if that was needed.
2124 * TODO: Add a database feature that asserts we have no FL2000
2125 * style links to avoid this check or add a feature that
2126 * uses a similar check to find sorted/unsorted links
2127 * for an on-the-fly upgrade.
2130 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2131 *pdn, el->num_values,
2134 if (ret != LDB_SUCCESS) {
2142 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2143 otherwise an error code. For compatibility the error code differs depending
2144 on whether or not the attribute is "member".
2146 As always, the parsed_dn list is assumed to be sorted.
2148 static int check_parsed_dn_duplicates(struct ldb_module *module,
2149 struct ldb_message_element *el,
2150 struct parsed_dn *pdn)
2153 struct ldb_context *ldb = ldb_module_get_ctx(module);
2155 for (i = 1; i < el->num_values; i++) {
2156 struct parsed_dn *p = &pdn[i];
2157 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2158 ldb_asprintf_errstring(ldb,
2159 "Linked attribute %s has "
2160 "multiple identical values",
2162 if (ldb_attr_cmp(el->name, "member") == 0) {
2163 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2165 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2173 build a new extended DN, including all meta data fields
2175 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2176 RMD_ADDTIME = originating_add_time
2177 RMD_INVOCID = originating_invocation_id
2178 RMD_CHANGETIME = originating_change_time
2179 RMD_ORIGINATING_USN = originating_usn
2180 RMD_LOCAL_USN = local_usn
2181 RMD_VERSION = version
2183 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2184 struct dsdb_dn *dsdb_dn,
2185 const struct GUID *invocation_id,
2186 uint64_t local_usn, NTTIME nttime)
2188 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2189 local_usn, local_usn, nttime,
2190 RMD_VERSION_INITIAL, false);
2193 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2194 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2195 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2199 check if any links need upgrading from w2k format
2201 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2202 struct parsed_dn *dns, uint32_t count,
2203 struct ldb_message_element *el,
2204 const char *ldap_oid)
2207 const struct GUID *invocation_id = NULL;
2208 for (i=0; i<count; i++) {
2212 if (dns[i].dsdb_dn == NULL) {
2213 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2215 if (ret != LDB_SUCCESS) {
2216 return LDB_ERR_INVALID_DN_SYNTAX;
2220 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2221 &version, "RMD_VERSION");
2222 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2224 * We optimistically assume they are all the same; if
2225 * the first one is fixed, they are all fixed.
2227 * If the first one was *not* fixed and we find a
2228 * later one that is, that is an occasion to shout
2234 DEBUG(0, ("Mixed w2k and fixed format "
2235 "linked attributes\n"));
2239 if (invocation_id == NULL) {
2240 invocation_id = samdb_ntds_invocation_id(ldb);
2241 if (invocation_id == NULL) {
2242 return LDB_ERR_OPERATIONS_ERROR;
2247 /* it's an old one that needs upgrading */
2248 ret = replmd_update_la_val(el->values, dns[i].v,
2249 dns[i].dsdb_dn, dns[i].dsdb_dn,
2250 invocation_id, 1, 1, 0, false);
2251 if (ret != LDB_SUCCESS) {
2257 * This sort() is critical for the operation of
2258 * get_parsed_dns_trusted() because callers of this function
2259 * expect a sorted list, and FL2000 style links are not
2260 * sorted. In particular, as well as the upgrade case,
2261 * get_parsed_dns_trusted() is called from
2262 * replmd_delete_remove_link() even in FL2000 mode
2264 * We do not normally pay the cost of the qsort() due to the
2265 * early return in the RMD_VERSION found case.
2267 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2272 Sets the value for a linked attribute, including all meta data fields
2274 see replmd_build_la_val for value names
2276 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2277 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2278 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2279 uint32_t version, bool deleted)
2281 struct ldb_dn *dn = dsdb_dn->dn;
2282 const char *tstring, *usn_string, *flags_string;
2283 struct ldb_val tval;
2285 struct ldb_val usnv, local_usnv;
2286 struct ldb_val vers, flagsv;
2287 const struct ldb_val *old_addtime = NULL;
2290 const char *dnstring;
2292 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2294 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2296 return LDB_ERR_OPERATIONS_ERROR;
2298 tval = data_blob_string_const(tstring);
2300 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2302 return LDB_ERR_OPERATIONS_ERROR;
2304 usnv = data_blob_string_const(usn_string);
2306 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2308 return LDB_ERR_OPERATIONS_ERROR;
2310 local_usnv = data_blob_string_const(usn_string);
2312 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2313 if (!NT_STATUS_IS_OK(status)) {
2314 return LDB_ERR_OPERATIONS_ERROR;
2317 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2318 if (!flags_string) {
2319 return LDB_ERR_OPERATIONS_ERROR;
2321 flagsv = data_blob_string_const(flags_string);
2323 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2324 if (ret != LDB_SUCCESS) return ret;
2326 /* get the ADDTIME from the original */
2327 if (old_dsdb_dn != NULL) {
2328 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2331 if (old_addtime == NULL) {
2332 old_addtime = &tval;
2334 if (dsdb_dn != old_dsdb_dn ||
2335 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2336 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2337 if (ret != LDB_SUCCESS) return ret;
2340 /* use our invocation id */
2341 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2342 if (ret != LDB_SUCCESS) return ret;
2344 /* changetime is the current time */
2345 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2346 if (ret != LDB_SUCCESS) return ret;
2348 /* update the USN */
2349 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2350 if (ret != LDB_SUCCESS) return ret;
2352 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2353 if (ret != LDB_SUCCESS) return ret;
2355 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2356 vers = data_blob_string_const(vstring);
2357 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2358 if (ret != LDB_SUCCESS) return ret;
2360 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2361 if (dnstring == NULL) {
2362 return LDB_ERR_OPERATIONS_ERROR;
2364 *v = data_blob_string_const(dnstring);
2370 * Updates the value for a linked attribute, including all meta data fields
2372 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2373 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2374 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2377 uint32_t old_version;
2378 uint32_t version = RMD_VERSION_INITIAL;
2382 * We're updating the linked attribute locally, so increase the version
2383 * by 1 so that other DCs will see the change when it gets replicated out
2385 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2388 if (NT_STATUS_IS_OK(status)) {
2389 version = old_version + 1;
2392 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2393 usn, local_usn, nttime, version, deleted);
2397 handle adding a linked attribute
2399 static int replmd_modify_la_add(struct ldb_module *module,
2400 struct replmd_private *replmd_private,
2401 const struct dsdb_schema *schema,
2402 struct ldb_message *msg,
2403 struct ldb_message_element *el,
2404 struct ldb_message_element *old_el,
2405 const struct dsdb_attribute *schema_attr,
2408 struct ldb_dn *msg_dn,
2409 struct ldb_request *parent)
2412 struct parsed_dn *dns, *old_dns;
2413 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2415 struct ldb_val *new_values = NULL;
2416 unsigned old_num_values = old_el ? old_el->num_values : 0;
2417 unsigned num_values = 0;
2418 unsigned max_num_values;
2419 const struct GUID *invocation_id;
2420 struct ldb_context *ldb = ldb_module_get_ctx(module);
2422 unix_to_nt_time(&now, t);
2424 invocation_id = samdb_ntds_invocation_id(ldb);
2425 if (!invocation_id) {
2426 talloc_free(tmp_ctx);
2427 return LDB_ERR_OPERATIONS_ERROR;
2430 /* get the DNs to be added, fully parsed.
2432 * We need full parsing because they came off the wire and we don't
2433 * trust them, besides which we need their details to know where to put
2436 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2437 schema_attr->syntax->ldap_oid, parent);
2438 if (ret != LDB_SUCCESS) {
2439 talloc_free(tmp_ctx);
2443 /* get the existing DNs, lazily parsed */
2444 ret = get_parsed_dns_trusted(module, replmd_private,
2445 tmp_ctx, old_el, &old_dns,
2446 schema_attr->syntax->ldap_oid, parent);
2448 if (ret != LDB_SUCCESS) {
2449 talloc_free(tmp_ctx);
2453 max_num_values = old_num_values + el->num_values;
2454 if (max_num_values < old_num_values) {
2455 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2456 "old values: %u, new values: %u, sum: %u",
2457 old_num_values, el->num_values, max_num_values));
2458 talloc_free(tmp_ctx);
2459 return LDB_ERR_OPERATIONS_ERROR;
2462 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2464 if (new_values == NULL) {
2465 ldb_module_oom(module);
2466 talloc_free(tmp_ctx);
2467 return LDB_ERR_OPERATIONS_ERROR;
2471 * For each new value, find where it would go in the list. If there is
2472 * a matching GUID there, we update the existing value; otherwise we
2476 for (i = 0; i < el->num_values; i++) {
2477 struct parsed_dn *exact;
2478 struct parsed_dn *next;
2480 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2483 dns[i].dsdb_dn->extra_part, 0,
2485 schema_attr->syntax->ldap_oid,
2487 if (err != LDB_SUCCESS) {
2488 talloc_free(tmp_ctx);
2492 if (exact != NULL) {
2494 * We are trying to add one that exists, which is only
2495 * allowed if it was previously deleted.
2497 * When we do undelete a link we change it in place.
2498 * It will be copied across into the right spot in due
2502 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2504 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2505 struct GUID_txt_buf guid_str;
2506 ldb_asprintf_errstring(ldb,
2507 "Attribute %s already "
2508 "exists for target GUID %s",
2510 GUID_buf_string(&exact->guid,
2512 talloc_free(tmp_ctx);
2513 /* error codes for 'member' need to be
2515 if (ldb_attr_cmp(el->name, "member") == 0) {
2516 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2518 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2522 ret = replmd_update_la_val(new_values, exact->v,
2525 invocation_id, seq_num,
2526 seq_num, now, false);
2527 if (ret != LDB_SUCCESS) {
2528 talloc_free(tmp_ctx);
2532 ret = replmd_add_backlink(module, replmd_private,
2539 if (ret != LDB_SUCCESS) {
2540 talloc_free(tmp_ctx);
2546 * Here we don't have an exact match.
2548 * If next is NULL, this one goes beyond the end of the
2549 * existing list, so we need to add all of those ones first.
2551 * If next is not NULL, we need to add all the ones before
2555 offset = old_num_values;
2557 /* next should have been parsed, but let's make sure */
2558 if (next->dsdb_dn == NULL) {
2559 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2560 schema_attr->syntax->ldap_oid);
2561 if (ret != LDB_SUCCESS) {
2565 offset = MIN(next - old_dns, old_num_values);
2568 /* put all the old ones before next on the list */
2569 for (; j < offset; j++) {
2570 new_values[num_values] = *old_dns[j].v;
2574 ret = replmd_add_backlink(module, replmd_private,
2579 /* Make the new linked attribute ldb_val. */
2580 ret = replmd_build_la_val(new_values, &new_values[num_values],
2581 dns[i].dsdb_dn, invocation_id,
2583 if (ret != LDB_SUCCESS) {
2584 talloc_free(tmp_ctx);
2588 if (ret != LDB_SUCCESS) {
2589 talloc_free(tmp_ctx);
2593 /* copy the rest of the old ones (if any) */
2594 for (; j < old_num_values; j++) {
2595 new_values[num_values] = *old_dns[j].v;
2599 talloc_steal(msg->elements, new_values);
2600 if (old_el != NULL) {
2601 talloc_steal(msg->elements, old_el->values);
2603 el->values = new_values;
2604 el->num_values = num_values;
2606 talloc_free(tmp_ctx);
2608 /* we now tell the backend to replace all existing values
2609 with the one we have constructed */
2610 el->flags = LDB_FLAG_MOD_REPLACE;
2617 handle deleting all active linked attributes
2619 static int replmd_modify_la_delete(struct ldb_module *module,
2620 struct replmd_private *replmd_private,
2621 const struct dsdb_schema *schema,
2622 struct ldb_message *msg,
2623 struct ldb_message_element *el,
2624 struct ldb_message_element *old_el,
2625 const struct dsdb_attribute *schema_attr,
2628 struct ldb_dn *msg_dn,
2629 struct ldb_request *parent)
2632 struct parsed_dn *dns, *old_dns;
2633 TALLOC_CTX *tmp_ctx = NULL;
2635 struct ldb_context *ldb = ldb_module_get_ctx(module);
2636 struct ldb_control *vanish_links_ctrl = NULL;
2637 bool vanish_links = false;
2638 unsigned int num_to_delete = el->num_values;
2640 const struct GUID *invocation_id;
2643 unix_to_nt_time(&now, t);
2645 invocation_id = samdb_ntds_invocation_id(ldb);
2646 if (!invocation_id) {
2647 return LDB_ERR_OPERATIONS_ERROR;
2650 if (old_el == NULL || old_el->num_values == 0) {
2651 /* there is nothing to delete... */
2652 if (num_to_delete == 0) {
2653 /* and we're deleting nothing, so that's OK */
2656 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2659 tmp_ctx = talloc_new(msg);
2660 if (tmp_ctx == NULL) {
2661 return LDB_ERR_OPERATIONS_ERROR;
2664 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2665 schema_attr->syntax->ldap_oid, parent);
2666 if (ret != LDB_SUCCESS) {
2667 talloc_free(tmp_ctx);
2671 ret = get_parsed_dns_trusted(module, replmd_private,
2672 tmp_ctx, old_el, &old_dns,
2673 schema_attr->syntax->ldap_oid, parent);
2675 if (ret != LDB_SUCCESS) {
2676 talloc_free(tmp_ctx);
2681 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2682 if (vanish_links_ctrl) {
2683 vanish_links = true;
2684 vanish_links_ctrl->critical = false;
2688 /* we empty out el->values here to avoid damage if we return early. */
2693 * If vanish links is set, we are actually removing members of
2694 * old_el->values; otherwise we are just marking them deleted.
2696 * There is a special case when no values are given: we remove them
2697 * all. When we have the vanish_links control we just have to remove
2698 * the backlinks and change our element to replace the existing values
2699 * with the empty list.
2702 if (num_to_delete == 0) {
2703 for (i = 0; i < old_el->num_values; i++) {
2704 struct parsed_dn *p = &old_dns[i];
2705 if (p->dsdb_dn == NULL) {
2706 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2707 schema_attr->syntax->ldap_oid);
2708 if (ret != LDB_SUCCESS) {
2712 ret = replmd_add_backlink(module, replmd_private,
2713 schema, msg_dn, &p->guid,
2716 if (ret != LDB_SUCCESS) {
2717 talloc_free(tmp_ctx);
2724 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2725 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2729 ret = replmd_update_la_val(old_el->values, p->v,
2730 p->dsdb_dn, p->dsdb_dn,
2731 invocation_id, seq_num,
2732 seq_num, now, true);
2733 if (ret != LDB_SUCCESS) {
2734 talloc_free(tmp_ctx);
2740 el->flags = LDB_FLAG_MOD_REPLACE;
2741 talloc_free(tmp_ctx);
2747 for (i = 0; i < num_to_delete; i++) {
2748 struct parsed_dn *p = &dns[i];
2749 struct parsed_dn *exact = NULL;
2750 struct parsed_dn *next = NULL;
2751 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2754 p->dsdb_dn->extra_part, 0,
2756 schema_attr->syntax->ldap_oid,
2758 if (ret != LDB_SUCCESS) {
2759 talloc_free(tmp_ctx);
2762 if (exact == NULL) {
2763 struct GUID_txt_buf buf;
2764 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2765 "exist for target GUID %s",
2767 GUID_buf_string(&p->guid, &buf));
2768 if (ldb_attr_cmp(el->name, "member") == 0) {
2769 talloc_free(tmp_ctx);
2770 return LDB_ERR_UNWILLING_TO_PERFORM;
2772 talloc_free(tmp_ctx);
2773 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2778 if (CHECK_DEBUGLVL(5)) {
2779 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2780 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2781 struct GUID_txt_buf buf;
2782 const char *guid_str = \
2783 GUID_buf_string(&p->guid, &buf);
2784 DEBUG(5, ("Deleting deleted linked "
2785 "attribute %s to %s, because "
2786 "vanish_links control is set\n",
2787 el->name, guid_str));
2791 /* remove the backlink */
2792 ret = replmd_add_backlink(module,
2799 if (ret != LDB_SUCCESS) {
2800 talloc_free(tmp_ctx);
2804 /* We flag the deletion and tidy it up later. */
2809 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2811 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2812 struct GUID_txt_buf buf;
2813 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2814 ldb_asprintf_errstring(ldb, "Attribute %s already "
2815 "deleted for target GUID %s",
2816 el->name, guid_str);
2817 if (ldb_attr_cmp(el->name, "member") == 0) {
2818 talloc_free(tmp_ctx);
2819 return LDB_ERR_UNWILLING_TO_PERFORM;
2821 talloc_free(tmp_ctx);
2822 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2826 ret = replmd_update_la_val(old_el->values, exact->v,
2827 exact->dsdb_dn, exact->dsdb_dn,
2828 invocation_id, seq_num, seq_num,
2830 if (ret != LDB_SUCCESS) {
2831 talloc_free(tmp_ctx);
2834 ret = replmd_add_backlink(module, replmd_private,
2839 if (ret != LDB_SUCCESS) {
2840 talloc_free(tmp_ctx);
2847 for (i = 0; i < old_el->num_values; i++) {
2848 if (old_dns[i].v != NULL) {
2849 old_el->values[j] = *old_dns[i].v;
2853 old_el->num_values = j;
2856 el->values = talloc_steal(msg->elements, old_el->values);
2857 el->num_values = old_el->num_values;
2859 talloc_free(tmp_ctx);
2861 /* we now tell the backend to replace all existing values
2862 with the one we have constructed */
2863 el->flags = LDB_FLAG_MOD_REPLACE;
2869 handle replacing a linked attribute
2871 static int replmd_modify_la_replace(struct ldb_module *module,
2872 struct replmd_private *replmd_private,
2873 const struct dsdb_schema *schema,
2874 struct ldb_message *msg,
2875 struct ldb_message_element *el,
2876 struct ldb_message_element *old_el,
2877 const struct dsdb_attribute *schema_attr,
2880 struct ldb_dn *msg_dn,
2881 struct ldb_request *parent)
2883 unsigned int i, old_i, new_i;
2884 struct parsed_dn *dns, *old_dns;
2885 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2887 const struct GUID *invocation_id;
2888 struct ldb_context *ldb = ldb_module_get_ctx(module);
2889 struct ldb_val *new_values = NULL;
2890 const char *ldap_oid = schema_attr->syntax->ldap_oid;
2891 unsigned int old_num_values;
2892 unsigned int repl_num_values;
2893 unsigned int max_num_values;
2896 unix_to_nt_time(&now, t);
2898 invocation_id = samdb_ntds_invocation_id(ldb);
2899 if (!invocation_id) {
2900 return LDB_ERR_OPERATIONS_ERROR;
2904 * The replace operation is unlike the replace and delete cases in that
2905 * we need to look at every existing link to see whether it is being
2906 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
2908 * As we are trying to combine two sorted lists, the algorithm we use
2909 * is akin to the merge phase of a merge sort. We interleave the two
2910 * lists, doing different things depending on which side the current
2913 * There are three main cases, with some sub-cases.
2915 * - a DN is in the old list but not the new one. It needs to be
2916 * marked as deleted (but left in the list).
2917 * - maybe it is already deleted, and we have less to do.
2919 * - a DN is in both lists. The old data gets replaced by the new,
2920 * and the list doesn't grow. The old link may have been marked as
2921 * deleted, in which case we undelete it.
2923 * - a DN is in the new list only. We add it in the right place.
2926 old_num_values = old_el ? old_el->num_values : 0;
2927 repl_num_values = el->num_values;
2928 max_num_values = old_num_values + repl_num_values;
2930 if (max_num_values == 0) {
2931 /* There is nothing to do! */
2935 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
2936 if (ret != LDB_SUCCESS) {
2937 talloc_free(tmp_ctx);
2941 ret = check_parsed_dn_duplicates(module, el, dns);
2942 if (ret != LDB_SUCCESS) {
2943 talloc_free(tmp_ctx);
2947 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
2949 if (ret != LDB_SUCCESS) {
2950 talloc_free(tmp_ctx);
2954 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
2956 if (ret != LDB_SUCCESS) {
2957 talloc_free(tmp_ctx);
2961 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
2962 if (new_values == NULL) {
2963 ldb_module_oom(module);
2964 talloc_free(tmp_ctx);
2965 return LDB_ERR_OPERATIONS_ERROR;
2970 for (i = 0; i < max_num_values; i++) {
2972 struct parsed_dn *old_p, *new_p;
2973 if (old_i < old_num_values && new_i < repl_num_values) {
2974 old_p = &old_dns[old_i];
2975 new_p = &dns[new_i];
2976 cmp = parsed_dn_compare(old_p, new_p);
2977 } else if (old_i < old_num_values) {
2978 /* the new list is empty, read the old list */
2979 old_p = &old_dns[old_i];
2982 } else if (new_i < repl_num_values) {
2983 /* the old list is empty, read new list */
2985 new_p = &dns[new_i];
2993 * An old ones that come before the next replacement
2994 * (if any). We mark it as deleted and add it to the
2997 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2998 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
2999 ret = replmd_update_la_val(new_values, old_p->v,
3005 if (ret != LDB_SUCCESS) {
3006 talloc_free(tmp_ctx);
3010 ret = replmd_add_backlink(module, replmd_private,
3013 &old_p->guid, false,
3016 if (ret != LDB_SUCCESS) {
3017 talloc_free(tmp_ctx);
3021 new_values[i] = *old_p->v;
3023 } else if (cmp == 0) {
3025 * We are overwriting one. If it was previously
3026 * deleted, we need to add a backlink.
3028 * Note that if any RMD_FLAGs in an extended new DN
3033 ret = replmd_update_la_val(new_values, old_p->v,
3039 if (ret != LDB_SUCCESS) {
3040 talloc_free(tmp_ctx);
3044 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3045 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3046 ret = replmd_add_backlink(module, replmd_private,
3052 if (ret != LDB_SUCCESS) {
3053 talloc_free(tmp_ctx);
3058 new_values[i] = *old_p->v;
3063 * Replacements that don't match an existing one. We
3064 * just add them to the final list.
3066 ret = replmd_build_la_val(new_values,
3071 if (ret != LDB_SUCCESS) {
3072 talloc_free(tmp_ctx);
3075 ret = replmd_add_backlink(module, replmd_private,
3081 if (ret != LDB_SUCCESS) {
3082 talloc_free(tmp_ctx);
3085 new_values[i] = *new_p->v;
3089 if (old_el != NULL) {
3090 talloc_steal(msg->elements, old_el->values);
3092 el->values = talloc_steal(msg->elements, new_values);
3094 talloc_free(tmp_ctx);
3096 el->flags = LDB_FLAG_MOD_REPLACE;
3103 handle linked attributes in modify requests
3105 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3106 struct replmd_private *replmd_private,
3107 struct ldb_message *msg,
3108 uint64_t seq_num, time_t t,
3109 struct ldb_request *parent)
3111 struct ldb_result *res;
3114 struct ldb_context *ldb = ldb_module_get_ctx(module);
3115 struct ldb_message *old_msg;
3117 const struct dsdb_schema *schema;
3119 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3121 * Nothing special is required for modifying or vanishing links
3122 * in fl2000 since they are just strings in a multi-valued
3125 struct ldb_control *ctrl = ldb_request_get_control(parent,
3126 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3128 ctrl->critical = false;
3136 * We should restrict this to the intersection of the list of
3137 * linked attributes in the schema and the list of attributes
3140 * This will help performance a little, as otherwise we have
3141 * to allocate the entire object value-by-value.
3143 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3144 DSDB_FLAG_NEXT_MODULE |
3145 DSDB_SEARCH_SHOW_RECYCLED |
3146 DSDB_SEARCH_REVEAL_INTERNALS |
3147 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3149 if (ret != LDB_SUCCESS) {
3152 schema = dsdb_get_schema(ldb, res);
3154 return LDB_ERR_OPERATIONS_ERROR;
3157 old_msg = res->msgs[0];
3159 for (i=0; i<msg->num_elements; i++) {
3160 struct ldb_message_element *el = &msg->elements[i];
3161 struct ldb_message_element *old_el, *new_el;
3162 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3163 const struct dsdb_attribute *schema_attr
3164 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3166 ldb_asprintf_errstring(ldb,
3167 "%s: attribute %s is not a valid attribute in schema",
3168 __FUNCTION__, el->name);
3169 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3171 if (schema_attr->linkID == 0) {
3174 if ((schema_attr->linkID & 1) == 1) {
3176 struct ldb_control *ctrl;
3178 ctrl = ldb_request_get_control(parent,
3179 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3181 ctrl->critical = false;
3184 ctrl = ldb_request_get_control(parent,
3185 DSDB_CONTROL_DBCHECK);
3191 /* Odd is for the target. Illegal to modify */
3192 ldb_asprintf_errstring(ldb,
3193 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3194 return LDB_ERR_UNWILLING_TO_PERFORM;
3196 old_el = ldb_msg_find_element(old_msg, el->name);
3198 case LDB_FLAG_MOD_REPLACE:
3199 ret = replmd_modify_la_replace(module, replmd_private,
3200 schema, msg, el, old_el,
3201 schema_attr, seq_num, t,
3205 case LDB_FLAG_MOD_DELETE:
3206 ret = replmd_modify_la_delete(module, replmd_private,
3207 schema, msg, el, old_el,
3208 schema_attr, seq_num, t,
3212 case LDB_FLAG_MOD_ADD:
3213 ret = replmd_modify_la_add(module, replmd_private,
3214 schema, msg, el, old_el,
3215 schema_attr, seq_num, t,
3220 ldb_asprintf_errstring(ldb,
3221 "invalid flags 0x%x for %s linked attribute",
3222 el->flags, el->name);
3223 return LDB_ERR_UNWILLING_TO_PERFORM;
3225 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3226 ldb_asprintf_errstring(ldb,
3227 "Attribute %s is single valued but more than one value has been supplied",
3229 /* Return codes as found on Windows 2012r2 */
3230 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3231 return LDB_ERR_CONSTRAINT_VIOLATION;
3233 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3236 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3239 if (ret != LDB_SUCCESS) {
3243 ldb_msg_remove_attr(old_msg, el->name);
3245 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3246 new_el->num_values = el->num_values;
3247 new_el->values = talloc_steal(msg->elements, el->values);
3249 /* TODO: this relises a bit too heavily on the exact
3250 behaviour of ldb_msg_find_element and
3251 ldb_msg_remove_element */
3252 old_el = ldb_msg_find_element(msg, el->name);
3254 ldb_msg_remove_element(msg, old_el);
3264 static int send_rodc_referral(struct ldb_request *req,
3265 struct ldb_context *ldb,
3268 char *referral = NULL;
3269 struct loadparm_context *lp_ctx = NULL;
3270 struct ldb_dn *fsmo_role_dn = NULL;
3271 struct ldb_dn *role_owner_dn = NULL;
3272 const char *domain = NULL;
3275 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3276 struct loadparm_context);
3278 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3279 &fsmo_role_dn, &role_owner_dn);
3281 if (W_ERROR_IS_OK(werr)) {
3282 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3283 if (server_dn != NULL) {
3284 ldb_dn_remove_child_components(server_dn, 1);
3285 domain = samdb_dn_to_dnshostname(ldb, req,
3290 if (domain == NULL) {
3291 domain = lpcfg_dnsdomain(lp_ctx);
3294 referral = talloc_asprintf(req, "ldap://%s/%s",
3296 ldb_dn_get_linearized(dn));
3297 if (referral == NULL) {
3299 return LDB_ERR_OPERATIONS_ERROR;
3302 return ldb_module_send_referral(req, referral);
3306 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3308 struct ldb_context *ldb;
3309 struct replmd_replicated_request *ac;
3310 struct ldb_request *down_req;
3311 struct ldb_message *msg;
3312 time_t t = time(NULL);
3314 bool is_urgent = false, rodc = false;
3315 bool is_schema_nc = false;
3316 unsigned int functional_level;
3317 const struct ldb_message_element *guid_el = NULL;
3318 struct ldb_control *sd_propagation_control;
3319 struct ldb_control *fix_links_control = NULL;
3320 struct replmd_private *replmd_private =
3321 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3323 /* do not manipulate our control entries */
3324 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3325 return ldb_next_request(module, req);
3328 sd_propagation_control = ldb_request_get_control(req,
3329 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3330 if (sd_propagation_control != NULL) {
3331 if (req->op.mod.message->num_elements != 1) {
3332 return ldb_module_operr(module);
3334 ret = strcmp(req->op.mod.message->elements[0].name,
3335 "nTSecurityDescriptor");
3337 return ldb_module_operr(module);
3340 return ldb_next_request(module, req);
3343 ldb = ldb_module_get_ctx(module);
3345 fix_links_control = ldb_request_get_control(req,
3346 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3347 if (fix_links_control != NULL) {
3348 struct dsdb_schema *schema = NULL;
3349 const struct dsdb_attribute *sa = NULL;
3351 if (req->op.mod.message->num_elements != 1) {
3352 return ldb_module_operr(module);
3355 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3356 return ldb_module_operr(module);
3359 schema = dsdb_get_schema(ldb, req);
3360 if (schema == NULL) {
3361 return ldb_module_operr(module);
3364 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3365 req->op.mod.message->elements[0].name);
3367 return ldb_module_operr(module);
3370 if (sa->linkID == 0) {
3371 return ldb_module_operr(module);
3374 fix_links_control->critical = false;
3375 return ldb_next_request(module, req);
3378 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3380 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3381 if (guid_el != NULL) {
3382 ldb_set_errstring(ldb,
3383 "replmd_modify: it's not allowed to change the objectGUID!");
3384 return LDB_ERR_CONSTRAINT_VIOLATION;
3387 ac = replmd_ctx_init(module, req);
3389 return ldb_module_oom(module);
3392 functional_level = dsdb_functional_level(ldb);
3394 /* we have to copy the message as the caller might have it as a const */
3395 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3399 return LDB_ERR_OPERATIONS_ERROR;
3402 ldb_msg_remove_attr(msg, "whenChanged");
3403 ldb_msg_remove_attr(msg, "uSNChanged");
3405 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3407 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3408 msg, &ac->seq_num, t, is_schema_nc,
3410 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3411 ret = send_rodc_referral(req, ldb, msg->dn);
3417 if (ret != LDB_SUCCESS) {
3422 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3423 msg, ac->seq_num, t, req);
3424 if (ret != LDB_SUCCESS) {
3430 * - replace the old object with the newly constructed one
3433 ac->is_urgent = is_urgent;
3435 ret = ldb_build_mod_req(&down_req, ldb, ac,
3438 ac, replmd_op_callback,
3440 LDB_REQ_SET_LOCATION(down_req);
3441 if (ret != LDB_SUCCESS) {
3446 /* current partition control is needed by "replmd_op_callback" */
3447 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3448 ret = ldb_request_add_control(down_req,
3449 DSDB_CONTROL_CURRENT_PARTITION_OID,
3451 if (ret != LDB_SUCCESS) {
3457 /* If we are in functional level 2000, then
3458 * replmd_modify_handle_linked_attribs will have done
3460 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3461 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3462 if (ret != LDB_SUCCESS) {
3468 talloc_steal(down_req, msg);
3470 /* we only change whenChanged and uSNChanged if the seq_num
3472 if (ac->seq_num != 0) {
3473 ret = add_time_element(msg, "whenChanged", t);
3474 if (ret != LDB_SUCCESS) {
3480 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3481 if (ret != LDB_SUCCESS) {
3488 /* go on with the call chain */
3489 return ldb_next_request(module, down_req);
3492 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3495 handle a rename request
3497 On a rename we need to do an extra ldb_modify which sets the
3498 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3500 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3502 struct ldb_context *ldb;
3503 struct replmd_replicated_request *ac;
3505 struct ldb_request *down_req;
3507 /* do not manipulate our control entries */
3508 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3509 return ldb_next_request(module, req);
3512 ldb = ldb_module_get_ctx(module);
3514 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3516 ac = replmd_ctx_init(module, req);
3518 return ldb_module_oom(module);
3521 ret = ldb_build_rename_req(&down_req, ldb, ac,
3522 ac->req->op.rename.olddn,
3523 ac->req->op.rename.newdn,
3525 ac, replmd_rename_callback,
3527 LDB_REQ_SET_LOCATION(down_req);
3528 if (ret != LDB_SUCCESS) {
3533 /* go on with the call chain */
3534 return ldb_next_request(module, down_req);
3537 /* After the rename is compleated, update the whenchanged etc */
3538 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3540 struct ldb_context *ldb;
3541 struct ldb_request *down_req;
3542 struct ldb_message *msg;
3543 const struct dsdb_attribute *rdn_attr;
3544 const char *rdn_name;
3545 const struct ldb_val *rdn_val;
3546 const char *attrs[5] = { NULL, };
3547 time_t t = time(NULL);
3549 bool is_urgent = false, rodc = false;
3551 struct replmd_replicated_request *ac =
3552 talloc_get_type(req->context, struct replmd_replicated_request);
3553 struct replmd_private *replmd_private =
3554 talloc_get_type(ldb_module_get_private(ac->module),
3555 struct replmd_private);
3557 ldb = ldb_module_get_ctx(ac->module);
3559 if (ares->error != LDB_SUCCESS) {
3560 return ldb_module_done(ac->req, ares->controls,
3561 ares->response, ares->error);
3564 if (ares->type != LDB_REPLY_DONE) {
3565 ldb_set_errstring(ldb,
3566 "invalid ldb_reply_type in callback");
3568 return ldb_module_done(ac->req, NULL, NULL,
3569 LDB_ERR_OPERATIONS_ERROR);
3573 * - replace the old object with the newly constructed one
3576 msg = ldb_msg_new(ac);
3579 return LDB_ERR_OPERATIONS_ERROR;
3582 msg->dn = ac->req->op.rename.newdn;
3584 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3586 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3587 if (rdn_name == NULL) {
3589 return ldb_module_done(ac->req, NULL, NULL,
3593 /* normalize the rdn attribute name */
3594 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3595 if (rdn_attr == NULL) {
3597 return ldb_module_done(ac->req, NULL, NULL,
3600 rdn_name = rdn_attr->lDAPDisplayName;
3602 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3603 if (rdn_val == NULL) {
3605 return ldb_module_done(ac->req, NULL, NULL,
3609 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3611 return ldb_module_done(ac->req, NULL, NULL,
3614 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3616 return ldb_module_done(ac->req, NULL, NULL,
3619 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3621 return ldb_module_done(ac->req, NULL, NULL,
3624 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3626 return ldb_module_done(ac->req, NULL, NULL,
3631 * here we let replmd_update_rpmd() only search for
3632 * the existing "replPropertyMetaData" and rdn_name attributes.
3634 * We do not want the existing "name" attribute as
3635 * the "name" attribute needs to get the version
3636 * updated on rename even if the rdn value hasn't changed.
3638 * This is the diff of the meta data, for a moved user
3639 * on a w2k8r2 server:
3642 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3643 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3644 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3645 * version : 0x00000001 (1)
3646 * reserved : 0x00000000 (0)
3647 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3648 * local_usn : 0x00000000000037a5 (14245)
3649 * array: struct replPropertyMetaData1
3650 * attid : DRSUAPI_ATTID_name (0x90001)
3651 * - version : 0x00000001 (1)
3652 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3653 * + version : 0x00000002 (2)
3654 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3655 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3656 * - originating_usn : 0x00000000000037a5 (14245)
3657 * - local_usn : 0x00000000000037a5 (14245)
3658 * + originating_usn : 0x0000000000003834 (14388)
3659 * + local_usn : 0x0000000000003834 (14388)
3660 * array: struct replPropertyMetaData1
3661 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3662 * version : 0x00000004 (4)
3664 attrs[0] = "replPropertyMetaData";
3665 attrs[1] = "objectClass";
3666 attrs[2] = "instanceType";
3667 attrs[3] = rdn_name;
3670 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3671 msg, &ac->seq_num, t,
3672 is_schema_nc, &is_urgent, &rodc);
3673 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3674 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3676 return ldb_module_done(req, NULL, NULL, ret);
3679 if (ret != LDB_SUCCESS) {
3681 return ldb_module_done(ac->req, NULL, NULL, ret);
3684 if (ac->seq_num == 0) {
3686 return ldb_module_done(ac->req, NULL, NULL,
3688 "internal error seq_num == 0"));
3690 ac->is_urgent = is_urgent;
3692 ret = ldb_build_mod_req(&down_req, ldb, ac,
3695 ac, replmd_op_callback,
3697 LDB_REQ_SET_LOCATION(down_req);
3698 if (ret != LDB_SUCCESS) {
3703 /* current partition control is needed by "replmd_op_callback" */
3704 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3705 ret = ldb_request_add_control(down_req,
3706 DSDB_CONTROL_CURRENT_PARTITION_OID,
3708 if (ret != LDB_SUCCESS) {
3714 talloc_steal(down_req, msg);
3716 ret = add_time_element(msg, "whenChanged", t);
3717 if (ret != LDB_SUCCESS) {
3723 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3724 if (ret != LDB_SUCCESS) {
3730 /* go on with the call chain - do the modify after the rename */
3731 return ldb_next_request(ac->module, down_req);
3735 * remove links from objects that point at this object when an object
3736 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3737 * RemoveObj which states that link removal due to the object being
3738 * deleted is NOT an originating update - they just go away!
3741 static int replmd_delete_remove_link(struct ldb_module *module,
3742 const struct dsdb_schema *schema,
3743 struct replmd_private *replmd_private,
3746 struct ldb_message_element *el,
3747 const struct dsdb_attribute *sa,
3748 struct ldb_request *parent)
3751 TALLOC_CTX *tmp_ctx = talloc_new(module);
3752 struct ldb_context *ldb = ldb_module_get_ctx(module);
3754 for (i=0; i<el->num_values; i++) {
3755 struct dsdb_dn *dsdb_dn;
3757 struct ldb_message *msg;
3758 const struct dsdb_attribute *target_attr;
3759 struct ldb_message_element *el2;
3761 struct ldb_val dn_val;
3762 uint32_t dsdb_flags = 0;
3763 const char *attrs[] = { NULL, NULL };
3764 struct ldb_result *link_res;
3765 struct ldb_message *link_msg;
3766 struct ldb_message_element *link_el;
3767 struct parsed_dn *link_dns;
3768 struct parsed_dn *p = NULL, *unused = NULL;
3770 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3774 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3776 talloc_free(tmp_ctx);
3777 return LDB_ERR_OPERATIONS_ERROR;
3780 /* remove the link */
3781 msg = ldb_msg_new(tmp_ctx);
3783 ldb_module_oom(module);
3784 talloc_free(tmp_ctx);
3785 return LDB_ERR_OPERATIONS_ERROR;
3789 msg->dn = dsdb_dn->dn;
3791 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3792 if (target_attr == NULL) {
3795 attrs[0] = target_attr->lDAPDisplayName;
3797 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
3798 LDB_FLAG_MOD_DELETE, &el2);
3799 if (ret != LDB_SUCCESS) {
3800 ldb_module_oom(module);
3801 talloc_free(tmp_ctx);
3802 return LDB_ERR_OPERATIONS_ERROR;
3805 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
3807 DSDB_FLAG_NEXT_MODULE |
3808 DSDB_SEARCH_SHOW_EXTENDED_DN |
3809 DSDB_SEARCH_SHOW_RECYCLED,
3812 if (ret != LDB_SUCCESS) {
3813 talloc_free(tmp_ctx);
3817 link_msg = link_res->msgs[0];
3818 link_el = ldb_msg_find_element(link_msg,
3819 target_attr->lDAPDisplayName);
3820 if (link_el == NULL) {
3821 talloc_free(tmp_ctx);
3822 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3826 * This call 'upgrades' the links in link_dns, but we
3827 * do not commit the result back into the database, so
3828 * this is safe to call in FL2000 or on databases that
3829 * have been run at that level in the past.
3831 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
3833 target_attr->syntax->ldap_oid, parent);
3834 if (ret != LDB_SUCCESS) {
3835 talloc_free(tmp_ctx);
3839 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
3843 target_attr->syntax->ldap_oid, false);
3844 if (ret != LDB_SUCCESS) {
3845 talloc_free(tmp_ctx);
3850 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3851 "Failed to find forward link on %s "
3852 "as %s to remove backlink %s on %s",
3853 ldb_dn_get_linearized(msg->dn),
3854 target_attr->lDAPDisplayName,
3855 sa->lDAPDisplayName,
3856 ldb_dn_get_linearized(dn));
3857 talloc_free(tmp_ctx);
3858 return LDB_ERR_NO_SUCH_ATTRIBUTE;
3862 /* This needs to get the Binary DN, by first searching */
3863 dn_str = dsdb_dn_get_linearized(tmp_ctx,
3866 dn_val = data_blob_string_const(dn_str);
3867 el2->values = &dn_val;
3868 el2->num_values = 1;
3871 * Ensure that we tell the modification to vanish any linked
3872 * attributes (not simply mark them as isDeleted = TRUE)
3874 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3876 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3877 if (ret != LDB_SUCCESS) {
3878 talloc_free(tmp_ctx);
3882 talloc_free(tmp_ctx);
3888 handle update of replication meta data for deletion of objects
3890 This also handles the mapping of delete to a rename operation
3891 to allow deletes to be replicated.
3893 It also handles the incoming deleted objects, to ensure they are
3894 fully deleted here. In that case re_delete is true, and we do not
3895 use this as a signal to change the deleted state, just reinforce it.
3898 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3900 int ret = LDB_ERR_OTHER;
3901 bool retb, disallow_move_on_delete;
3902 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
3903 const char *rdn_name;
3904 const struct ldb_val *rdn_value, *new_rdn_value;
3906 struct ldb_context *ldb = ldb_module_get_ctx(module);
3907 const struct dsdb_schema *schema;
3908 struct ldb_message *msg, *old_msg;
3909 struct ldb_message_element *el;
3910 TALLOC_CTX *tmp_ctx;
3911 struct ldb_result *res, *parent_res;
3912 static const char * const preserved_attrs[] = {
3913 /* yes, this really is a hard coded list. See MS-ADTS
3914 section 3.1.1.5.5.1.1 */
3917 "dNReferenceUpdate",
3928 "msDS-LastKnownRDN",
3934 "distinguishedName",
3938 "proxiedObjectName",
3940 "nTSecurityDescriptor",
3941 "replPropertyMetaData",
3943 "securityIdentifier",
3951 "userAccountControl",
3958 static const char * const all_attrs[] = {
3959 DSDB_SECRET_ATTRIBUTES,
3963 unsigned int i, el_count = 0;
3964 uint32_t dsdb_flags = 0;
3965 struct replmd_private *replmd_private;
3966 enum deletion_state deletion_state, next_deletion_state;
3968 if (ldb_dn_is_special(req->op.del.dn)) {
3969 return ldb_next_request(module, req);
3973 * We have to allow dbcheck to remove an object that
3974 * is beyond repair, and to do so totally. This could
3975 * mean we we can get a partial object from the other
3976 * DC, causing havoc, so dbcheck suggests
3977 * re-replication first. dbcheck sets both DBCHECK
3978 * and RELAX in this situation.
3980 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3981 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3982 /* really, really remove it */
3983 return ldb_next_request(module, req);
3986 tmp_ctx = talloc_new(ldb);
3989 return LDB_ERR_OPERATIONS_ERROR;
3992 schema = dsdb_get_schema(ldb, tmp_ctx);
3994 talloc_free(tmp_ctx);
3995 return LDB_ERR_OPERATIONS_ERROR;
3998 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4000 /* we need the complete msg off disk, so we can work out which
4001 attributes need to be removed */
4002 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4003 DSDB_FLAG_NEXT_MODULE |
4004 DSDB_SEARCH_SHOW_RECYCLED |
4005 DSDB_SEARCH_REVEAL_INTERNALS |
4006 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4007 if (ret != LDB_SUCCESS) {
4008 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4009 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4010 re_delete ? "re-delete" : "delete",
4011 ldb_dn_get_linearized(old_dn),
4012 ldb_errstring(ldb_module_get_ctx(module)));
4013 talloc_free(tmp_ctx);
4016 old_msg = res->msgs[0];
4018 replmd_deletion_state(module, old_msg,
4020 &next_deletion_state);
4022 /* This supports us noticing an incoming isDeleted and acting on it */
4024 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4025 next_deletion_state = deletion_state;
4028 if (next_deletion_state == OBJECT_REMOVED) {
4030 * We have to prevent objects being deleted, even if
4031 * the administrator really wants them gone, as
4032 * without the tombstone, we can get a partial object
4033 * from the other DC, causing havoc.
4035 * The only other valid case is when the 180 day
4036 * timeout has expired, when relax is specified.
4038 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4039 /* it is already deleted - really remove it this time */
4040 talloc_free(tmp_ctx);
4041 return ldb_next_request(module, req);
4044 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4045 "This check is to prevent corruption of the replicated state.",
4046 ldb_dn_get_linearized(old_msg->dn));
4047 return LDB_ERR_UNWILLING_TO_PERFORM;
4050 rdn_name = ldb_dn_get_rdn_name(old_dn);
4051 rdn_value = ldb_dn_get_rdn_val(old_dn);
4052 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4053 talloc_free(tmp_ctx);
4054 return ldb_operr(ldb);
4057 msg = ldb_msg_new(tmp_ctx);
4059 ldb_module_oom(module);
4060 talloc_free(tmp_ctx);
4061 return LDB_ERR_OPERATIONS_ERROR;
4066 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4067 disallow_move_on_delete =
4068 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4069 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4071 /* work out where we will be renaming this object to */
4072 if (!disallow_move_on_delete) {
4073 struct ldb_dn *deleted_objects_dn;
4074 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4075 &deleted_objects_dn);
4078 * We should not move objects if we can't find the
4079 * deleted objects DN. Not moving (or otherwise
4080 * harming) the Deleted Objects DN itself is handled
4083 if (re_delete && (ret != LDB_SUCCESS)) {
4084 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4085 if (new_dn == NULL) {
4086 ldb_module_oom(module);
4087 talloc_free(tmp_ctx);
4088 return LDB_ERR_OPERATIONS_ERROR;
4090 } else if (ret != LDB_SUCCESS) {
4091 /* this is probably an attempted delete on a partition
4092 * that doesn't allow delete operations, such as the
4093 * schema partition */
4094 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4095 ldb_dn_get_linearized(old_dn));
4096 talloc_free(tmp_ctx);
4097 return LDB_ERR_UNWILLING_TO_PERFORM;
4099 new_dn = deleted_objects_dn;
4102 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4103 if (new_dn == NULL) {
4104 ldb_module_oom(module);
4105 talloc_free(tmp_ctx);
4106 return LDB_ERR_OPERATIONS_ERROR;
4110 /* get the objects GUID from the search we just did */
4111 guid = samdb_result_guid(old_msg, "objectGUID");
4113 if (deletion_state == OBJECT_NOT_DELETED) {
4115 ret = replmd_make_deleted_child_dn(tmp_ctx,
4118 rdn_name, rdn_value,
4121 if (ret != LDB_SUCCESS) {
4122 talloc_free(tmp_ctx);
4126 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
4127 if (ret != LDB_SUCCESS) {
4128 ldb_asprintf_errstring(ldb, __location__
4129 ": Failed to add isDeleted string to the msg");
4130 talloc_free(tmp_ctx);
4133 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4136 * No matter what has happened with other renames etc, try again to
4137 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4140 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4141 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4143 ldb_asprintf_errstring(ldb, __location__
4144 ": Unable to add a prepare rdn of %s",
4145 ldb_dn_get_linearized(rdn));
4146 talloc_free(tmp_ctx);
4147 return LDB_ERR_OPERATIONS_ERROR;
4149 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4151 retb = ldb_dn_add_child(new_dn, rdn);
4153 ldb_asprintf_errstring(ldb, __location__
4154 ": Unable to add rdn %s to base dn: %s",
4155 ldb_dn_get_linearized(rdn),
4156 ldb_dn_get_linearized(new_dn));
4157 talloc_free(tmp_ctx);
4158 return LDB_ERR_OPERATIONS_ERROR;
4163 now we need to modify the object in the following ways:
4165 - add isDeleted=TRUE
4166 - update rDN and name, with new rDN
4167 - remove linked attributes
4168 - remove objectCategory and sAMAccountType
4169 - remove attribs not on the preserved list
4170 - preserved if in above list, or is rDN
4171 - remove all linked attribs from this object
4172 - remove all links from other objects to this object
4173 - add lastKnownParent
4174 - update replPropertyMetaData?
4176 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4179 if (deletion_state == OBJECT_NOT_DELETED) {
4180 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4181 char *parent_dn_str = NULL;
4183 /* we need the storage form of the parent GUID */
4184 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4186 DSDB_FLAG_NEXT_MODULE |
4187 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4188 DSDB_SEARCH_REVEAL_INTERNALS|
4189 DSDB_SEARCH_SHOW_RECYCLED, req);
4190 if (ret != LDB_SUCCESS) {
4191 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4192 "repmd_delete: Failed to %s %s, "
4193 "because we failed to find it's parent (%s): %s",
4194 re_delete ? "re-delete" : "delete",
4195 ldb_dn_get_linearized(old_dn),
4196 ldb_dn_get_linearized(parent_dn),
4197 ldb_errstring(ldb_module_get_ctx(module)));
4198 talloc_free(tmp_ctx);
4203 * Now we can use the DB version,
4204 * it will have the extended DN info in it
4206 parent_dn = parent_res->msgs[0]->dn;
4207 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4210 if (parent_dn_str == NULL) {
4211 talloc_free(tmp_ctx);
4212 return ldb_module_oom(module);
4215 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4217 if (ret != LDB_SUCCESS) {
4218 ldb_asprintf_errstring(ldb, __location__
4219 ": Failed to add lastKnownParent "
4220 "string when deleting %s",
4221 ldb_dn_get_linearized(old_dn));
4222 talloc_free(tmp_ctx);
4225 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4227 if (next_deletion_state == OBJECT_DELETED) {
4228 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4229 if (ret != LDB_SUCCESS) {
4230 ldb_asprintf_errstring(ldb, __location__
4231 ": Failed to add msDS-LastKnownRDN "
4232 "string when deleting %s",
4233 ldb_dn_get_linearized(old_dn));
4234 talloc_free(tmp_ctx);
4237 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
4241 switch (next_deletion_state) {
4243 case OBJECT_RECYCLED:
4244 case OBJECT_TOMBSTONE:
4247 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4248 * describes what must be removed from a tombstone
4251 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4252 * describes what must be removed from a recycled
4258 * we also mark it as recycled, meaning this object can't be
4259 * recovered (we are stripping its attributes).
4260 * This is done only if we have this schema object of course ...
4261 * This behavior is identical to the one of Windows 2008R2 which
4262 * always set the isRecycled attribute, even if the recycle-bin is
4263 * not activated and what ever the forest level is.
4265 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4266 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
4267 if (ret != LDB_SUCCESS) {
4268 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4269 ldb_module_oom(module);
4270 talloc_free(tmp_ctx);
4273 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
4276 replmd_private = talloc_get_type(ldb_module_get_private(module),
4277 struct replmd_private);
4278 /* work out which of the old attributes we will be removing */
4279 for (i=0; i<old_msg->num_elements; i++) {
4280 const struct dsdb_attribute *sa;
4281 el = &old_msg->elements[i];
4282 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4284 talloc_free(tmp_ctx);
4285 return LDB_ERR_OPERATIONS_ERROR;
4287 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4288 /* don't remove the rDN */
4292 if (sa->linkID & 1) {
4294 we have a backlink in this object
4295 that needs to be removed. We're not
4296 allowed to remove it directly
4297 however, so we instead setup a
4298 modify to delete the corresponding
4301 ret = replmd_delete_remove_link(module, schema,
4305 if (ret == LDB_SUCCESS) {
4307 * now we continue, which means we
4308 * won't remove this backlink
4314 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4315 const char *old_dn_str
4316 = ldb_dn_get_linearized(old_dn);
4317 ldb_asprintf_errstring(ldb,
4319 ": Failed to remove backlink of "
4320 "%s when deleting %s: %s",
4323 ldb_errstring(ldb));
4324 talloc_free(tmp_ctx);
4325 return LDB_ERR_OPERATIONS_ERROR;
4329 * Otherwise vanish the link, we are
4330 * out of sync and the controlling
4331 * object does not have the source
4335 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4337 } else if (sa->linkID == 0) {
4338 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4341 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4346 * Ensure that we tell the modification to vanish any linked
4347 * attributes (not simply mark them as isDeleted = TRUE)
4349 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4351 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4352 if (ret != LDB_SUCCESS) {
4353 talloc_free(tmp_ctx);
4354 ldb_module_oom(module);
4361 case OBJECT_DELETED:
4363 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4364 * describes what must be removed from a deleted
4368 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4369 if (ret != LDB_SUCCESS) {
4370 talloc_free(tmp_ctx);
4371 ldb_module_oom(module);
4375 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4376 if (ret != LDB_SUCCESS) {
4377 talloc_free(tmp_ctx);
4378 ldb_module_oom(module);
4388 if (deletion_state == OBJECT_NOT_DELETED) {
4389 const struct dsdb_attribute *sa;
4391 /* work out what the new rdn value is, for updating the
4392 rDN and name fields */
4393 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4394 if (new_rdn_value == NULL) {
4395 talloc_free(tmp_ctx);
4396 return ldb_operr(ldb);
4399 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4401 talloc_free(tmp_ctx);
4402 return LDB_ERR_OPERATIONS_ERROR;
4405 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4407 if (ret != LDB_SUCCESS) {
4408 talloc_free(tmp_ctx);
4411 el->flags = LDB_FLAG_MOD_REPLACE;
4413 el = ldb_msg_find_element(old_msg, "name");
4415 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4416 if (ret != LDB_SUCCESS) {
4417 talloc_free(tmp_ctx);
4420 el->flags = LDB_FLAG_MOD_REPLACE;
4425 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4430 * No matter what has happned with other renames, try again to
4431 * get this to be under the deleted DN.
4433 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4434 /* now rename onto the new DN */
4435 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4436 if (ret != LDB_SUCCESS){
4437 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4438 ldb_dn_get_linearized(old_dn),
4439 ldb_dn_get_linearized(new_dn),
4440 ldb_errstring(ldb)));
4441 talloc_free(tmp_ctx);
4447 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4448 if (ret != LDB_SUCCESS) {
4449 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4450 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4451 talloc_free(tmp_ctx);
4455 talloc_free(tmp_ctx);
4457 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4460 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4462 return replmd_delete_internals(module, req, false);
4466 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4471 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4473 int ret = LDB_ERR_OTHER;
4474 /* TODO: do some error mapping */
4476 /* Let the caller know the full WERROR */
4477 ar->objs->error = status;
4483 static struct replPropertyMetaData1 *
4484 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4485 enum drsuapi_DsAttributeId attid)
4488 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4490 for (i = 0; i < rpmd_ctr->count; i++) {
4491 if (rpmd_ctr->array[i].attid == attid) {
4492 return &rpmd_ctr->array[i];
4500 return true if an update is newer than an existing entry
4501 see section 5.11 of MS-ADTS
4503 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4504 const struct GUID *update_invocation_id,
4505 uint32_t current_version,
4506 uint32_t update_version,
4507 NTTIME current_change_time,
4508 NTTIME update_change_time)
4510 if (update_version != current_version) {
4511 return update_version > current_version;
4513 if (update_change_time != current_change_time) {
4514 return update_change_time > current_change_time;
4516 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4519 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4520 struct replPropertyMetaData1 *new_m)
4522 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4523 &new_m->originating_invocation_id,
4526 cur_m->originating_change_time,
4527 new_m->originating_change_time);
4530 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4531 struct replPropertyMetaData1 *cur_m,
4532 struct replPropertyMetaData1 *new_m)
4537 * If the new replPropertyMetaData entry for this attribute is
4538 * not provided (this happens in the case where we look for
4539 * ATTID_name, but the name was not changed), then the local
4540 * state is clearly still current, as the remote
4541 * server didn't send it due to being older the high watermark
4544 if (new_m == NULL) {
4548 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4550 * if we compare equal then do an
4551 * update. This is used when a client
4552 * asks for a FULL_SYNC, and can be
4553 * used to recover a corrupt
4556 * This call is a bit tricky, what we
4557 * are doing it turning the 'is_newer'
4558 * call into a 'not is older' by
4559 * swapping cur_m and new_m, and negating the
4562 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4565 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4573 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4575 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4576 struct ldb_context *ldb,
4578 const char *four_char_prefix,
4579 const char *rdn_name,
4580 const struct ldb_val *rdn_value,
4583 struct ldb_val deleted_child_rdn_val;
4584 struct GUID_txt_buf guid_str;
4587 GUID_buf_string(&guid, &guid_str);
4589 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4591 ldb_asprintf_errstring(ldb, __location__
4592 ": Unable to add a formatted child to dn: %s",
4593 ldb_dn_get_linearized(dn));
4594 return LDB_ERR_OPERATIONS_ERROR;
4598 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4599 * we should truncate this value to ensure the RDN is not more than 255 chars.
4601 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4603 * "Naming constraints are not enforced for replicated
4604 * updates." so this is safe and we don't have to work out not
4605 * splitting a UTF8 char right now.
4607 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4610 * sizeof(guid_str.buf) will always be longer than
4611 * strlen(guid_str.buf) but we allocate using this and
4612 * waste the trailing bytes to avoid scaring folks
4613 * with memcpy() using strlen() below
4616 deleted_child_rdn_val.data
4617 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4619 rdn_value->length + 5
4620 + sizeof(guid_str.buf));
4621 if (!deleted_child_rdn_val.data) {
4622 ldb_asprintf_errstring(ldb, __location__
4623 ": Unable to add a formatted child to dn: %s",
4624 ldb_dn_get_linearized(dn));
4625 return LDB_ERR_OPERATIONS_ERROR;
4628 deleted_child_rdn_val.length =
4629 rdn_value->length + 5
4630 + strlen(guid_str.buf);
4632 SMB_ASSERT(deleted_child_rdn_val.length <
4633 talloc_get_size(deleted_child_rdn_val.data));
4636 * talloc won't allocate more than 256MB so we can't
4637 * overflow but just to be sure
4639 if (deleted_child_rdn_val.length < rdn_value->length) {
4640 return LDB_ERR_OPERATIONS_ERROR;
4643 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4644 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4645 four_char_prefix, 4);
4646 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4648 sizeof(guid_str.buf));
4650 /* Now set the value into the RDN, without parsing it */
4651 ldb_dn_set_component(dn, 0, rdn_name,
4652 deleted_child_rdn_val);
4661 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4662 struct ldb_context *ldb,
4666 const struct ldb_val *rdn_val;
4667 const char *rdn_name;
4668 struct ldb_dn *new_dn;
4671 rdn_val = ldb_dn_get_rdn_val(dn);
4672 rdn_name = ldb_dn_get_rdn_name(dn);
4673 if (!rdn_val || !rdn_name) {
4677 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4682 ret = replmd_make_prefix_child_dn(mem_ctx,
4688 if (ret != LDB_SUCCESS) {
4697 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4698 struct ldb_context *ldb,
4700 const char *rdn_name,
4701 const struct ldb_val *rdn_value,
4704 return replmd_make_prefix_child_dn(tmp_ctx,
4714 perform a modify operation which sets the rDN and name attributes to
4715 their current values. This has the effect of changing these
4716 attributes to have been last updated by the current DC. This is
4717 needed to ensure that renames performed as part of conflict
4718 resolution are propogated to other DCs
4720 static int replmd_name_modify(struct replmd_replicated_request *ar,
4721 struct ldb_request *req, struct ldb_dn *dn)
4723 struct ldb_message *msg;
4724 const char *rdn_name;
4725 const struct ldb_val *rdn_val;
4726 const struct dsdb_attribute *rdn_attr;
4729 msg = ldb_msg_new(req);
4735 rdn_name = ldb_dn_get_rdn_name(dn);
4736 if (rdn_name == NULL) {
4740 /* normalize the rdn attribute name */
4741 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4742 if (rdn_attr == NULL) {
4745 rdn_name = rdn_attr->lDAPDisplayName;
4747 rdn_val = ldb_dn_get_rdn_val(dn);
4748 if (rdn_val == NULL) {
4752 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4755 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
4758 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
4761 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
4766 * We have to mark this as a replicated update otherwise
4767 * schema_data may reject a rename in the schema partition
4770 ret = dsdb_module_modify(ar->module, msg,
4771 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
4773 if (ret != LDB_SUCCESS) {
4774 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
4775 ldb_dn_get_linearized(dn),
4776 ldb_errstring(ldb_module_get_ctx(ar->module))));
4786 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
4787 ldb_dn_get_linearized(dn)));
4788 return LDB_ERR_OPERATIONS_ERROR;
4793 callback for conflict DN handling where we have renamed the incoming
4794 record. After renaming it, we need to ensure the change of name and
4795 rDN for the incoming record is seen as an originating update by this DC.
4797 This also handles updating lastKnownParent for entries sent to lostAndFound
4799 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4801 struct replmd_replicated_request *ar =
4802 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4803 struct ldb_dn *conflict_dn = NULL;
4806 if (ares->error != LDB_SUCCESS) {
4807 /* call the normal callback for everything except success */
4808 return replmd_op_callback(req, ares);
4811 switch (req->operation) {
4813 conflict_dn = req->op.add.message->dn;
4816 conflict_dn = req->op.mod.message->dn;
4819 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4822 /* perform a modify of the rDN and name of the record */
4823 ret = replmd_name_modify(ar, req, conflict_dn);
4824 if (ret != LDB_SUCCESS) {
4826 return replmd_op_callback(req, ares);
4829 if (ar->objs->objects[ar->index_current].last_known_parent) {
4830 struct ldb_message *msg = ldb_msg_new(req);
4832 ldb_module_oom(ar->module);
4833 return LDB_ERR_OPERATIONS_ERROR;
4836 msg->dn = req->op.add.message->dn;
4838 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4839 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4840 if (ret != LDB_SUCCESS) {
4841 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4842 ldb_module_oom(ar->module);
4845 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4847 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4848 if (ret != LDB_SUCCESS) {
4849 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4850 ldb_dn_get_linearized(msg->dn),
4851 ldb_errstring(ldb_module_get_ctx(ar->module))));
4857 return replmd_op_callback(req, ares);
4861 callback for replmd_replicated_apply_add()
4862 This copes with the creation of conflict records in the case where
4863 the DN exists, but with a different objectGUID
4865 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))
4867 struct ldb_dn *conflict_dn;
4868 struct replmd_replicated_request *ar =
4869 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4870 struct ldb_result *res;
4871 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4873 const struct ldb_val *omd_value;
4874 struct replPropertyMetaDataBlob omd, *rmd;
4875 enum ndr_err_code ndr_err;
4876 bool rename_incoming_record, rodc;
4877 struct replPropertyMetaData1 *rmd_name, *omd_name;
4878 struct ldb_message *msg;
4879 struct ldb_request *down_req = NULL;
4881 /* call the normal callback for success */
4882 if (ares->error == LDB_SUCCESS) {
4883 return callback(req, ares);
4887 * we have a conflict, and need to decide if we will keep the
4888 * new record or the old record
4891 msg = ar->objs->objects[ar->index_current].msg;
4892 conflict_dn = msg->dn;
4894 /* For failures other than conflicts, fail the whole operation here */
4895 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4896 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4897 ldb_dn_get_linearized(conflict_dn),
4898 ldb_errstring(ldb_module_get_ctx(ar->module)));
4900 return ldb_module_done(ar->req, NULL, NULL,
4901 LDB_ERR_OPERATIONS_ERROR);
4904 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4905 if (ret != LDB_SUCCESS) {
4906 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)));
4907 return ldb_module_done(ar->req, NULL, NULL,
4908 LDB_ERR_OPERATIONS_ERROR);
4914 * We are on an RODC, or were a GC for this
4915 * partition, so we have to fail this until
4916 * someone who owns the partition sorts it
4919 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4920 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4921 " - We must fail the operation until a master for this partition resolves the conflict",
4922 ldb_dn_get_linearized(conflict_dn));
4927 * first we need the replPropertyMetaData attribute from the
4928 * local, conflicting record
4930 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4932 DSDB_FLAG_NEXT_MODULE |
4933 DSDB_SEARCH_SHOW_DELETED |
4934 DSDB_SEARCH_SHOW_RECYCLED, req);
4935 if (ret != LDB_SUCCESS) {
4936 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4937 ldb_dn_get_linearized(conflict_dn)));
4941 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4942 if (omd_value == NULL) {
4943 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4944 ldb_dn_get_linearized(conflict_dn)));
4948 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4949 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4950 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4951 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4952 ldb_dn_get_linearized(conflict_dn)));
4956 rmd = ar->objs->objects[ar->index_current].meta_data;
4959 * we decide which is newer based on the RPMD on the name
4960 * attribute. See [MS-DRSR] ResolveNameConflict.
4962 * We expect omd_name to be present, as this is from a local
4963 * search, but while rmd_name should have been given to us by
4964 * the remote server, if it is missing we just prefer the
4966 * replmd_replPropertyMetaData1_new_should_be_taken()
4968 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4969 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4971 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4972 ldb_dn_get_linearized(conflict_dn)));
4977 * Should we preserve the current record, and so rename the
4978 * incoming record to be a conflict?
4980 rename_incoming_record
4981 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4982 omd_name, rmd_name);
4984 if (rename_incoming_record) {
4986 struct ldb_dn *new_dn;
4988 guid = samdb_result_guid(msg, "objectGUID");
4989 if (GUID_all_zero(&guid)) {
4990 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4991 ldb_dn_get_linearized(conflict_dn)));
4994 new_dn = replmd_conflict_dn(req,
4995 ldb_module_get_ctx(ar->module),
4996 conflict_dn, &guid);
4997 if (new_dn == NULL) {
4998 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4999 ldb_dn_get_linearized(conflict_dn)));
5003 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5004 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5006 /* re-submit the request, but with the new DN */
5007 callback = replmd_op_name_modify_callback;
5010 /* we are renaming the existing record */
5012 struct ldb_dn *new_dn;
5014 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5015 if (GUID_all_zero(&guid)) {
5016 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5017 ldb_dn_get_linearized(conflict_dn)));
5021 new_dn = replmd_conflict_dn(req,
5022 ldb_module_get_ctx(ar->module),
5023 conflict_dn, &guid);
5024 if (new_dn == NULL) {
5025 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5026 ldb_dn_get_linearized(conflict_dn)));
5030 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5031 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5033 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5034 DSDB_FLAG_OWN_MODULE, req);
5035 if (ret != LDB_SUCCESS) {
5036 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5037 ldb_dn_get_linearized(conflict_dn),
5038 ldb_dn_get_linearized(new_dn),
5039 ldb_errstring(ldb_module_get_ctx(ar->module))));
5044 * now we need to ensure that the rename is seen as an
5045 * originating update. We do that with a modify.
5047 ret = replmd_name_modify(ar, req, new_dn);
5048 if (ret != LDB_SUCCESS) {
5052 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5053 ldb_dn_get_linearized(req->op.add.message->dn)));
5056 ret = ldb_build_add_req(&down_req,
5057 ldb_module_get_ctx(ar->module),
5064 if (ret != LDB_SUCCESS) {
5067 LDB_REQ_SET_LOCATION(down_req);
5069 /* current partition control needed by "repmd_op_callback" */
5070 ret = ldb_request_add_control(down_req,
5071 DSDB_CONTROL_CURRENT_PARTITION_OID,
5073 if (ret != LDB_SUCCESS) {
5074 return replmd_replicated_request_error(ar, ret);
5077 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5078 /* this tells the partition module to make it a
5079 partial replica if creating an NC */
5080 ret = ldb_request_add_control(down_req,
5081 DSDB_CONTROL_PARTIAL_REPLICA,
5083 if (ret != LDB_SUCCESS) {
5084 return replmd_replicated_request_error(ar, ret);
5089 * Finally we re-run the add, otherwise the new record won't
5090 * exist, as we are here because of that exact failure!
5092 return ldb_next_request(ar->module, down_req);
5095 /* on failure make the caller get the error. This means
5096 * replication will stop with an error, but there is not much
5099 return ldb_module_done(ar->req, NULL, NULL,
5104 callback for replmd_replicated_apply_add()
5105 This copes with the creation of conflict records in the case where
5106 the DN exists, but with a different objectGUID
5108 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5110 struct replmd_replicated_request *ar =
5111 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5113 if (ar->objs->objects[ar->index_current].last_known_parent) {
5114 /* This is like a conflict DN, where we put the object in LostAndFound
5115 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5116 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5119 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5123 this is called when a new object comes in over DRS
5125 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5127 struct ldb_context *ldb;
5128 struct ldb_request *change_req;
5129 enum ndr_err_code ndr_err;
5130 struct ldb_message *msg;
5131 struct replPropertyMetaDataBlob *md;
5132 struct ldb_val md_value;
5135 bool remote_isDeleted = false;
5138 time_t t = time(NULL);
5139 const struct ldb_val *rdn_val;
5140 struct replmd_private *replmd_private =
5141 talloc_get_type(ldb_module_get_private(ar->module),
5142 struct replmd_private);
5143 unix_to_nt_time(&now, t);
5145 ldb = ldb_module_get_ctx(ar->module);
5146 msg = ar->objs->objects[ar->index_current].msg;
5147 md = ar->objs->objects[ar->index_current].meta_data;
5148 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5150 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5151 if (ret != LDB_SUCCESS) {
5152 return replmd_replicated_request_error(ar, ret);
5155 ret = dsdb_msg_add_guid(msg,
5156 &ar->objs->objects[ar->index_current].object_guid,
5158 if (ret != LDB_SUCCESS) {
5159 return replmd_replicated_request_error(ar, ret);
5162 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5163 if (ret != LDB_SUCCESS) {
5164 return replmd_replicated_request_error(ar, ret);
5167 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5168 if (ret != LDB_SUCCESS) {
5169 return replmd_replicated_request_error(ar, ret);
5172 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5173 if (ret != LDB_SUCCESS) {
5174 return replmd_replicated_request_error(ar, ret);
5177 /* remove any message elements that have zero values */
5178 for (i=0; i<msg->num_elements; i++) {
5179 struct ldb_message_element *el = &msg->elements[i];
5181 if (el->num_values == 0) {
5182 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5183 ldb_asprintf_errstring(ldb, __location__
5184 ": empty objectClass sent on %s, aborting replication\n",
5185 ldb_dn_get_linearized(msg->dn));
5186 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5189 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5191 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5192 msg->num_elements--;
5199 struct GUID_txt_buf guid_txt;
5201 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5204 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5205 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5208 } else if (DEBUGLVL(4)) {
5209 struct GUID_txt_buf guid_txt;
5210 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5211 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5212 ldb_dn_get_linearized(msg->dn)));
5214 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5215 "isDeleted", false);
5218 * the meta data array is already sorted by the caller, except
5219 * for the RDN, which needs to be added.
5223 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5224 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5225 md, ar, now, is_schema_nc,
5227 if (ret != LDB_SUCCESS) {
5228 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5229 return replmd_replicated_request_error(ar, ret);
5232 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5233 if (ret != LDB_SUCCESS) {
5234 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5235 return replmd_replicated_request_error(ar, ret);
5238 for (i=0; i < md->ctr.ctr1.count; i++) {
5239 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5241 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5242 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5243 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5244 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5245 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5247 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5248 if (ret != LDB_SUCCESS) {
5249 return replmd_replicated_request_error(ar, ret);
5252 replmd_ldb_message_sort(msg, ar->schema);
5254 if (!remote_isDeleted) {
5255 ret = dsdb_module_schedule_sd_propagation(ar->module,
5256 ar->objs->partition_dn,
5258 if (ret != LDB_SUCCESS) {
5259 return replmd_replicated_request_error(ar, ret);
5263 ar->isDeleted = remote_isDeleted;
5265 ret = ldb_build_add_req(&change_req,
5271 replmd_op_add_callback,
5273 LDB_REQ_SET_LOCATION(change_req);
5274 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5276 /* current partition control needed by "repmd_op_callback" */
5277 ret = ldb_request_add_control(change_req,
5278 DSDB_CONTROL_CURRENT_PARTITION_OID,
5280 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5282 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5283 /* this tells the partition module to make it a
5284 partial replica if creating an NC */
5285 ret = ldb_request_add_control(change_req,
5286 DSDB_CONTROL_PARTIAL_REPLICA,
5288 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5291 return ldb_next_request(ar->module, change_req);
5294 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5295 struct ldb_reply *ares)
5297 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5298 struct replmd_replicated_request);
5302 return ldb_module_done(ar->req, NULL, NULL,
5303 LDB_ERR_OPERATIONS_ERROR);
5307 * The error NO_SUCH_OBJECT is not expected, unless the search
5308 * base is the partition DN, and that case doesn't happen here
5309 * because then we wouldn't get a parent_guid_value in any
5312 if (ares->error != LDB_SUCCESS) {
5313 return ldb_module_done(ar->req, ares->controls,
5314 ares->response, ares->error);
5317 switch (ares->type) {
5318 case LDB_REPLY_ENTRY:
5320 struct ldb_message *parent_msg = ares->message;
5321 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5322 struct ldb_dn *parent_dn = NULL;
5325 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5326 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5327 /* Per MS-DRSR 4.1.10.6.10
5328 * FindBestParentObject we need to move this
5329 * new object under a deleted object to
5331 struct ldb_dn *nc_root;
5333 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5334 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5335 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5336 "No suitable NC root found for %s. "
5337 "We need to move this object because parent object %s "
5338 "is deleted, but this object is not.",
5339 ldb_dn_get_linearized(msg->dn),
5340 ldb_dn_get_linearized(parent_msg->dn));
5341 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5342 } else if (ret != LDB_SUCCESS) {
5343 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5344 "Unable to find NC root for %s: %s. "
5345 "We need to move this object because parent object %s "
5346 "is deleted, but this object is not.",
5347 ldb_dn_get_linearized(msg->dn),
5348 ldb_errstring(ldb_module_get_ctx(ar->module)),
5349 ldb_dn_get_linearized(parent_msg->dn));
5350 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5353 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5355 DS_GUID_LOSTANDFOUND_CONTAINER,
5357 if (ret != LDB_SUCCESS) {
5358 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5359 "Unable to find LostAndFound Container for %s "
5360 "in partition %s: %s. "
5361 "We need to move this object because parent object %s "
5362 "is deleted, but this object is not.",
5363 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5364 ldb_errstring(ldb_module_get_ctx(ar->module)),
5365 ldb_dn_get_linearized(parent_msg->dn));
5366 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5368 ar->objs->objects[ar->index_current].last_known_parent
5369 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5373 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5376 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5378 comp_num = ldb_dn_get_comp_num(msg->dn);
5380 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5382 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5385 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5387 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5391 case LDB_REPLY_REFERRAL:
5392 /* we ignore referrals */
5395 case LDB_REPLY_DONE:
5397 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5398 struct GUID_txt_buf str_buf;
5399 if (ar->search_msg != NULL) {
5400 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5401 "No parent with GUID %s found for object locally known as %s",
5402 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5403 ldb_dn_get_linearized(ar->search_msg->dn));
5405 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5406 "No parent with GUID %s found for object remotely known as %s",
5407 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5408 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5412 * This error code is really important, as it
5413 * is the flag back to the callers to retry
5414 * this with DRSUAPI_DRS_GET_ANC, and so get
5415 * the parent objects before the child
5418 return ldb_module_done(ar->req, NULL, NULL,
5419 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5422 if (ar->search_msg != NULL) {
5423 ret = replmd_replicated_apply_merge(ar);
5425 ret = replmd_replicated_apply_add(ar);
5427 if (ret != LDB_SUCCESS) {
5428 return ldb_module_done(ar->req, NULL, NULL, ret);
5437 * Look for the parent object, so we put the new object in the right
5438 * place This is akin to NameObject in MS-DRSR - this routine and the
5439 * callbacks find the right parent name, and correct name for this
5443 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5445 struct ldb_context *ldb;
5449 struct ldb_request *search_req;
5450 static const char *attrs[] = {"isDeleted", NULL};
5451 struct GUID_txt_buf guid_str_buf;
5453 ldb = ldb_module_get_ctx(ar->module);
5455 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5456 if (ar->search_msg != NULL) {
5457 return replmd_replicated_apply_merge(ar);
5459 return replmd_replicated_apply_add(ar);
5463 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5466 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5467 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5469 ret = ldb_build_search_req(&search_req,
5472 ar->objs->partition_dn,
5478 replmd_replicated_apply_search_for_parent_callback,
5480 LDB_REQ_SET_LOCATION(search_req);
5482 ret = dsdb_request_add_controls(search_req,
5483 DSDB_SEARCH_SHOW_RECYCLED|
5484 DSDB_SEARCH_SHOW_DELETED|
5485 DSDB_SEARCH_SHOW_EXTENDED_DN);
5486 if (ret != LDB_SUCCESS) {
5490 return ldb_next_request(ar->module, search_req);
5494 handle renames that come in over DRS replication
5496 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5497 struct ldb_message *msg,
5498 struct ldb_request *parent,
5502 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5503 struct ldb_result *res;
5504 struct ldb_dn *conflict_dn;
5505 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5506 const struct ldb_val *omd_value;
5507 struct replPropertyMetaDataBlob omd, *rmd;
5508 enum ndr_err_code ndr_err;
5509 bool rename_incoming_record, rodc;
5510 struct replPropertyMetaData1 *rmd_name, *omd_name;
5511 struct ldb_dn *new_dn;
5514 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5515 ldb_dn_get_linearized(ar->search_msg->dn),
5516 ldb_dn_get_linearized(msg->dn)));
5519 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5520 DSDB_FLAG_NEXT_MODULE, ar->req);
5521 if (ret == LDB_SUCCESS) {
5522 talloc_free(tmp_ctx);
5527 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5528 talloc_free(tmp_ctx);
5529 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5530 ldb_dn_get_linearized(ar->search_msg->dn),
5531 ldb_dn_get_linearized(msg->dn),
5532 ldb_errstring(ldb_module_get_ctx(ar->module)));
5536 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5537 if (ret != LDB_SUCCESS) {
5538 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5539 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5540 ldb_errstring(ldb_module_get_ctx(ar->module)));
5541 return LDB_ERR_OPERATIONS_ERROR;
5544 * we have a conflict, and need to decide if we will keep the
5545 * new record or the old record
5548 conflict_dn = msg->dn;
5552 * We are on an RODC, or were a GC for this
5553 * partition, so we have to fail this until
5554 * someone who owns the partition sorts it
5557 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5558 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5559 " - We must fail the operation until a master for this partition resolves the conflict",
5560 ldb_dn_get_linearized(conflict_dn));
5565 * first we need the replPropertyMetaData attribute from the
5568 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5570 DSDB_FLAG_NEXT_MODULE |
5571 DSDB_SEARCH_SHOW_DELETED |
5572 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5573 if (ret != LDB_SUCCESS) {
5574 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5575 ldb_dn_get_linearized(conflict_dn)));
5579 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5580 if (omd_value == NULL) {
5581 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5582 ldb_dn_get_linearized(conflict_dn)));
5586 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5587 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5588 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5589 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5590 ldb_dn_get_linearized(conflict_dn)));
5594 rmd = ar->objs->objects[ar->index_current].meta_data;
5597 * we decide which is newer based on the RPMD on the name
5598 * attribute. See [MS-DRSR] ResolveNameConflict.
5600 * We expect omd_name to be present, as this is from a local
5601 * search, but while rmd_name should have been given to us by
5602 * the remote server, if it is missing we just prefer the
5604 * replmd_replPropertyMetaData1_new_should_be_taken()
5606 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5607 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5609 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5610 ldb_dn_get_linearized(conflict_dn)));
5615 * Should we preserve the current record, and so rename the
5616 * incoming record to be a conflict?
5618 rename_incoming_record =
5619 !replmd_replPropertyMetaData1_new_should_be_taken(
5620 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5621 omd_name, rmd_name);
5623 if (rename_incoming_record) {
5625 new_dn = replmd_conflict_dn(msg,
5626 ldb_module_get_ctx(ar->module),
5628 &ar->objs->objects[ar->index_current].object_guid);
5629 if (new_dn == NULL) {
5630 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5631 "Failed to form conflict DN for %s\n",
5632 ldb_dn_get_linearized(msg->dn));
5634 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5637 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5638 DSDB_FLAG_NEXT_MODULE, ar->req);
5639 if (ret != LDB_SUCCESS) {
5640 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5641 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5642 ldb_dn_get_linearized(conflict_dn),
5643 ldb_dn_get_linearized(ar->search_msg->dn),
5644 ldb_dn_get_linearized(new_dn),
5645 ldb_errstring(ldb_module_get_ctx(ar->module)));
5646 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5654 /* we are renaming the existing record */
5656 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5657 if (GUID_all_zero(&guid)) {
5658 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5659 ldb_dn_get_linearized(conflict_dn)));
5663 new_dn = replmd_conflict_dn(tmp_ctx,
5664 ldb_module_get_ctx(ar->module),
5665 conflict_dn, &guid);
5666 if (new_dn == NULL) {
5667 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5668 ldb_dn_get_linearized(conflict_dn)));
5672 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5673 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5675 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5676 DSDB_FLAG_OWN_MODULE, ar->req);
5677 if (ret != LDB_SUCCESS) {
5678 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5679 ldb_dn_get_linearized(conflict_dn),
5680 ldb_dn_get_linearized(new_dn),
5681 ldb_errstring(ldb_module_get_ctx(ar->module))));
5686 * now we need to ensure that the rename is seen as an
5687 * originating update. We do that with a modify.
5689 ret = replmd_name_modify(ar, ar->req, new_dn);
5690 if (ret != LDB_SUCCESS) {
5694 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5695 ldb_dn_get_linearized(ar->search_msg->dn),
5696 ldb_dn_get_linearized(msg->dn)));
5699 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5700 DSDB_FLAG_NEXT_MODULE, ar->req);
5701 if (ret != LDB_SUCCESS) {
5702 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5703 ldb_dn_get_linearized(ar->search_msg->dn),
5704 ldb_dn_get_linearized(msg->dn),
5705 ldb_errstring(ldb_module_get_ctx(ar->module))));
5711 * On failure make the caller get the error
5712 * This means replication will stop with an error,
5713 * but there is not much else we can do. In the
5714 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5718 talloc_free(tmp_ctx);
5723 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5725 struct ldb_context *ldb;
5726 struct ldb_request *change_req;
5727 enum ndr_err_code ndr_err;
5728 struct ldb_message *msg;
5729 struct replPropertyMetaDataBlob *rmd;
5730 struct replPropertyMetaDataBlob omd;
5731 const struct ldb_val *omd_value;
5732 struct replPropertyMetaDataBlob nmd;
5733 struct ldb_val nmd_value;
5734 struct GUID remote_parent_guid;
5737 unsigned int removed_attrs = 0;
5739 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5740 bool isDeleted = false;
5741 bool local_isDeleted = false;
5742 bool remote_isDeleted = false;
5743 bool take_remote_isDeleted = false;
5744 bool sd_updated = false;
5745 bool renamed = false;
5746 bool is_schema_nc = false;
5748 const struct ldb_val *old_rdn, *new_rdn;
5749 struct replmd_private *replmd_private =
5750 talloc_get_type(ldb_module_get_private(ar->module),
5751 struct replmd_private);
5753 time_t t = time(NULL);
5754 unix_to_nt_time(&now, t);
5756 ldb = ldb_module_get_ctx(ar->module);
5757 msg = ar->objs->objects[ar->index_current].msg;
5759 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5761 rmd = ar->objs->objects[ar->index_current].meta_data;
5765 /* find existing meta data */
5766 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5768 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5769 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5770 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5771 nt_status = ndr_map_error2ntstatus(ndr_err);
5772 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5775 if (omd.version != 1) {
5776 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5781 struct GUID_txt_buf guid_txt;
5783 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5784 LDB_CHANGETYPE_MODIFY, msg);
5785 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
5788 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5790 ndr_print_struct_string(s,
5791 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5792 "existing replPropertyMetaData",
5794 ndr_print_struct_string(s,
5795 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
5796 "incoming replPropertyMetaData",
5799 } else if (DEBUGLVL(4)) {
5800 struct GUID_txt_buf guid_txt;
5802 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
5803 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5805 ldb_dn_get_linearized(msg->dn)));
5808 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5809 "isDeleted", false);
5810 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5811 "isDeleted", false);
5814 * Fill in the remote_parent_guid with the GUID or an all-zero
5817 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5818 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5820 remote_parent_guid = GUID_zero();
5824 * To ensure we follow a complex rename chain around, we have
5825 * to confirm that the DN is the same (mostly to confirm the
5826 * RDN) and the parentGUID is the same.
5828 * This ensures we keep things under the correct parent, which
5829 * replmd_replicated_handle_rename() will do.
5832 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5833 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5837 * handle renames, even just by case that come in over
5838 * DRS. Changes in the parent DN don't hit us here,
5839 * because the search for a parent will clean up those
5842 * We also have already filtered out the case where
5843 * the peer has an older name to what we have (see
5844 * replmd_replicated_apply_search_callback())
5846 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5849 if (ret != LDB_SUCCESS) {
5850 ldb_debug(ldb, LDB_DEBUG_FATAL,
5851 "replmd_replicated_request rename %s => %s failed - %s\n",
5852 ldb_dn_get_linearized(ar->search_msg->dn),
5853 ldb_dn_get_linearized(msg->dn),
5854 ldb_errstring(ldb));
5855 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5858 if (renamed == true) {
5860 * Set the callback to one that will fix up the name
5861 * metadata on the new conflict DN
5863 callback = replmd_op_name_modify_callback;
5868 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5869 nmd.ctr.ctr1.array = talloc_array(ar,
5870 struct replPropertyMetaData1,
5871 nmd.ctr.ctr1.count);
5872 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5874 /* first copy the old meta data */
5875 for (i=0; i < omd.ctr.ctr1.count; i++) {
5876 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5881 /* now merge in the new meta data */
5882 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5885 for (j=0; j < ni; j++) {
5888 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5892 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5893 ar->objs->dsdb_repl_flags,
5894 &nmd.ctr.ctr1.array[j],
5895 &rmd->ctr.ctr1.array[i]);
5897 /* replace the entry */
5898 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5899 if (ar->seq_num == 0) {
5900 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5901 if (ret != LDB_SUCCESS) {
5902 return replmd_replicated_request_error(ar, ret);
5905 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5906 switch (nmd.ctr.ctr1.array[j].attid) {
5907 case DRSUAPI_ATTID_ntSecurityDescriptor:
5910 case DRSUAPI_ATTID_isDeleted:
5911 take_remote_isDeleted = true;
5920 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5921 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5922 msg->elements[i-removed_attrs].name,
5923 ldb_dn_get_linearized(msg->dn),
5924 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5927 /* we don't want to apply this change so remove the attribute */
5928 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5935 if (found) continue;
5937 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5938 if (ar->seq_num == 0) {
5939 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5940 if (ret != LDB_SUCCESS) {
5941 return replmd_replicated_request_error(ar, ret);
5944 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5945 switch (nmd.ctr.ctr1.array[ni].attid) {
5946 case DRSUAPI_ATTID_ntSecurityDescriptor:
5949 case DRSUAPI_ATTID_isDeleted:
5950 take_remote_isDeleted = true;
5959 * finally correct the size of the meta_data array
5961 nmd.ctr.ctr1.count = ni;
5963 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5964 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5967 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5968 &nmd, ar, now, is_schema_nc,
5970 if (ret != LDB_SUCCESS) {
5971 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5972 return replmd_replicated_request_error(ar, ret);
5976 * sort the new meta data array
5978 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5979 if (ret != LDB_SUCCESS) {
5980 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5985 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5988 * This also controls SD propagation below
5990 if (take_remote_isDeleted) {
5991 isDeleted = remote_isDeleted;
5993 isDeleted = local_isDeleted;
5996 ar->isDeleted = isDeleted;
5999 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6001 if (msg->num_elements == 0) {
6002 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6005 return replmd_replicated_apply_isDeleted(ar);
6008 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6009 ar->index_current, msg->num_elements);
6015 if (sd_updated && !isDeleted) {
6016 ret = dsdb_module_schedule_sd_propagation(ar->module,
6017 ar->objs->partition_dn,
6019 if (ret != LDB_SUCCESS) {
6020 return ldb_operr(ldb);
6024 /* create the meta data value */
6025 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6026 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6027 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6028 nt_status = ndr_map_error2ntstatus(ndr_err);
6029 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6033 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6034 * and replPopertyMetaData attributes
6036 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6037 if (ret != LDB_SUCCESS) {
6038 return replmd_replicated_request_error(ar, ret);
6040 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6041 if (ret != LDB_SUCCESS) {
6042 return replmd_replicated_request_error(ar, ret);
6044 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6045 if (ret != LDB_SUCCESS) {
6046 return replmd_replicated_request_error(ar, ret);
6049 replmd_ldb_message_sort(msg, ar->schema);
6051 /* we want to replace the old values */
6052 for (i=0; i < msg->num_elements; i++) {
6053 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6054 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6055 if (msg->elements[i].num_values == 0) {
6056 ldb_asprintf_errstring(ldb, __location__
6057 ": objectClass removed on %s, aborting replication\n",
6058 ldb_dn_get_linearized(msg->dn));
6059 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6065 struct GUID_txt_buf guid_txt;
6067 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6068 LDB_CHANGETYPE_MODIFY,
6070 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6071 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6075 } else if (DEBUGLVL(4)) {
6076 struct GUID_txt_buf guid_txt;
6078 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6079 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6081 ldb_dn_get_linearized(msg->dn)));
6084 ret = ldb_build_mod_req(&change_req,
6092 LDB_REQ_SET_LOCATION(change_req);
6093 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6095 /* current partition control needed by "repmd_op_callback" */
6096 ret = ldb_request_add_control(change_req,
6097 DSDB_CONTROL_CURRENT_PARTITION_OID,
6099 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6101 return ldb_next_request(ar->module, change_req);
6104 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6105 struct ldb_reply *ares)
6107 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6108 struct replmd_replicated_request);
6112 return ldb_module_done(ar->req, NULL, NULL,
6113 LDB_ERR_OPERATIONS_ERROR);
6115 if (ares->error != LDB_SUCCESS &&
6116 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6117 return ldb_module_done(ar->req, ares->controls,
6118 ares->response, ares->error);
6121 switch (ares->type) {
6122 case LDB_REPLY_ENTRY:
6123 ar->search_msg = talloc_steal(ar, ares->message);
6126 case LDB_REPLY_REFERRAL:
6127 /* we ignore referrals */
6130 case LDB_REPLY_DONE:
6132 struct replPropertyMetaData1 *md_remote;
6133 struct replPropertyMetaData1 *md_local;
6135 struct replPropertyMetaDataBlob omd;
6136 const struct ldb_val *omd_value;
6137 struct replPropertyMetaDataBlob *rmd;
6138 struct ldb_message *msg;
6140 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6141 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6144 * This is the ADD case, find the appropriate parent,
6145 * as this object doesn't exist locally:
6147 if (ar->search_msg == NULL) {
6148 ret = replmd_replicated_apply_search_for_parent(ar);
6149 if (ret != LDB_SUCCESS) {
6150 return ldb_module_done(ar->req, NULL, NULL, ret);
6157 * Otherwise, in the MERGE case, work out if we are
6158 * attempting a rename, and if so find the parent the
6159 * newly renamed object wants to belong under (which
6160 * may not be the parent in it's attached string DN
6162 rmd = ar->objs->objects[ar->index_current].meta_data;
6166 /* find existing meta data */
6167 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6169 enum ndr_err_code ndr_err;
6170 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6171 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6172 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6173 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6174 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6177 if (omd.version != 1) {
6178 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6182 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6184 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6185 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6186 && GUID_all_zero(&ar->local_parent_guid)) {
6187 DEBUG(0, ("Refusing to replicate new version of %s "
6188 "as local object has an all-zero parentGUID attribute, "
6189 "despite not being an NC root\n",
6190 ldb_dn_get_linearized(ar->search_msg->dn)));
6191 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6195 * now we need to check for double renames. We could have a
6196 * local rename pending which our replication partner hasn't
6197 * received yet. We choose which one wins by looking at the
6198 * attribute stamps on the two objects, the newer one wins.
6200 * This also simply applies the correct algorithms for
6201 * determining if a change was made to name at all, or
6202 * if the object has just been renamed under the same
6205 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6206 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6208 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6209 ldb_dn_get_linearized(ar->search_msg->dn)));
6210 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6214 * if there is no name attribute given then we have to assume the
6215 * object we've received has the older name
6217 if (replmd_replPropertyMetaData1_new_should_be_taken(
6218 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6219 md_local, md_remote)) {
6220 struct GUID_txt_buf p_guid_local;
6221 struct GUID_txt_buf p_guid_remote;
6222 msg = ar->objs->objects[ar->index_current].msg;
6224 /* Merge on the existing object, with rename */
6226 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6227 "as incoming object changing to %s under %s\n",
6228 ldb_dn_get_linearized(ar->search_msg->dn),
6229 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6230 ldb_dn_get_linearized(msg->dn),
6231 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6233 ret = replmd_replicated_apply_search_for_parent(ar);
6235 struct GUID_txt_buf p_guid_local;
6236 struct GUID_txt_buf p_guid_remote;
6237 msg = ar->objs->objects[ar->index_current].msg;
6240 * Merge on the existing object, force no
6241 * rename (code below just to explain why in
6245 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6246 ldb_dn_get_linearized(msg->dn)) == 0) {
6247 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6248 GUID_equal(&ar->local_parent_guid,
6249 ar->objs->objects[ar->index_current].parent_guid)
6251 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6252 "despite incoming object changing parent to %s\n",
6253 ldb_dn_get_linearized(ar->search_msg->dn),
6254 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6255 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6259 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6260 " and rejecting older rename to %s under %s\n",
6261 ldb_dn_get_linearized(ar->search_msg->dn),
6262 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6263 ldb_dn_get_linearized(msg->dn),
6264 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6268 * This assignment ensures that the strcmp()
6269 * and GUID_equal() calls in
6270 * replmd_replicated_apply_merge() avoids the
6273 ar->objs->objects[ar->index_current].parent_guid =
6274 &ar->local_parent_guid;
6276 msg->dn = ar->search_msg->dn;
6277 ret = replmd_replicated_apply_merge(ar);
6279 if (ret != LDB_SUCCESS) {
6280 return ldb_module_done(ar->req, NULL, NULL, ret);
6290 * Stores the linked attributes received in the replication chunk - these get
6291 * applied at the end of the transaction. We also check that each linked
6292 * attribute is valid, i.e. source and target objects are known.
6294 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6296 int ret = LDB_SUCCESS;
6298 struct ldb_module *module = ar->module;
6299 struct replmd_private *replmd_private =
6300 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6301 struct ldb_context *ldb;
6303 ldb = ldb_module_get_ctx(module);
6305 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6307 /* save away the linked attributes for the end of the transaction */
6308 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6309 struct la_entry *la_entry;
6311 if (replmd_private->la_ctx == NULL) {
6312 replmd_private->la_ctx = talloc_new(replmd_private);
6314 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6315 if (la_entry == NULL) {
6317 return LDB_ERR_OPERATIONS_ERROR;
6319 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6320 if (la_entry->la == NULL) {
6321 talloc_free(la_entry);
6323 return LDB_ERR_OPERATIONS_ERROR;
6325 *la_entry->la = ar->objs->linked_attributes[i];
6326 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6328 /* we need to steal the non-scalars so they stay
6329 around until the end of the transaction */
6330 talloc_steal(la_entry->la, la_entry->la->identifier);
6331 talloc_steal(la_entry->la, la_entry->la->value.blob);
6333 ret = replmd_verify_linked_attribute(ar, la_entry);
6335 if (ret != LDB_SUCCESS) {
6339 DLIST_ADD(replmd_private->la_list, la_entry);
6345 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6347 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6349 struct ldb_context *ldb;
6353 struct ldb_request *search_req;
6354 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6355 "parentGUID", "instanceType",
6356 "replPropertyMetaData", "nTSecurityDescriptor",
6357 "isDeleted", NULL };
6358 struct GUID_txt_buf guid_str_buf;
6360 if (ar->index_current >= ar->objs->num_objects) {
6363 * Now that we've applied all the objects, check the new linked
6364 * attributes and store them (we apply them in .prepare_commit)
6366 ret = replmd_store_linked_attributes(ar);
6368 if (ret != LDB_SUCCESS) {
6372 /* done applying objects, move on to the next stage */
6373 return replmd_replicated_uptodate_vector(ar);
6376 ldb = ldb_module_get_ctx(ar->module);
6377 ar->search_msg = NULL;
6378 ar->isDeleted = false;
6380 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6383 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6384 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6386 ret = ldb_build_search_req(&search_req,
6389 ar->objs->partition_dn,
6395 replmd_replicated_apply_search_callback,
6397 LDB_REQ_SET_LOCATION(search_req);
6399 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6401 if (ret != LDB_SUCCESS) {
6405 return ldb_next_request(ar->module, search_req);
6409 * This is essentially a wrapper for replmd_replicated_apply_next()
6411 * This is needed to ensure that both codepaths call this handler.
6413 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6415 struct ldb_dn *deleted_objects_dn;
6416 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6417 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6418 &deleted_objects_dn);
6419 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6421 * Do a delete here again, so that if there is
6422 * anything local that conflicts with this
6423 * object being deleted, it is removed. This
6424 * includes links. See MS-DRSR 4.1.10.6.9
6427 * If the object is already deleted, and there
6428 * is no more work required, it doesn't do
6432 /* This has been updated to point to the DN we eventually did the modify on */
6434 struct ldb_request *del_req;
6435 struct ldb_result *res;
6437 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6439 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6443 res = talloc_zero(tmp_ctx, struct ldb_result);
6445 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6446 talloc_free(tmp_ctx);
6450 /* Build a delete request, which hopefully will artually turn into nothing */
6451 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6455 ldb_modify_default_callback,
6457 LDB_REQ_SET_LOCATION(del_req);
6458 if (ret != LDB_SUCCESS) {
6459 talloc_free(tmp_ctx);
6464 * This is the guts of the call, call back
6465 * into our delete code, but setting the
6466 * re_delete flag so we delete anything that
6467 * shouldn't be there on a deleted or recycled
6470 ret = replmd_delete_internals(ar->module, del_req, true);
6471 if (ret == LDB_SUCCESS) {
6472 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6475 talloc_free(tmp_ctx);
6476 if (ret != LDB_SUCCESS) {
6481 ar->index_current++;
6482 return replmd_replicated_apply_next(ar);
6485 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6486 struct ldb_reply *ares)
6488 struct ldb_context *ldb;
6489 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6490 struct replmd_replicated_request);
6491 ldb = ldb_module_get_ctx(ar->module);
6494 return ldb_module_done(ar->req, NULL, NULL,
6495 LDB_ERR_OPERATIONS_ERROR);
6497 if (ares->error != LDB_SUCCESS) {
6498 return ldb_module_done(ar->req, ares->controls,
6499 ares->response, ares->error);
6502 if (ares->type != LDB_REPLY_DONE) {
6503 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6504 return ldb_module_done(ar->req, NULL, NULL,
6505 LDB_ERR_OPERATIONS_ERROR);
6510 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6513 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6515 struct ldb_context *ldb;
6516 struct ldb_request *change_req;
6517 enum ndr_err_code ndr_err;
6518 struct ldb_message *msg;
6519 struct replUpToDateVectorBlob ouv;
6520 const struct ldb_val *ouv_value;
6521 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6522 struct replUpToDateVectorBlob nuv;
6523 struct ldb_val nuv_value;
6524 struct ldb_message_element *nuv_el = NULL;
6525 struct ldb_message_element *orf_el = NULL;
6526 struct repsFromToBlob nrf;
6527 struct ldb_val *nrf_value = NULL;
6528 struct ldb_message_element *nrf_el = NULL;
6532 time_t t = time(NULL);
6535 uint32_t instanceType;
6537 ldb = ldb_module_get_ctx(ar->module);
6538 ruv = ar->objs->uptodateness_vector;
6544 unix_to_nt_time(&now, t);
6546 if (ar->search_msg == NULL) {
6547 /* this happens for a REPL_OBJ call where we are
6548 creating the target object by replicating it. The
6549 subdomain join code does this for the partition DN
6551 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6552 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6555 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6556 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6557 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6558 ldb_dn_get_linearized(ar->search_msg->dn)));
6559 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6563 * first create the new replUpToDateVector
6565 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6567 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6568 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6569 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6570 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6571 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6574 if (ouv.version != 2) {
6575 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6580 * the new uptodateness vector will at least
6581 * contain 1 entry, one for the source_dsa
6583 * plus optional values from our old vector and the one from the source_dsa
6585 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6586 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6587 nuv.ctr.ctr2.cursors = talloc_array(ar,
6588 struct drsuapi_DsReplicaCursor2,
6589 nuv.ctr.ctr2.count);
6590 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6592 /* first copy the old vector */
6593 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6594 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6598 /* merge in the source_dsa vector is available */
6599 for (i=0; (ruv && i < ruv->count); i++) {
6602 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6603 &ar->our_invocation_id)) {
6607 for (j=0; j < ni; j++) {
6608 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6609 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6615 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6616 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6621 if (found) continue;
6623 /* if it's not there yet, add it */
6624 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6629 * finally correct the size of the cursors array
6631 nuv.ctr.ctr2.count = ni;
6636 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6639 * create the change ldb_message
6641 msg = ldb_msg_new(ar);
6642 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6643 msg->dn = ar->search_msg->dn;
6645 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6646 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6647 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6648 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6649 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6651 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6652 if (ret != LDB_SUCCESS) {
6653 return replmd_replicated_request_error(ar, ret);
6655 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6658 * now create the new repsFrom value from the given repsFromTo1 structure
6662 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6663 nrf.ctr.ctr1.last_attempt = now;
6664 nrf.ctr.ctr1.last_success = now;
6665 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6668 * first see if we already have a repsFrom value for the current source dsa
6669 * if so we'll later replace this value
6671 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6673 for (i=0; i < orf_el->num_values; i++) {
6674 struct repsFromToBlob *trf;
6676 trf = talloc(ar, struct repsFromToBlob);
6677 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6679 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6680 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6681 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6682 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6683 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6686 if (trf->version != 1) {
6687 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6691 * we compare the source dsa objectGUID not the invocation_id
6692 * because we want only one repsFrom value per source dsa
6693 * and when the invocation_id of the source dsa has changed we don't need
6694 * the old repsFrom with the old invocation_id
6696 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6697 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6703 nrf_value = &orf_el->values[i];
6708 * copy over all old values to the new ldb_message
6710 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6711 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6716 * if we haven't found an old repsFrom value for the current source dsa
6717 * we'll add a new value
6720 struct ldb_val zero_value;
6721 ZERO_STRUCT(zero_value);
6722 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
6723 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6725 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
6728 /* we now fill the value which is already attached to ldb_message */
6729 ndr_err = ndr_push_struct_blob(nrf_value, msg,
6731 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
6732 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6733 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6734 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6738 * the ldb_message_element for the attribute, has all the old values and the new one
6739 * so we'll replace the whole attribute with all values
6741 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
6743 if (CHECK_DEBUGLVL(4)) {
6744 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6745 LDB_CHANGETYPE_MODIFY,
6747 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
6751 /* prepare the ldb_modify() request */
6752 ret = ldb_build_mod_req(&change_req,
6758 replmd_replicated_uptodate_modify_callback,
6760 LDB_REQ_SET_LOCATION(change_req);
6761 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6763 return ldb_next_request(ar->module, change_req);
6766 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
6767 struct ldb_reply *ares)
6769 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6770 struct replmd_replicated_request);
6774 return ldb_module_done(ar->req, NULL, NULL,
6775 LDB_ERR_OPERATIONS_ERROR);
6777 if (ares->error != LDB_SUCCESS &&
6778 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6779 return ldb_module_done(ar->req, ares->controls,
6780 ares->response, ares->error);
6783 switch (ares->type) {
6784 case LDB_REPLY_ENTRY:
6785 ar->search_msg = talloc_steal(ar, ares->message);
6788 case LDB_REPLY_REFERRAL:
6789 /* we ignore referrals */
6792 case LDB_REPLY_DONE:
6793 ret = replmd_replicated_uptodate_modify(ar);
6794 if (ret != LDB_SUCCESS) {
6795 return ldb_module_done(ar->req, NULL, NULL, ret);
6804 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
6806 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
6807 struct replmd_private *replmd_private =
6808 talloc_get_type_abort(ldb_module_get_private(ar->module),
6809 struct replmd_private);
6811 static const char *attrs[] = {
6812 "replUpToDateVector",
6817 struct ldb_request *search_req;
6819 ar->search_msg = NULL;
6822 * Let the caller know that we did an originating updates
6824 ar->objs->originating_updates = replmd_private->originating_updates;
6826 ret = ldb_build_search_req(&search_req,
6829 ar->objs->partition_dn,
6835 replmd_replicated_uptodate_search_callback,
6837 LDB_REQ_SET_LOCATION(search_req);
6838 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6840 return ldb_next_request(ar->module, search_req);
6845 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
6847 struct ldb_context *ldb;
6848 struct dsdb_extended_replicated_objects *objs;
6849 struct replmd_replicated_request *ar;
6850 struct ldb_control **ctrls;
6853 ldb = ldb_module_get_ctx(module);
6855 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
6857 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
6859 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
6860 return LDB_ERR_PROTOCOL_ERROR;
6863 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
6864 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
6865 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
6866 return LDB_ERR_PROTOCOL_ERROR;
6869 ar = replmd_ctx_init(module, req);
6871 return LDB_ERR_OPERATIONS_ERROR;
6873 /* Set the flags to have the replmd_op_callback run over the full set of objects */
6874 ar->apply_mode = true;
6876 ar->schema = dsdb_get_schema(ldb, ar);
6878 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
6880 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
6881 return LDB_ERR_CONSTRAINT_VIOLATION;
6884 ctrls = req->controls;
6886 if (req->controls) {
6887 req->controls = talloc_memdup(ar, req->controls,
6888 talloc_get_size(req->controls));
6889 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6892 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6893 if (ret != LDB_SUCCESS) {
6897 /* If this change contained linked attributes in the body
6898 * (rather than in the links section) we need to update
6899 * backlinks in linked_attributes */
6900 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6901 if (ret != LDB_SUCCESS) {
6905 ar->controls = req->controls;
6906 req->controls = ctrls;
6908 return replmd_replicated_apply_next(ar);
6912 * Checks how to handle an missing target - either we need to fail the
6913 * replication and retry with GET_TGT, ignore the link and continue, or try to
6914 * add a partial link to an unknown target.
6916 static int replmd_allow_missing_target(struct ldb_module *module,
6917 TALLOC_CTX *mem_ctx,
6918 struct ldb_dn *target_dn,
6919 struct ldb_dn *source_dn,
6922 uint32_t dsdb_repl_flags,
6924 const char * missing_str)
6926 struct ldb_context *ldb = ldb_module_get_ctx(module);
6930 * we may not be able to resolve link targets properly when
6931 * dealing with subsets of objects, e.g. the source is a
6932 * critical object and the target isn't
6935 * When we implement Trusted Domains we need to consider
6936 * whether they get treated as an incomplete replica here or not
6938 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
6941 * Ignore the link. We don't increase the highwater-mark in
6942 * the object subset cases, so subsequent replications should
6943 * resolve any missing links
6945 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
6946 ldb_dn_get_linearized(target_dn),
6947 ldb_dn_get_linearized(source_dn)));
6948 *ignore_link = true;
6952 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
6955 * target should already be up-to-date so there's no point in
6956 * retrying. This could be due to bad timing, or if a target
6957 * on a one-way link was deleted. We ignore the link rather
6958 * than failing the replication cycle completely
6960 *ignore_link = true;
6961 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
6962 ldb_dn_get_linearized(target_dn), missing_str,
6963 ldb_dn_get_linearized(source_dn));
6967 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
6971 if (is_in_same_nc) {
6972 /* fail the replication and retry with GET_TGT */
6973 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
6975 ldb_dn_get_linearized(target_dn),
6976 GUID_string(mem_ctx, guid),
6977 ldb_dn_get_linearized(source_dn));
6978 return LDB_ERR_NO_SUCH_OBJECT;
6982 * The target of the cross-partition link is missing. Continue
6983 * and try to at least add the forward-link. This isn't great,
6984 * but a partial link can be fixed by dbcheck, so it's better
6985 * than dropping the link completely.
6987 *ignore_link = false;
6989 if (is_obj_commit) {
6992 * Only log this when we're actually committing the objects.
6993 * This avoids spurious logs, i.e. if we're just verifying the
6994 * received link during a join.
6996 DBG_WARNING("%s cross-partition target %s linked from %s\n",
6997 missing_str, ldb_dn_get_linearized(target_dn),
6998 ldb_dn_get_linearized(source_dn));
7005 * Checks that the target object for a linked attribute exists.
7006 * @param guid returns the target object's GUID (is returned)if it exists)
7007 * @param ignore_link set to true if the linked attribute should be ignored
7008 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7010 static int replmd_check_target_exists(struct ldb_module *module,
7011 struct dsdb_dn *dsdb_dn,
7012 struct la_entry *la_entry,
7013 struct ldb_dn *source_dn,
7018 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7019 struct ldb_context *ldb = ldb_module_get_ctx(module);
7020 struct ldb_result *target_res;
7021 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7022 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7025 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7026 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7028 *ignore_link = false;
7029 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7031 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7034 * This strange behaviour (allowing a NULL/missing
7035 * GUID) originally comes from:
7037 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7038 * Author: Andrew Tridgell <tridge@samba.org>
7039 * Date: Mon Dec 21 21:21:55 2009 +1100
7041 * s4-drs: cope better with NULL GUIDS from DRS
7043 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7044 * need to match by DN if possible when seeing if we should update an
7047 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7049 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7051 DSDB_FLAG_NEXT_MODULE |
7052 DSDB_SEARCH_SHOW_RECYCLED |
7053 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7054 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7056 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7057 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7059 ldb_dn_get_linearized(dsdb_dn->dn),
7060 ldb_dn_get_linearized(source_dn));
7061 talloc_free(tmp_ctx);
7062 return LDB_ERR_OPERATIONS_ERROR;
7064 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7065 NULL, LDB_SCOPE_SUBTREE,
7067 DSDB_FLAG_NEXT_MODULE |
7068 DSDB_SEARCH_SHOW_RECYCLED |
7069 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7070 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7073 GUID_string(tmp_ctx, guid));
7076 if (ret != LDB_SUCCESS) {
7077 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7078 GUID_string(tmp_ctx, guid),
7079 ldb_errstring(ldb));
7080 talloc_free(tmp_ctx);
7084 if (target_res->count == 0) {
7087 * target object is unknown. Check whether to ignore the link,
7088 * fail the replication, or add a partial link
7090 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7091 source_dn, is_obj_commit, guid,
7092 la_entry->dsdb_repl_flags,
7093 ignore_link, "Unknown");
7095 } else if (target_res->count != 1) {
7096 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7097 GUID_string(tmp_ctx, guid));
7098 ret = LDB_ERR_OPERATIONS_ERROR;
7100 struct ldb_message *target_msg = target_res->msgs[0];
7102 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7104 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7105 replmd_deletion_state(module, target_msg,
7106 &target_deletion_state, NULL);
7109 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7110 * ProcessLinkValue(). Link updates should not be sent for
7111 * recycled and tombstone objects (deleting the links should
7112 * happen when we delete the object). This probably means our
7113 * copy of the target object isn't up to date.
7115 if (target_deletion_state >= OBJECT_RECYCLED) {
7118 * target object is deleted. Check whether to ignore the
7119 * link, fail the replication, or add a partial link
7121 ret = replmd_allow_missing_target(module, tmp_ctx,
7122 dsdb_dn->dn, source_dn,
7123 is_obj_commit, guid,
7124 la_entry->dsdb_repl_flags,
7125 ignore_link, "Deleted");
7129 talloc_free(tmp_ctx);
7134 * Extracts the key details about the source/target object for a
7135 * linked-attribute entry.
7136 * This returns the following details:
7137 * @param ret_attr the schema details for the linked attribute
7138 * @param source_msg the search result for the source object
7139 * @param target_dsdb_dn the unpacked DN info for the target object
7141 static int replmd_extract_la_entry_details(struct ldb_module *module,
7142 struct la_entry *la_entry,
7143 TALLOC_CTX *mem_ctx,
7144 const struct dsdb_attribute **ret_attr,
7145 struct ldb_message **source_msg,
7146 struct dsdb_dn **target_dsdb_dn)
7148 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7149 struct ldb_context *ldb = ldb_module_get_ctx(module);
7150 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7152 const struct dsdb_attribute *attr;
7154 struct ldb_result *res;
7155 const char *attrs[4];
7158 linked_attributes[0]:
7159 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7161 identifier: struct drsuapi_DsReplicaObjectIdentifier
7162 __ndr_size : 0x0000003a (58)
7163 __ndr_size_sid : 0x00000000 (0)
7164 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7166 __ndr_size_dn : 0x00000000 (0)
7168 attid : DRSUAPI_ATTID_member (0x1F)
7169 value: struct drsuapi_DsAttributeValue
7170 __ndr_size : 0x0000007e (126)
7172 blob : DATA_BLOB length=126
7173 flags : 0x00000001 (1)
7174 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7175 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7176 meta_data: struct drsuapi_DsReplicaMetaData
7177 version : 0x00000015 (21)
7178 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7179 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7180 originating_usn : 0x000000000001e19c (123292)
7182 (for cases where the link is to a normal DN)
7183 &target: struct drsuapi_DsReplicaObjectIdentifier3
7184 __ndr_size : 0x0000007e (126)
7185 __ndr_size_sid : 0x0000001c (28)
7186 guid : 7639e594-db75-4086-b0d4-67890ae46031
7187 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7188 __ndr_size_dn : 0x00000022 (34)
7189 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7192 /* find the attribute being modified */
7193 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7195 struct GUID_txt_buf guid_str;
7196 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7198 GUID_buf_string(&la->identifier->guid,
7200 return LDB_ERR_OPERATIONS_ERROR;
7203 attrs[0] = attr->lDAPDisplayName;
7204 attrs[1] = "isDeleted";
7205 attrs[2] = "isRecycled";
7209 * get the existing message from the db for the object with
7210 * this GUID, returning attribute being modified. We will then
7211 * use this msg as the basis for a modify call
7213 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7214 DSDB_FLAG_NEXT_MODULE |
7215 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7216 DSDB_SEARCH_SHOW_RECYCLED |
7217 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7218 DSDB_SEARCH_REVEAL_INTERNALS,
7220 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7221 if (ret != LDB_SUCCESS) {
7224 if (res->count != 1) {
7225 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7226 GUID_string(mem_ctx, &la->identifier->guid));
7227 return LDB_ERR_NO_SUCH_OBJECT;
7230 *source_msg = res->msgs[0];
7232 /* the value blob for the attribute holds the target object DN */
7233 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
7234 if (!W_ERROR_IS_OK(status)) {
7235 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7236 attr->lDAPDisplayName,
7237 ldb_dn_get_linearized(res->msgs[0]->dn),
7238 win_errstr(status));
7239 return LDB_ERR_OPERATIONS_ERROR;
7248 * Verifies the source and target objects are known for a linked attribute
7250 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7251 struct la_entry *la)
7253 int ret = LDB_SUCCESS;
7254 TALLOC_CTX *tmp_ctx = talloc_new(la);
7255 struct ldb_module *module = ar->module;
7256 struct ldb_message *src_msg;
7257 const struct dsdb_attribute *attr;
7258 struct dsdb_dn *tgt_dsdb_dn;
7259 struct GUID guid = GUID_zero();
7262 ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
7263 &src_msg, &tgt_dsdb_dn);
7266 * When we fail to find the source object, the error code we pass
7267 * back here is really important. It flags back to the callers to
7268 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7269 * never happen if we're replicating from a Samba DC, but it is
7270 * needed to talk to a Windows DC
7272 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7273 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7276 if (ret != LDB_SUCCESS) {
7277 talloc_free(tmp_ctx);
7282 * We can skip the target object checks if we're only syncing critical
7283 * objects, or we know the target is up-to-date. If either case, we
7284 * still continue even if the target doesn't exist
7286 if ((la->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7287 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7289 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
7290 src_msg->dn, false, &guid,
7295 * When we fail to find the target object, the error code we pass
7296 * back here is really important. It flags back to the callers to
7297 * retry this request with DRSUAPI_DRS_GET_TGT
7299 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7300 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7303 talloc_free(tmp_ctx);
7308 * Finds the current active Parsed-DN value for a single-valued linked
7309 * attribute, if one exists.
7310 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7311 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7314 static int replmd_get_active_singleval_link(struct ldb_module *module,
7315 TALLOC_CTX *mem_ctx,
7316 struct parsed_dn pdn_list[],
7318 const struct dsdb_attribute *attr,
7319 struct parsed_dn **ret_pdn)
7325 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7327 /* nothing to do for multi-valued linked attributes */
7331 for (i = 0; i < count; i++) {
7332 int ret = LDB_SUCCESS;
7333 struct parsed_dn *pdn = &pdn_list[i];
7335 /* skip any inactive links */
7336 if (dsdb_dn_is_deleted_val(pdn->v)) {
7340 /* we've found an active value for this attribute */
7343 if (pdn->dsdb_dn == NULL) {
7344 struct ldb_context *ldb = ldb_module_get_ctx(module);
7346 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7347 attr->syntax->ldap_oid);
7353 /* no active link found */
7358 * @returns true if the replication linked attribute info is newer than we
7359 * already have in our DB
7360 * @param pdn the existing linked attribute info in our DB
7361 * @param la the new linked attribute info received during replication
7363 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7364 struct drsuapi_DsReplicaLinkedAttribute *la)
7366 /* see if this update is newer than what we have already */
7367 struct GUID invocation_id = GUID_zero();
7368 uint32_t version = 0;
7369 NTTIME change_time = 0;
7373 /* no existing info so update is newer */
7377 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7378 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7379 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7381 return replmd_update_is_newer(&invocation_id,
7382 &la->meta_data.originating_invocation_id,
7384 la->meta_data.version,
7386 la->meta_data.originating_change_time);
7390 * Marks an existing linked attribute value as deleted in the DB
7391 * @param pdn the parsed-DN of the target-value to delete
7393 static int replmd_delete_link_value(struct ldb_module *module,
7394 struct replmd_private *replmd_private,
7395 TALLOC_CTX *mem_ctx,
7396 struct ldb_dn *src_obj_dn,
7397 const struct dsdb_schema *schema,
7398 const struct dsdb_attribute *attr,
7401 struct GUID *target_guid,
7402 struct dsdb_dn *target_dsdb_dn,
7403 struct ldb_val *output_val)
7405 struct ldb_context *ldb = ldb_module_get_ctx(module);
7408 const struct GUID *invocation_id = NULL;
7412 unix_to_nt_time(&now, t);
7414 invocation_id = samdb_ntds_invocation_id(ldb);
7415 if (invocation_id == NULL) {
7416 return LDB_ERR_OPERATIONS_ERROR;
7419 /* if the existing link is active, remove its backlink */
7422 ret = replmd_add_backlink(module, replmd_private, schema,
7423 src_obj_dn, target_guid, false,
7425 if (ret != LDB_SUCCESS) {
7430 /* mark the existing value as deleted */
7431 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7432 target_dsdb_dn, invocation_id, seq_num,
7433 seq_num, now, true);
7438 * Checks for a conflict in single-valued link attributes, and tries to
7439 * resolve the problem if possible.
7441 * Single-valued links should only ever have one active value. If we already
7442 * have an active link value, and during replication we receive an active link
7443 * value for a different target DN, then we need to resolve this inconsistency
7444 * and determine which value should be active. If the received info is better/
7445 * newer than the existing link attribute, then we need to set our existing
7446 * link as deleted. If the received info is worse/older, then we should continue
7447 * to add it, but set it as an inactive link.
7449 * Note that this is a corner-case that is unlikely to happen (but if it does
7450 * happen, we don't want it to break replication completely).
7452 * @param pdn_being_modified the parsed DN corresponding to the received link
7453 * target (note this is NULL if the link does not already exist in our DB)
7454 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7455 * any existing active or inactive values for the attribute in our DB.
7456 * @param dsdb_dn the target DN for the received link attribute
7457 * @param add_as_inactive gets set to true if the received link is worse than
7458 * the existing link - it should still be added, but as an inactive link.
7460 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7461 struct replmd_private *replmd_private,
7462 TALLOC_CTX *mem_ctx,
7463 struct ldb_dn *src_obj_dn,
7464 struct drsuapi_DsReplicaLinkedAttribute *la,
7465 struct dsdb_dn *dsdb_dn,
7466 struct parsed_dn *pdn_being_modified,
7467 struct parsed_dn *pdn_list,
7468 struct ldb_message_element *old_el,
7469 const struct dsdb_schema *schema,
7470 const struct dsdb_attribute *attr,
7472 bool *add_as_inactive)
7474 struct parsed_dn *active_pdn = NULL;
7475 bool update_is_newer = false;
7479 * check if there's a conflict for single-valued links, i.e. an active
7480 * linked attribute already exists, but it has a different target value
7482 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7483 old_el->num_values, attr,
7486 if (ret != LDB_SUCCESS) {
7491 * If no active value exists (or the received info is for the currently
7492 * active value), then no conflict exists
7494 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7498 DBG_WARNING("Link conflict for %s attribute on %s\n",
7499 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7501 /* Work out how to resolve the conflict based on which info is better */
7502 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7504 if (update_is_newer) {
7505 DBG_WARNING("Using received value %s, over existing target %s\n",
7506 ldb_dn_get_linearized(dsdb_dn->dn),
7507 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7510 * Delete our existing active link. The received info will then
7511 * be added (through normal link processing) as the active value
7513 ret = replmd_delete_link_value(module, replmd_private, old_el,
7514 src_obj_dn, schema, attr,
7515 seq_num, true, &active_pdn->guid,
7516 active_pdn->dsdb_dn,
7519 if (ret != LDB_SUCCESS) {
7523 DBG_WARNING("Using existing target %s, over received value %s\n",
7524 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7525 ldb_dn_get_linearized(dsdb_dn->dn));
7528 * we want to keep our existing active link and add the
7529 * received link as inactive
7531 *add_as_inactive = true;
7538 process one linked attribute structure
7540 static int replmd_process_linked_attribute(struct ldb_module *module,
7541 struct replmd_private *replmd_private,
7542 struct la_entry *la_entry,
7543 struct ldb_request *parent)
7545 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7546 struct ldb_context *ldb = ldb_module_get_ctx(module);
7547 struct ldb_message *msg;
7548 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7549 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7551 const struct dsdb_attribute *attr;
7552 struct dsdb_dn *dsdb_dn;
7553 uint64_t seq_num = 0;
7554 struct ldb_message_element *old_el;
7555 time_t t = time(NULL);
7556 struct parsed_dn *pdn_list, *pdn, *next;
7557 struct GUID guid = GUID_zero();
7558 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7560 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
7561 struct dsdb_dn *old_dsdb_dn = NULL;
7562 struct ldb_val *val_to_update = NULL;
7563 bool add_as_inactive = false;
7566 * get the attribute being modified, the search result for the source object,
7567 * and the target object's DN details
7569 ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
7572 if (ret != LDB_SUCCESS) {
7573 talloc_free(tmp_ctx);
7578 * Check for deleted objects per MS-DRSR 4.1.10.6.14
7579 * ProcessLinkValue, because link updates are not applied to
7580 * recycled and tombstone objects. We don't have to delete
7581 * any existing link, that should have happened when the
7582 * object deletion was replicated or initiated.
7584 replmd_deletion_state(module, msg, &deletion_state, NULL);
7586 if (deletion_state >= OBJECT_RECYCLED) {
7587 talloc_free(tmp_ctx);
7591 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7592 if (old_el == NULL) {
7593 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7594 if (ret != LDB_SUCCESS) {
7595 ldb_module_oom(module);
7596 talloc_free(tmp_ctx);
7597 return LDB_ERR_OPERATIONS_ERROR;
7600 old_el->flags = LDB_FLAG_MOD_REPLACE;
7603 /* parse the existing links */
7604 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
7605 attr->syntax->ldap_oid, parent);
7607 if (ret != LDB_SUCCESS) {
7608 talloc_free(tmp_ctx);
7612 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7613 true, &guid, &ignore_link);
7615 if (ret != LDB_SUCCESS) {
7616 talloc_free(tmp_ctx);
7621 * there are some cases where the target object doesn't exist, but it's
7622 * OK to ignore the linked attribute
7625 talloc_free(tmp_ctx);
7629 /* see if this link already exists */
7630 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7633 dsdb_dn->extra_part, 0,
7635 attr->syntax->ldap_oid,
7637 if (ret != LDB_SUCCESS) {
7638 talloc_free(tmp_ctx);
7642 if (!replmd_link_update_is_newer(pdn, la)) {
7643 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7644 old_el->name, ldb_dn_get_linearized(msg->dn),
7645 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
7646 talloc_free(tmp_ctx);
7650 /* get a seq_num for this change */
7651 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7652 if (ret != LDB_SUCCESS) {
7653 talloc_free(tmp_ctx);
7658 * check for single-valued link conflicts, i.e. an active linked
7659 * attribute already exists, but it has a different target value
7662 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7663 tmp_ctx, msg->dn, la,
7664 dsdb_dn, pdn, pdn_list,
7665 old_el, schema, attr,
7668 if (ret != LDB_SUCCESS) {
7669 talloc_free(tmp_ctx);
7675 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7677 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7678 /* remove the existing backlink */
7679 ret = replmd_add_backlink(module, replmd_private,
7682 &pdn->guid, false, attr,
7684 if (ret != LDB_SUCCESS) {
7685 talloc_free(tmp_ctx);
7690 val_to_update = pdn->v;
7691 old_dsdb_dn = pdn->dsdb_dn;
7697 * We know where the new one needs to be, from the *next
7698 * pointer into pdn_list.
7701 offset = old_el->num_values;
7703 if (next->dsdb_dn == NULL) {
7704 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
7705 attr->syntax->ldap_oid);
7706 if (ret != LDB_SUCCESS) {
7710 offset = next - pdn_list;
7711 if (offset > old_el->num_values) {
7712 talloc_free(tmp_ctx);
7713 return LDB_ERR_OPERATIONS_ERROR;
7717 old_el->values = talloc_realloc(msg->elements, old_el->values,
7718 struct ldb_val, old_el->num_values+1);
7719 if (!old_el->values) {
7720 ldb_module_oom(module);
7721 return LDB_ERR_OPERATIONS_ERROR;
7724 if (offset != old_el->num_values) {
7725 memmove(&old_el->values[offset + 1], &old_el->values[offset],
7726 (old_el->num_values - offset) * sizeof(old_el->values[0]));
7729 old_el->num_values++;
7731 val_to_update = &old_el->values[offset];
7735 /* set the link attribute's value to the info that was received */
7736 ret = replmd_set_la_val(tmp_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
7737 &la->meta_data.originating_invocation_id,
7738 la->meta_data.originating_usn, seq_num,
7739 la->meta_data.originating_change_time,
7740 la->meta_data.version,
7742 if (ret != LDB_SUCCESS) {
7743 talloc_free(tmp_ctx);
7747 if (add_as_inactive) {
7749 /* Set the new link as inactive/deleted to avoid conflicts */
7750 ret = replmd_delete_link_value(module, replmd_private, old_el,
7751 msg->dn, schema, attr, seq_num,
7752 false, &guid, dsdb_dn,
7755 if (ret != LDB_SUCCESS) {
7756 talloc_free(tmp_ctx);
7760 } else if (active) {
7762 /* if the new link is active, then add the new backlink */
7763 ret = replmd_add_backlink(module, replmd_private,
7768 if (ret != LDB_SUCCESS) {
7769 talloc_free(tmp_ctx);
7774 /* we only change whenChanged and uSNChanged if the seq_num
7776 ret = add_time_element(msg, "whenChanged", t);
7777 if (ret != LDB_SUCCESS) {
7778 talloc_free(tmp_ctx);
7783 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
7784 if (ret != LDB_SUCCESS) {
7785 talloc_free(tmp_ctx);
7790 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7791 if (old_el == NULL) {
7792 talloc_free(tmp_ctx);
7793 return ldb_operr(ldb);
7796 ret = dsdb_check_single_valued_link(attr, old_el);
7797 if (ret != LDB_SUCCESS) {
7798 talloc_free(tmp_ctx);
7802 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
7804 ret = linked_attr_modify(module, msg, parent);
7805 if (ret != LDB_SUCCESS) {
7806 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
7808 ldb_ldif_message_redacted_string(ldb,
7810 LDB_CHANGETYPE_MODIFY,
7812 talloc_free(tmp_ctx);
7816 talloc_free(tmp_ctx);
7821 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
7823 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
7824 return replmd_extended_replicated_objects(module, req);
7827 return ldb_next_request(module, req);
7832 we hook into the transaction operations to allow us to
7833 perform the linked attribute updates at the end of the whole
7834 transaction. This allows a forward linked attribute to be created
7835 before the object is created. During a vampire, w2k8 sends us linked
7836 attributes before the objects they are part of.
7838 static int replmd_start_transaction(struct ldb_module *module)
7840 /* create our private structure for this transaction */
7841 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
7842 struct replmd_private);
7843 replmd_txn_cleanup(replmd_private);
7845 /* free any leftover mod_usn records from cancelled
7847 while (replmd_private->ncs) {
7848 struct nc_entry *e = replmd_private->ncs;
7849 DLIST_REMOVE(replmd_private->ncs, e);
7853 replmd_private->originating_updates = false;
7855 return ldb_next_start_trans(module);
7859 on prepare commit we loop over our queued la_context structures and
7862 static int replmd_prepare_commit(struct ldb_module *module)
7864 struct replmd_private *replmd_private =
7865 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7866 struct la_entry *la, *prev;
7870 * Walk the list of linked attributes from DRS replication.
7872 * We walk backwards, to do the first entry first, as we
7873 * added the entries with DLIST_ADD() which puts them at the
7876 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
7877 prev = DLIST_PREV(la);
7878 DLIST_REMOVE(replmd_private->la_list, la);
7879 ret = replmd_process_linked_attribute(module, replmd_private,
7881 if (ret != LDB_SUCCESS) {
7882 replmd_txn_cleanup(replmd_private);
7887 replmd_txn_cleanup(replmd_private);
7889 /* possibly change @REPLCHANGED */
7890 ret = replmd_notify_store(module, NULL);
7891 if (ret != LDB_SUCCESS) {
7895 return ldb_next_prepare_commit(module);
7898 static int replmd_del_transaction(struct ldb_module *module)
7900 struct replmd_private *replmd_private =
7901 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
7902 replmd_txn_cleanup(replmd_private);
7904 return ldb_next_del_trans(module);
7908 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
7909 .name = "repl_meta_data",
7910 .init_context = replmd_init,
7912 .modify = replmd_modify,
7913 .rename = replmd_rename,
7914 .del = replmd_delete,
7915 .extended = replmd_extended,
7916 .start_transaction = replmd_start_transaction,
7917 .prepare_commit = replmd_prepare_commit,
7918 .del_transaction = replmd_del_transaction,
7921 int ldb_repl_meta_data_module_init(const char *version)
7923 LDB_MODULE_CHECK_VERSION(version);
7924 return ldb_register_module(&ldb_repl_meta_data_module_ops);