4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 Copyright (C) Andrew Tridgell 2005-2009
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "dsdb/common/util.h"
43 #include "../libds/common/flags.h"
44 #include "librpc/gen_ndr/irpc.h"
45 #include "librpc/gen_ndr/ndr_misc.h"
46 #include "librpc/gen_ndr/ndr_drsuapi.h"
47 #include "librpc/gen_ndr/ndr_drsblobs.h"
48 #include "param/param.h"
49 #include "libcli/security/security.h"
50 #include "lib/util/dlinklist.h"
51 #include "dsdb/samdb/ldb_modules/util.h"
52 #include "lib/util/tsort.h"
55 #define DBGC_CLASS DBGC_DRS_REPL
57 /* the RMD_VERSION for linked attributes starts from 1 */
58 #define RMD_VERSION_INITIAL 1
61 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
62 * Deleted Objects Container
64 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
66 struct replmd_private {
68 struct la_group *la_list;
70 struct nc_entry *prev, *next;
73 uint64_t mod_usn_urgent;
75 struct ldb_dn *schema_dn;
76 bool originating_updates;
79 uint32_t num_processed;
83 * groups link attributes together by source-object and attribute-ID,
84 * to improve processing efficiency (i.e. for 'member' attribute, which
85 * could have 100s or 1000s of links).
86 * Note this grouping is best effort - the same source object could still
87 * correspond to several la_groups (a lot depends on the order DRS sends
88 * the links in). The groups currently don't span replication chunks (which
89 * caps the size to ~1500 links by default).
92 struct la_group *next, *prev;
93 struct la_entry *la_entries;
97 struct la_entry *next, *prev;
98 struct drsuapi_DsReplicaLinkedAttribute *la;
99 uint32_t dsdb_repl_flags;
102 struct replmd_replicated_request {
103 struct ldb_module *module;
104 struct ldb_request *req;
106 const struct dsdb_schema *schema;
107 struct GUID our_invocation_id;
109 /* the controls we pass down */
110 struct ldb_control **controls;
113 * Backlinks for the replmd_add() case (we want to create
114 * backlinks after creating the user, but before the end of
117 struct la_backlink *la_backlinks;
119 /* details for the mode where we apply a bunch of inbound replication meessages */
121 uint32_t index_current;
122 struct dsdb_extended_replicated_objects *objs;
124 struct ldb_message *search_msg;
125 struct GUID local_parent_guid;
135 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
136 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
137 static int replmd_check_upgrade_links(struct ldb_context *ldb,
138 struct parsed_dn *dns, uint32_t count,
139 struct ldb_message_element *el,
140 const char *ldap_oid);
141 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
142 struct la_entry *la);
143 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
144 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
145 uint64_t usn, uint64_t local_usn, NTTIME nttime,
146 uint32_t version, bool deleted);
148 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
149 struct ldb_context *ldb,
151 const char *rdn_name,
152 const struct ldb_val *rdn_value,
155 enum urgent_situation {
156 REPL_URGENT_ON_CREATE = 1,
157 REPL_URGENT_ON_UPDATE = 2,
158 REPL_URGENT_ON_DELETE = 4
161 enum deletion_state {
162 OBJECT_NOT_DELETED=1,
169 static void replmd_deletion_state(struct ldb_module *module,
170 const struct ldb_message *msg,
171 enum deletion_state *current_state,
172 enum deletion_state *next_state)
175 bool enabled = false;
178 *current_state = OBJECT_REMOVED;
179 if (next_state != NULL) {
180 *next_state = OBJECT_REMOVED;
185 ret = dsdb_recyclebin_enabled(module, &enabled);
186 if (ret != LDB_SUCCESS) {
190 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
192 *current_state = OBJECT_TOMBSTONE;
193 if (next_state != NULL) {
194 *next_state = OBJECT_REMOVED;
199 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
200 *current_state = OBJECT_RECYCLED;
201 if (next_state != NULL) {
202 *next_state = OBJECT_REMOVED;
207 *current_state = OBJECT_DELETED;
208 if (next_state != NULL) {
209 *next_state = OBJECT_RECYCLED;
214 *current_state = OBJECT_NOT_DELETED;
215 if (next_state == NULL) {
220 *next_state = OBJECT_DELETED;
222 *next_state = OBJECT_TOMBSTONE;
226 static const struct {
227 const char *update_name;
228 enum urgent_situation repl_situation;
229 } urgent_objects[] = {
230 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
231 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
232 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
233 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
234 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
235 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
239 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
240 static const char *urgent_attrs[] = {
243 "userAccountControl",
248 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
249 enum urgent_situation situation)
252 for (i=0; urgent_objects[i].update_name; i++) {
254 if ((situation & urgent_objects[i].repl_situation) == 0) {
258 for (j=0; j<objectclass_el->num_values; j++) {
259 const struct ldb_val *v = &objectclass_el->values[j];
260 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
268 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
270 if (ldb_attr_in_list(urgent_attrs, el->name)) {
276 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
279 initialise the module
280 allocate the private structure and build the list
281 of partition DNs for use by replmd_notify()
283 static int replmd_init(struct ldb_module *module)
285 struct replmd_private *replmd_private;
286 struct ldb_context *ldb = ldb_module_get_ctx(module);
287 static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
288 struct ldb_dn *samba_dsdb_dn;
289 struct ldb_result *res;
291 TALLOC_CTX *frame = talloc_stackframe();
292 replmd_private = talloc_zero(module, struct replmd_private);
293 if (replmd_private == NULL) {
296 return LDB_ERR_OPERATIONS_ERROR;
298 ldb_module_set_private(module, replmd_private);
300 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
302 samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
303 if (!samba_dsdb_dn) {
308 ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
309 samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
310 if (ret == LDB_SUCCESS) {
311 replmd_private->sorted_links
312 = ldb_msg_check_string_attribute(res->msgs[0],
313 SAMBA_COMPATIBLE_FEATURES_ATTR,
314 SAMBA_SORTED_LINKS_FEATURE);
318 return ldb_next_init(module);
322 cleanup our per-transaction contexts
324 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
326 talloc_free(replmd_private->la_ctx);
327 replmd_private->la_list = NULL;
328 replmd_private->la_ctx = NULL;
334 struct la_backlink *next, *prev;
335 const char *attr_name;
336 struct ldb_dn *forward_dn;
337 struct GUID target_guid;
342 a ldb_modify request operating on modules below the
345 static int linked_attr_modify(struct ldb_module *module,
346 const struct ldb_message *message,
347 struct ldb_request *parent)
349 struct ldb_request *mod_req;
351 struct ldb_context *ldb = ldb_module_get_ctx(module);
352 TALLOC_CTX *tmp_ctx = talloc_new(module);
353 struct ldb_result *res;
355 res = talloc_zero(tmp_ctx, struct ldb_result);
357 talloc_free(tmp_ctx);
358 return ldb_oom(ldb_module_get_ctx(module));
361 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
365 ldb_modify_default_callback,
367 LDB_REQ_SET_LOCATION(mod_req);
368 if (ret != LDB_SUCCESS) {
369 talloc_free(tmp_ctx);
373 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
375 if (ret != LDB_SUCCESS) {
379 /* Run the new request */
380 ret = ldb_next_request(module, mod_req);
382 if (ret == LDB_SUCCESS) {
383 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
386 talloc_free(tmp_ctx);
391 process a backlinks we accumulated during a transaction, adding and
392 deleting the backlinks from the target objects
394 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
396 struct ldb_dn *target_dn, *source_dn;
398 struct ldb_context *ldb = ldb_module_get_ctx(module);
399 struct ldb_message *msg;
400 TALLOC_CTX *frame = talloc_stackframe();
406 - construct ldb_message
407 - either an add or a delete
409 ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
410 if (ret != LDB_SUCCESS) {
411 struct GUID_txt_buf guid_str;
412 DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
413 GUID_buf_string(&bl->target_guid, &guid_str));
414 DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
419 msg = ldb_msg_new(frame);
421 ldb_module_oom(module);
423 return LDB_ERR_OPERATIONS_ERROR;
426 source_dn = ldb_dn_copy(frame, bl->forward_dn);
428 ldb_module_oom(module);
430 return LDB_ERR_OPERATIONS_ERROR;
432 /* Filter down to the attributes we want in the backlink */
433 const char *accept[] = { "GUID", "SID", NULL };
434 ldb_dn_extended_filter(source_dn, accept);
437 /* construct a ldb_message for adding/deleting the backlink */
439 dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
441 ldb_module_oom(module);
443 return LDB_ERR_OPERATIONS_ERROR;
445 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
446 if (ret != LDB_SUCCESS) {
450 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
452 /* a backlink should never be single valued. Unfortunately the
453 exchange schema has a attribute
454 msExchBridgeheadedLocalConnectorsDNBL which is single
455 valued and a backlink. We need to cope with that by
456 ignoring the single value flag */
457 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
459 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
460 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
461 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
462 cope with possible corruption where the backlink has
463 already been removed */
464 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
465 ldb_dn_get_linearized(target_dn),
466 ldb_dn_get_linearized(source_dn),
467 ldb_errstring(ldb)));
469 } else if (ret != LDB_SUCCESS) {
470 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
471 bl->active?"add":"remove",
472 ldb_dn_get_linearized(source_dn),
473 ldb_dn_get_linearized(target_dn),
483 add a backlink to the list of backlinks to add/delete in the prepare
486 forward_dn is stolen onto the defereed context
488 static int replmd_defer_add_backlink(struct ldb_module *module,
489 struct replmd_private *replmd_private,
490 const struct dsdb_schema *schema,
491 struct replmd_replicated_request *ac,
492 struct ldb_dn *forward_dn,
493 struct GUID *target_guid, bool active,
494 const struct dsdb_attribute *schema_attr,
495 struct ldb_request *parent)
497 const struct dsdb_attribute *target_attr;
498 struct la_backlink *bl;
500 bl = talloc(ac, struct la_backlink);
502 ldb_module_oom(module);
503 return LDB_ERR_OPERATIONS_ERROR;
506 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
509 * windows 2003 has a broken schema where the
510 * definition of msDS-IsDomainFor is missing (which is
511 * supposed to be the backlink of the
512 * msDS-HasDomainNCs attribute
517 bl->attr_name = target_attr->lDAPDisplayName;
518 bl->forward_dn = talloc_steal(bl, forward_dn);
519 bl->target_guid = *target_guid;
522 DLIST_ADD(ac->la_backlinks, bl);
528 add a backlink to the list of backlinks to add/delete in the prepare
531 static int replmd_add_backlink(struct ldb_module *module,
532 struct replmd_private *replmd_private,
533 const struct dsdb_schema *schema,
534 struct ldb_dn *forward_dn,
535 struct GUID *target_guid, bool active,
536 const struct dsdb_attribute *schema_attr,
537 struct ldb_request *parent)
539 const struct dsdb_attribute *target_attr;
540 struct la_backlink bl;
543 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
546 * windows 2003 has a broken schema where the
547 * definition of msDS-IsDomainFor is missing (which is
548 * supposed to be the backlink of the
549 * msDS-HasDomainNCs attribute
554 bl.attr_name = target_attr->lDAPDisplayName;
555 bl.forward_dn = forward_dn;
556 bl.target_guid = *target_guid;
559 ret = replmd_process_backlink(module, &bl, parent);
565 * Callback for most write operations in this module:
567 * notify the repl task that a object has changed. The notifies are
568 * gathered up in the replmd_private structure then written to the
569 * @REPLCHANGED object in each partition during the prepare_commit
571 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
574 struct replmd_replicated_request *ac =
575 talloc_get_type_abort(req->context, struct replmd_replicated_request);
576 struct replmd_private *replmd_private =
577 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
578 struct nc_entry *modified_partition;
579 struct ldb_control *partition_ctrl;
580 const struct dsdb_control_current_partition *partition;
582 struct ldb_control **controls;
584 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
586 controls = ares->controls;
587 if (ldb_request_get_control(ac->req,
588 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
590 * Remove the current partition control from what we pass up
591 * the chain if it hasn't been requested manually.
593 controls = ldb_controls_except_specified(ares->controls, ares,
597 if (ares->error != LDB_SUCCESS) {
598 struct GUID_txt_buf guid_txt;
599 struct ldb_message *msg = NULL;
602 if (ac->apply_mode == false) {
603 DBG_NOTICE("Originating update failure. Error is: %s\n",
604 ldb_strerror(ares->error));
605 return ldb_module_done(ac->req, controls,
606 ares->response, ares->error);
609 msg = ac->objs->objects[ac->index_current].msg;
611 * Set at DBG_NOTICE as once these start to happe, they
612 * will happen a lot until resolved, due to repeated
613 * replication. The caller will probably print the
614 * ldb error string anyway.
616 DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
617 ldb_dn_get_linearized(msg->dn),
618 ldb_strerror(ares->error));
620 s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
625 DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
626 ac->search_msg == NULL ? "ADD" : "MODIFY",
627 GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
631 return ldb_module_done(ac->req, controls,
632 ares->response, ares->error);
635 if (ares->type != LDB_REPLY_DONE) {
636 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
637 return ldb_module_done(ac->req, NULL,
638 NULL, LDB_ERR_OPERATIONS_ERROR);
641 if (ac->apply_mode == false) {
642 struct la_backlink *bl;
644 * process our backlink list after an replmd_add(),
645 * creating and deleting backlinks as necessary (this
646 * code is sync). The other cases are handled inline
649 for (bl=ac->la_backlinks; bl; bl=bl->next) {
650 ret = replmd_process_backlink(ac->module, bl, ac->req);
651 if (ret != LDB_SUCCESS) {
652 return ldb_module_done(ac->req, NULL,
658 if (!partition_ctrl) {
659 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
660 return ldb_module_done(ac->req, NULL,
661 NULL, LDB_ERR_OPERATIONS_ERROR);
664 partition = talloc_get_type_abort(partition_ctrl->data,
665 struct dsdb_control_current_partition);
667 if (ac->seq_num > 0) {
668 for (modified_partition = replmd_private->ncs; modified_partition;
669 modified_partition = modified_partition->next) {
670 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
675 if (modified_partition == NULL) {
676 modified_partition = talloc_zero(replmd_private, struct nc_entry);
677 if (!modified_partition) {
678 ldb_oom(ldb_module_get_ctx(ac->module));
679 return ldb_module_done(ac->req, NULL,
680 NULL, LDB_ERR_OPERATIONS_ERROR);
682 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
683 if (!modified_partition->dn) {
684 ldb_oom(ldb_module_get_ctx(ac->module));
685 return ldb_module_done(ac->req, NULL,
686 NULL, LDB_ERR_OPERATIONS_ERROR);
688 DLIST_ADD(replmd_private->ncs, modified_partition);
691 if (ac->seq_num > modified_partition->mod_usn) {
692 modified_partition->mod_usn = ac->seq_num;
694 modified_partition->mod_usn_urgent = ac->seq_num;
697 if (!ac->apply_mode) {
698 replmd_private->originating_updates = true;
702 if (ac->apply_mode) {
703 ret = replmd_replicated_apply_isDeleted(ac);
704 if (ret != LDB_SUCCESS) {
705 return ldb_module_done(ac->req, NULL, NULL, ret);
709 /* free the partition control container here, for the
710 * common path. Other cases will have it cleaned up
711 * eventually with the ares */
712 talloc_free(partition_ctrl);
713 return ldb_module_done(ac->req, controls,
714 ares->response, LDB_SUCCESS);
720 * update a @REPLCHANGED record in each partition if there have been
721 * any writes of replicated data in the partition
723 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
725 struct replmd_private *replmd_private =
726 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
728 while (replmd_private->ncs) {
730 struct nc_entry *modified_partition = replmd_private->ncs;
732 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
733 modified_partition->mod_usn,
734 modified_partition->mod_usn_urgent, parent);
735 if (ret != LDB_SUCCESS) {
736 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
737 ldb_dn_get_linearized(modified_partition->dn)));
741 if (ldb_dn_compare(modified_partition->dn,
742 replmd_private->schema_dn) == 0) {
743 struct ldb_result *ext_res;
744 ret = dsdb_module_extended(module,
745 replmd_private->schema_dn,
747 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
749 DSDB_FLAG_NEXT_MODULE,
751 if (ret != LDB_SUCCESS) {
754 talloc_free(ext_res);
757 DLIST_REMOVE(replmd_private->ncs, modified_partition);
758 talloc_free(modified_partition);
766 created a replmd_replicated_request context
768 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
769 struct ldb_request *req)
771 struct ldb_context *ldb;
772 struct replmd_replicated_request *ac;
773 const struct GUID *our_invocation_id;
775 ldb = ldb_module_get_ctx(module);
777 ac = talloc_zero(req, struct replmd_replicated_request);
786 ac->schema = dsdb_get_schema(ldb, ac);
788 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
789 "replmd_modify: no dsdb_schema loaded");
790 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
795 /* get our invocationId */
796 our_invocation_id = samdb_ntds_invocation_id(ldb);
797 if (!our_invocation_id) {
798 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
799 "replmd_add: unable to find invocationId\n");
803 ac->our_invocation_id = *our_invocation_id;
809 add a time element to a record
811 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
813 struct ldb_message_element *el;
817 if (ldb_msg_find_element(msg, attr) != NULL) {
821 s = ldb_timestring(msg, t);
823 return LDB_ERR_OPERATIONS_ERROR;
826 ret = ldb_msg_add_string(msg, attr, s);
827 if (ret != LDB_SUCCESS) {
831 el = ldb_msg_find_element(msg, attr);
832 /* always set as replace. This works because on add ops, the flag
834 el->flags = LDB_FLAG_MOD_REPLACE;
840 add a uint64_t element to a record
842 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
843 const char *attr, uint64_t v)
845 struct ldb_message_element *el;
848 if (ldb_msg_find_element(msg, attr) != NULL) {
852 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
853 if (ret != LDB_SUCCESS) {
857 el = ldb_msg_find_element(msg, attr);
858 /* always set as replace. This works because on add ops, the flag
860 el->flags = LDB_FLAG_MOD_REPLACE;
865 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
866 const struct replPropertyMetaData1 *m2,
867 const uint32_t *rdn_attid)
870 * This assignment seems inoccous, but it is critical for the
871 * system, as we need to do the comparisons as a unsigned
872 * quantity, not signed (enums are signed integers)
874 uint32_t attid_1 = m1->attid;
875 uint32_t attid_2 = m2->attid;
877 if (attid_1 == attid_2) {
882 * See above regarding this being an unsigned comparison.
883 * Otherwise when the high bit is set on non-standard
884 * attributes, they would end up first, before objectClass
887 return attid_1 > attid_2 ? 1 : -1;
890 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
891 struct replPropertyMetaDataCtr1 *ctr1,
894 if (ctr1->count == 0) {
895 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
896 "No elements found in replPropertyMetaData for %s!\n",
897 ldb_dn_get_linearized(dn));
898 return LDB_ERR_CONSTRAINT_VIOLATION;
901 /* the objectClass attribute is value 0x00000000, so must be first */
902 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
903 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
904 "No objectClass found in replPropertyMetaData for %s!\n",
905 ldb_dn_get_linearized(dn));
906 return LDB_ERR_OBJECT_CLASS_VIOLATION;
912 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
913 struct replPropertyMetaDataCtr1 *ctr1,
916 /* Note this is O(n^2) for the almost-sorted case, which this is */
917 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
918 replmd_replPropertyMetaData1_attid_sort);
919 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
922 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
923 const struct ldb_message_element *e2,
924 const struct dsdb_schema *schema)
926 const struct dsdb_attribute *a1;
927 const struct dsdb_attribute *a2;
930 * TODO: make this faster by caching the dsdb_attribute pointer
931 * on the ldb_messag_element
934 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
935 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
938 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
942 return strcasecmp(e1->name, e2->name);
944 if (a1->attributeID_id == a2->attributeID_id) {
947 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
950 static void replmd_ldb_message_sort(struct ldb_message *msg,
951 const struct dsdb_schema *schema)
953 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
956 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
957 const struct GUID *invocation_id,
958 uint64_t local_usn, NTTIME nttime);
960 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
962 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
963 struct ldb_message_element *el, struct parsed_dn **pdn,
964 const char *ldap_oid, struct ldb_request *parent);
966 static int check_parsed_dn_duplicates(struct ldb_module *module,
967 struct ldb_message_element *el,
968 struct parsed_dn *pdn);
971 fix up linked attributes in replmd_add.
972 This involves setting up the right meta-data in extended DN
973 components, and creating backlinks to the object
975 static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
976 struct replmd_private *replmd_private,
977 struct ldb_message_element *el,
978 struct replmd_replicated_request *ac,
980 struct ldb_dn *forward_dn,
981 const struct dsdb_attribute *sa,
982 struct ldb_request *parent)
985 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
986 struct ldb_context *ldb = ldb_module_get_ctx(module);
987 struct parsed_dn *pdn;
988 /* We will take a reference to the schema in replmd_add_backlink */
989 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
990 struct ldb_val *new_values = NULL;
993 if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
994 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
996 ldb_asprintf_errstring(ldb,
997 "Attribute %s is single valued but "
998 "more than one value has been supplied",
1000 talloc_free(tmp_ctx);
1001 return LDB_ERR_CONSTRAINT_VIOLATION;
1004 ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1005 sa->syntax->ldap_oid, parent);
1006 if (ret != LDB_SUCCESS) {
1007 talloc_free(tmp_ctx);
1011 ret = check_parsed_dn_duplicates(module, el, pdn);
1012 if (ret != LDB_SUCCESS) {
1013 talloc_free(tmp_ctx);
1017 new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1018 if (new_values == NULL) {
1019 ldb_module_oom(module);
1020 talloc_free(tmp_ctx);
1021 return LDB_ERR_OPERATIONS_ERROR;
1024 for (i = 0; i < el->num_values; i++) {
1025 struct parsed_dn *p = &pdn[i];
1026 ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
1027 &ac->our_invocation_id,
1029 if (ret != LDB_SUCCESS) {
1030 talloc_free(tmp_ctx);
1034 ret = replmd_defer_add_backlink(module, replmd_private,
1036 forward_dn, &p->guid, true, sa,
1038 if (ret != LDB_SUCCESS) {
1039 talloc_free(tmp_ctx);
1043 new_values[i] = *p->v;
1045 el->values = talloc_steal(mem_ctx, new_values);
1047 talloc_free(tmp_ctx);
1051 static int replmd_add_make_extended_dn(struct ldb_request *req,
1052 const DATA_BLOB *guid_blob,
1053 struct ldb_dn **_extended_dn)
1056 const DATA_BLOB *sid_blob;
1057 /* Calculate an extended DN for any linked attributes */
1058 struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1060 return LDB_ERR_OPERATIONS_ERROR;
1062 ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1063 if (ret != LDB_SUCCESS) {
1067 sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1068 if (sid_blob != NULL) {
1069 ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1070 if (ret != LDB_SUCCESS) {
1074 *_extended_dn = extended_dn;
1079 intercept add requests
1081 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1083 struct ldb_context *ldb;
1084 struct ldb_control *control;
1085 struct replmd_replicated_request *ac;
1086 enum ndr_err_code ndr_err;
1087 struct ldb_request *down_req;
1088 struct ldb_message *msg;
1089 const DATA_BLOB *guid_blob;
1090 DATA_BLOB guid_blob_stack;
1092 uint8_t guid_data[16];
1093 struct replPropertyMetaDataBlob nmd;
1094 struct ldb_val nmd_value;
1095 struct ldb_dn *extended_dn = NULL;
1098 * The use of a time_t here seems odd, but as the NTTIME
1099 * elements are actually declared as NTTIME_1sec in the IDL,
1100 * getting a higher resolution timestamp is not required.
1102 time_t t = time(NULL);
1107 unsigned int functional_level;
1109 bool allow_add_guid = false;
1110 bool remove_current_guid = false;
1111 bool is_urgent = false;
1112 bool is_schema_nc = false;
1113 struct ldb_message_element *objectclass_el;
1114 struct replmd_private *replmd_private =
1115 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1117 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1118 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1120 allow_add_guid = true;
1123 /* do not manipulate our control entries */
1124 if (ldb_dn_is_special(req->op.add.message->dn)) {
1125 return ldb_next_request(module, req);
1128 ldb = ldb_module_get_ctx(module);
1130 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1132 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1133 if (guid_blob != NULL) {
1134 if (!allow_add_guid) {
1135 ldb_set_errstring(ldb,
1136 "replmd_add: it's not allowed to add an object with objectGUID!");
1137 return LDB_ERR_UNWILLING_TO_PERFORM;
1139 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1140 if (!NT_STATUS_IS_OK(status)) {
1141 ldb_set_errstring(ldb,
1142 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1143 return LDB_ERR_UNWILLING_TO_PERFORM;
1145 /* we remove this attribute as it can be a string and
1146 * will not be treated correctly and then we will re-add
1147 * it later on in the good format */
1148 remove_current_guid = true;
1152 guid = GUID_random();
1154 guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1156 /* This can't fail */
1157 ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1158 (ndr_push_flags_fn_t)ndr_push_GUID);
1159 guid_blob = &guid_blob_stack;
1162 ac = replmd_ctx_init(module, req);
1164 return ldb_module_oom(module);
1167 functional_level = dsdb_functional_level(ldb);
1169 /* Get a sequence number from the backend */
1170 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1171 if (ret != LDB_SUCCESS) {
1176 /* we have to copy the message as the caller might have it as a const */
1177 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1181 return LDB_ERR_OPERATIONS_ERROR;
1184 /* generated times */
1185 unix_to_nt_time(&now, t);
1186 time_str = ldb_timestring(msg, t);
1190 return LDB_ERR_OPERATIONS_ERROR;
1192 if (remove_current_guid) {
1193 ldb_msg_remove_attr(msg,"objectGUID");
1197 * remove autogenerated attributes
1199 ldb_msg_remove_attr(msg, "whenCreated");
1200 ldb_msg_remove_attr(msg, "whenChanged");
1201 ldb_msg_remove_attr(msg, "uSNCreated");
1202 ldb_msg_remove_attr(msg, "uSNChanged");
1203 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1206 * readd replicated attributes
1208 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1209 if (ret != LDB_SUCCESS) {
1215 /* build the replication meta_data */
1218 nmd.ctr.ctr1.count = msg->num_elements;
1219 nmd.ctr.ctr1.array = talloc_array(msg,
1220 struct replPropertyMetaData1,
1221 nmd.ctr.ctr1.count);
1222 if (!nmd.ctr.ctr1.array) {
1225 return LDB_ERR_OPERATIONS_ERROR;
1228 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1230 for (i=0; i < msg->num_elements;) {
1231 struct ldb_message_element *e = &msg->elements[i];
1232 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1233 const struct dsdb_attribute *sa;
1235 if (e->name[0] == '@') {
1240 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1242 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1243 "replmd_add: attribute '%s' not defined in schema\n",
1246 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1249 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1250 /* if the attribute is not replicated (0x00000001)
1251 * or constructed (0x00000004) it has no metadata
1257 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1258 if (extended_dn == NULL) {
1259 ret = replmd_add_make_extended_dn(req,
1262 if (ret != LDB_SUCCESS) {
1269 * Prepare the context for the backlinks and
1270 * create metadata for the forward links. The
1271 * backlinks are created in
1272 * replmd_op_callback() after the successful
1273 * ADD of the object.
1275 ret = replmd_add_fix_la(module, msg->elements,
1280 if (ret != LDB_SUCCESS) {
1284 /* linked attributes are not stored in
1285 replPropertyMetaData in FL above w2k */
1290 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1292 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1293 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1296 if (rdn_val == NULL) {
1299 return LDB_ERR_OPERATIONS_ERROR;
1302 rdn = (const char*)rdn_val->data;
1303 if (strcmp(rdn, "Deleted Objects") == 0) {
1305 * Set the originating_change_time to 29/12/9999 at 23:59:59
1306 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1308 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1310 m->originating_change_time = now;
1313 m->originating_change_time = now;
1315 m->originating_invocation_id = ac->our_invocation_id;
1316 m->originating_usn = ac->seq_num;
1317 m->local_usn = ac->seq_num;
1320 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1325 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1327 if (e->num_values != 0) {
1332 ldb_msg_remove_element(msg, e);
1335 /* fix meta data count */
1336 nmd.ctr.ctr1.count = ni;
1339 * sort meta data array
1341 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1342 if (ret != LDB_SUCCESS) {
1343 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1348 /* generated NDR encoded values */
1349 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1351 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1352 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1355 return LDB_ERR_OPERATIONS_ERROR;
1359 * add the autogenerated values
1361 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1362 if (ret != LDB_SUCCESS) {
1367 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1368 if (ret != LDB_SUCCESS) {
1373 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1374 if (ret != LDB_SUCCESS) {
1379 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1380 if (ret != LDB_SUCCESS) {
1385 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1386 if (ret != LDB_SUCCESS) {
1393 * sort the attributes by attid before storing the object
1395 replmd_ldb_message_sort(msg, ac->schema);
1398 * Assert that we do have an objectClass
1400 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1401 if (objectclass_el == NULL) {
1402 ldb_asprintf_errstring(ldb, __location__
1403 ": objectClass missing on %s\n",
1404 ldb_dn_get_linearized(msg->dn));
1406 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1408 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1409 REPL_URGENT_ON_CREATE);
1411 ac->is_urgent = is_urgent;
1412 ret = ldb_build_add_req(&down_req, ldb, ac,
1415 ac, replmd_op_callback,
1418 LDB_REQ_SET_LOCATION(down_req);
1419 if (ret != LDB_SUCCESS) {
1424 /* current partition control is needed by "replmd_op_callback" */
1425 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1426 ret = ldb_request_add_control(down_req,
1427 DSDB_CONTROL_CURRENT_PARTITION_OID,
1429 if (ret != LDB_SUCCESS) {
1435 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1436 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1437 if (ret != LDB_SUCCESS) {
1443 /* mark the control done */
1445 control->critical = 0;
1447 /* go on with the call chain */
1448 return ldb_next_request(module, down_req);
1453 * update the replPropertyMetaData for one element
1455 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1456 struct ldb_message *msg,
1457 struct ldb_message_element *el,
1458 struct ldb_message_element *old_el,
1459 struct replPropertyMetaDataBlob *omd,
1460 const struct dsdb_schema *schema,
1462 const struct GUID *our_invocation_id,
1465 bool is_forced_rodc,
1466 struct ldb_request *req)
1469 const struct dsdb_attribute *a;
1470 struct replPropertyMetaData1 *md1;
1471 bool may_skip = false;
1474 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1476 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1477 /* allow this to make it possible for dbcheck
1478 to remove bad attributes */
1482 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1484 return LDB_ERR_OPERATIONS_ERROR;
1487 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1489 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1494 * if the attribute's value haven't changed, and this isn't
1495 * just a delete of everything then return LDB_SUCCESS Unless
1496 * we have the provision control or if the attribute is
1497 * interSiteTopologyGenerator as this page explain:
1498 * http://support.microsoft.com/kb/224815 this attribute is
1499 * periodicaly written by the DC responsible for the intersite
1500 * generation in a given site
1502 * Unchanged could be deleting or replacing an already-gone
1503 * thing with an unconstrained delete/empty replace or a
1504 * replace with the same value, but not an add with the same
1505 * value because that could be about adding a duplicate (which
1506 * is for someone else to error out on).
1508 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1509 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1512 } else if (old_el == NULL && el->num_values == 0) {
1513 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1515 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1518 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1519 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1521 * We intentionally skip the version bump when attempting to
1524 * The control is set by dbcheck and expunge-tombstones which
1525 * both attempt to be non-replicating. Otherwise, making an
1526 * alteration to the replication state would trigger a
1527 * broadcast of all expunged objects.
1532 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1534 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1538 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1539 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1541 * allow this to make it possible for dbcheck
1542 * to rebuild broken metadata
1548 for (i=0; i<omd->ctr.ctr1.count; i++) {
1550 * First check if we find it under the msDS-IntID,
1551 * then check if we find it under the OID and
1554 * This allows the administrator to simply re-write
1555 * the attributes and so restore replication, which is
1556 * likely what they will try to do.
1558 if (attid == omd->ctr.ctr1.array[i].attid) {
1562 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1567 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1568 /* linked attributes are not stored in
1569 replPropertyMetaData in FL above w2k, but we do
1570 raise the seqnum for the object */
1571 if (*seq_num == 0 &&
1572 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1573 return LDB_ERR_OPERATIONS_ERROR;
1578 if (i == omd->ctr.ctr1.count) {
1579 /* we need to add a new one */
1580 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1581 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1582 if (omd->ctr.ctr1.array == NULL) {
1584 return LDB_ERR_OPERATIONS_ERROR;
1586 omd->ctr.ctr1.count++;
1587 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1590 /* Get a new sequence number from the backend. We only do this
1591 * if we have a change that requires a new
1592 * replPropertyMetaData element
1594 if (*seq_num == 0) {
1595 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1596 if (ret != LDB_SUCCESS) {
1597 return LDB_ERR_OPERATIONS_ERROR;
1601 md1 = &omd->ctr.ctr1.array[i];
1605 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1606 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1609 if (rdn_val == NULL) {
1611 return LDB_ERR_OPERATIONS_ERROR;
1614 rdn = (const char*)rdn_val->data;
1615 if (strcmp(rdn, "Deleted Objects") == 0) {
1617 * Set the originating_change_time to 29/12/9999 at 23:59:59
1618 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1620 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1622 md1->originating_change_time = now;
1625 md1->originating_change_time = now;
1627 md1->originating_invocation_id = *our_invocation_id;
1628 md1->originating_usn = *seq_num;
1629 md1->local_usn = *seq_num;
1631 if (is_forced_rodc) {
1632 /* Force version to 0 to be overriden later via replication */
1640 * Bump the replPropertyMetaData version on an attribute, and if it
1641 * has changed (or forced by leaving rdn_old NULL), update the value
1644 * This is important, as calling a modify operation may not change the
1645 * version number if the values appear unchanged, but a rename between
1646 * parents bumps this value.
1649 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1650 struct ldb_message *msg,
1651 const struct ldb_val *rdn_new,
1652 const struct ldb_val *rdn_old,
1653 struct replPropertyMetaDataBlob *omd,
1654 struct replmd_replicated_request *ar,
1657 bool is_forced_rodc)
1659 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1660 const struct dsdb_attribute *rdn_attr =
1661 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1662 const char *attr_name = rdn_attr != NULL ?
1663 rdn_attr->lDAPDisplayName :
1665 struct ldb_message_element new_el = {
1666 .flags = LDB_FLAG_MOD_REPLACE,
1669 .values = discard_const_p(struct ldb_val, rdn_new)
1671 struct ldb_message_element old_el = {
1672 .flags = LDB_FLAG_MOD_REPLACE,
1674 .num_values = rdn_old ? 1 : 0,
1675 .values = discard_const_p(struct ldb_val, rdn_old)
1678 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1679 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1680 if (ret != LDB_SUCCESS) {
1681 return ldb_oom(ldb);
1685 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1686 omd, ar->schema, &ar->seq_num,
1687 &ar->our_invocation_id,
1688 now, is_schema_nc, is_forced_rodc,
1693 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1695 uint32_t count = omd.ctr.ctr1.count;
1698 for (i=0; i < count; i++) {
1699 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1700 if (max < m.local_usn) {
1708 * update the replPropertyMetaData object each time we modify an
1709 * object. This is needed for DRS replication, as the merge on the
1710 * client is based on this object
1712 static int replmd_update_rpmd(struct ldb_module *module,
1713 const struct dsdb_schema *schema,
1714 struct ldb_request *req,
1715 const char * const *rename_attrs,
1716 struct ldb_message *msg, uint64_t *seq_num,
1717 time_t t, bool is_schema_nc,
1718 bool *is_urgent, bool *rodc)
1720 const struct ldb_val *omd_value;
1721 enum ndr_err_code ndr_err;
1722 struct replPropertyMetaDataBlob omd;
1725 const struct GUID *our_invocation_id;
1727 const char * const *attrs = NULL;
1728 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1729 struct ldb_result *res;
1730 struct ldb_context *ldb;
1731 struct ldb_message_element *objectclass_el;
1732 enum urgent_situation situation;
1733 bool rmd_is_provided;
1734 bool rmd_is_just_resorted = false;
1735 const char *not_rename_attrs[4 + msg->num_elements];
1736 bool is_forced_rodc = false;
1739 attrs = rename_attrs;
1741 for (i = 0; i < msg->num_elements; i++) {
1742 not_rename_attrs[i] = msg->elements[i].name;
1744 not_rename_attrs[i] = "replPropertyMetaData";
1745 not_rename_attrs[i+1] = "objectClass";
1746 not_rename_attrs[i+2] = "instanceType";
1747 not_rename_attrs[i+3] = NULL;
1748 attrs = not_rename_attrs;
1751 ldb = ldb_module_get_ctx(module);
1753 ret = samdb_rodc(ldb, rodc);
1754 if (ret != LDB_SUCCESS) {
1755 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1760 ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1761 is_forced_rodc = true;
1764 our_invocation_id = samdb_ntds_invocation_id(ldb);
1765 if (!our_invocation_id) {
1766 /* this happens during an initial vampire while
1767 updating the schema */
1768 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1772 unix_to_nt_time(&now, t);
1774 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1775 rmd_is_provided = true;
1776 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1777 rmd_is_just_resorted = true;
1780 rmd_is_provided = false;
1783 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1784 * otherwise we consider we are updating */
1785 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1786 situation = REPL_URGENT_ON_DELETE;
1787 } else if (rename_attrs) {
1788 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1790 situation = REPL_URGENT_ON_UPDATE;
1793 if (rmd_is_provided) {
1794 /* In this case the change_replmetadata control was supplied */
1795 /* We check that it's the only attribute that is provided
1796 * (it's a rare case so it's better to keep the code simplier)
1797 * We also check that the highest local_usn is bigger or the same as
1800 if( msg->num_elements != 1 ||
1801 strncmp(msg->elements[0].name,
1802 "replPropertyMetaData", 20) ) {
1803 DEBUG(0,(__location__ ": changereplmetada control called without "\
1804 "a specified replPropertyMetaData attribute or with others\n"));
1805 return LDB_ERR_OPERATIONS_ERROR;
1807 if (situation != REPL_URGENT_ON_UPDATE) {
1808 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1809 return LDB_ERR_OPERATIONS_ERROR;
1811 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1813 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1814 ldb_dn_get_linearized(msg->dn)));
1815 return LDB_ERR_OPERATIONS_ERROR;
1817 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1818 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1819 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1820 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1821 ldb_dn_get_linearized(msg->dn)));
1822 return LDB_ERR_OPERATIONS_ERROR;
1825 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1826 DSDB_FLAG_NEXT_MODULE |
1827 DSDB_SEARCH_SHOW_RECYCLED |
1828 DSDB_SEARCH_SHOW_EXTENDED_DN |
1829 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1830 DSDB_SEARCH_REVEAL_INTERNALS, req);
1832 if (ret != LDB_SUCCESS) {
1836 if (rmd_is_just_resorted == false) {
1837 *seq_num = find_max_local_usn(omd);
1839 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1842 * The test here now allows for a new
1843 * replPropertyMetaData with no change, if was
1844 * just dbcheck re-sorting the values.
1846 if (*seq_num <= db_seq) {
1847 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1848 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1849 (long long)*seq_num, (long long)db_seq));
1850 return LDB_ERR_OPERATIONS_ERROR;
1855 /* search for the existing replPropertyMetaDataBlob. We need
1856 * to use REVEAL and ask for DNs in storage format to support
1857 * the check for values being the same in
1858 * replmd_update_rpmd_element()
1860 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1861 DSDB_FLAG_NEXT_MODULE |
1862 DSDB_SEARCH_SHOW_RECYCLED |
1863 DSDB_SEARCH_SHOW_EXTENDED_DN |
1864 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1865 DSDB_SEARCH_REVEAL_INTERNALS, req);
1866 if (ret != LDB_SUCCESS) {
1870 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1872 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1873 ldb_dn_get_linearized(msg->dn)));
1874 return LDB_ERR_OPERATIONS_ERROR;
1877 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1878 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1879 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1880 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1881 ldb_dn_get_linearized(msg->dn)));
1882 return LDB_ERR_OPERATIONS_ERROR;
1885 if (omd.version != 1) {
1886 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1887 omd.version, ldb_dn_get_linearized(msg->dn)));
1888 return LDB_ERR_OPERATIONS_ERROR;
1891 for (i=0; i<msg->num_elements;) {
1892 struct ldb_message_element *el = &msg->elements[i];
1893 struct ldb_message_element *old_el;
1895 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1896 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1897 &omd, schema, seq_num,
1902 if (ret != LDB_SUCCESS) {
1906 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1907 *is_urgent = replmd_check_urgent_attribute(el);
1910 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1915 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1917 if (el->num_values != 0) {
1922 ldb_msg_remove_element(msg, el);
1927 * Assert that we have an objectClass attribute - this is major
1928 * corruption if we don't have this!
1930 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1931 if (objectclass_el != NULL) {
1933 * Now check if this objectClass means we need to do urgent replication
1935 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1939 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1940 ldb_asprintf_errstring(ldb, __location__
1941 ": objectClass missing on %s\n",
1942 ldb_dn_get_linearized(msg->dn));
1943 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1947 * replmd_update_rpmd_element has done an update if the
1950 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1951 struct ldb_val *md_value;
1952 struct ldb_message_element *el;
1954 /*if we are RODC and this is a DRSR update then its ok*/
1955 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1956 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
1957 && !is_forced_rodc) {
1958 unsigned instanceType;
1961 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1962 return LDB_ERR_REFERRAL;
1965 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1966 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1967 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1968 "cannot change replicated attribute on partial replica");
1972 md_value = talloc(msg, struct ldb_val);
1973 if (md_value == NULL) {
1975 return LDB_ERR_OPERATIONS_ERROR;
1978 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1979 if (ret != LDB_SUCCESS) {
1980 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1984 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1985 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1986 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1987 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1988 ldb_dn_get_linearized(msg->dn)));
1989 return LDB_ERR_OPERATIONS_ERROR;
1992 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1993 if (ret != LDB_SUCCESS) {
1994 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1995 ldb_dn_get_linearized(msg->dn)));
2000 el->values = md_value;
2006 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2008 int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2010 return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2011 &pdn2->dsdb_dn->extra_part);
2017 get a series of message element values as an array of DNs and GUIDs
2018 the result is sorted by GUID
2020 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2021 struct ldb_message_element *el, struct parsed_dn **pdn,
2022 const char *ldap_oid, struct ldb_request *parent)
2025 bool values_are_sorted = true;
2026 struct ldb_context *ldb = ldb_module_get_ctx(module);
2033 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2035 ldb_module_oom(module);
2036 return LDB_ERR_OPERATIONS_ERROR;
2039 for (i=0; i<el->num_values; i++) {
2040 struct ldb_val *v = &el->values[i];
2043 struct parsed_dn *p;
2047 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2048 if (p->dsdb_dn == NULL) {
2049 return LDB_ERR_INVALID_DN_SYNTAX;
2052 dn = p->dsdb_dn->dn;
2054 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2055 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2056 unlikely(GUID_all_zero(&p->guid))) {
2057 /* we got a DN without a GUID - go find the GUID */
2058 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2059 if (ret != LDB_SUCCESS) {
2060 char *dn_str = NULL;
2061 dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2063 ldb_asprintf_errstring(ldb,
2064 "Unable to find GUID for DN %s\n",
2066 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2067 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2068 ldb_attr_cmp(el->name, "member") == 0) {
2069 return LDB_ERR_UNWILLING_TO_PERFORM;
2073 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2074 if (ret != LDB_SUCCESS) {
2077 } else if (!NT_STATUS_IS_OK(status)) {
2078 return LDB_ERR_OPERATIONS_ERROR;
2080 if (i > 0 && values_are_sorted) {
2081 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2083 values_are_sorted = false;
2086 /* keep a pointer to the original ldb_val */
2089 if (! values_are_sorted) {
2090 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2096 * Get a series of trusted message element values. The result is sorted by
2097 * GUID, even though the GUIDs might not be known. That works because we trust
2098 * the database to give us the elements like that if the
2099 * replmd_private->sorted_links flag is set.
2101 * We also ensure that the links are in the Functional Level 2003
2102 * linked attributes format.
2104 static int get_parsed_dns_trusted(struct ldb_module *module,
2105 struct replmd_private *replmd_private,
2106 TALLOC_CTX *mem_ctx,
2107 struct ldb_message_element *el,
2108 struct parsed_dn **pdn,
2109 const char *ldap_oid,
2110 struct ldb_request *parent)
2119 if (!replmd_private->sorted_links) {
2120 /* We need to sort the list. This is the slow old path we want
2123 ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2125 if (ret != LDB_SUCCESS) {
2129 /* Here we get a list of 'struct parsed_dns' without the parsing */
2130 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
2133 ldb_module_oom(module);
2134 return LDB_ERR_OPERATIONS_ERROR;
2137 for (i = 0; i < el->num_values; i++) {
2138 (*pdn)[i].v = &el->values[i];
2143 * This upgrades links to FL2003 style, and sorts the result
2144 * if that was needed.
2146 * TODO: Add a database feature that asserts we have no FL2000
2147 * style links to avoid this check or add a feature that
2148 * uses a similar check to find sorted/unsorted links
2149 * for an on-the-fly upgrade.
2152 ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2153 *pdn, el->num_values,
2156 if (ret != LDB_SUCCESS) {
2164 Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2165 otherwise an error code. For compatibility the error code differs depending
2166 on whether or not the attribute is "member".
2168 As always, the parsed_dn list is assumed to be sorted.
2170 static int check_parsed_dn_duplicates(struct ldb_module *module,
2171 struct ldb_message_element *el,
2172 struct parsed_dn *pdn)
2175 struct ldb_context *ldb = ldb_module_get_ctx(module);
2177 for (i = 1; i < el->num_values; i++) {
2178 struct parsed_dn *p = &pdn[i];
2179 if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2180 ldb_asprintf_errstring(ldb,
2181 "Linked attribute %s has "
2182 "multiple identical values",
2184 if (ldb_attr_cmp(el->name, "member") == 0) {
2185 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2187 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2195 build a new extended DN, including all meta data fields
2197 RMD_FLAGS = DSDB_RMD_FLAG_* bits
2198 RMD_ADDTIME = originating_add_time
2199 RMD_INVOCID = originating_invocation_id
2200 RMD_CHANGETIME = originating_change_time
2201 RMD_ORIGINATING_USN = originating_usn
2202 RMD_LOCAL_USN = local_usn
2203 RMD_VERSION = version
2205 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2206 struct dsdb_dn *dsdb_dn,
2207 const struct GUID *invocation_id,
2208 uint64_t local_usn, NTTIME nttime)
2210 return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2211 local_usn, local_usn, nttime,
2212 RMD_VERSION_INITIAL, false);
2215 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2216 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2217 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2221 check if any links need upgrading from w2k format
2223 static int replmd_check_upgrade_links(struct ldb_context *ldb,
2224 struct parsed_dn *dns, uint32_t count,
2225 struct ldb_message_element *el,
2226 const char *ldap_oid)
2229 const struct GUID *invocation_id = NULL;
2230 for (i=0; i<count; i++) {
2234 if (dns[i].dsdb_dn == NULL) {
2235 ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2237 if (ret != LDB_SUCCESS) {
2238 return LDB_ERR_INVALID_DN_SYNTAX;
2242 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2243 &version, "RMD_VERSION");
2244 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2246 * We optimistically assume they are all the same; if
2247 * the first one is fixed, they are all fixed.
2249 * If the first one was *not* fixed and we find a
2250 * later one that is, that is an occasion to shout
2256 DEBUG(0, ("Mixed w2k and fixed format "
2257 "linked attributes\n"));
2261 if (invocation_id == NULL) {
2262 invocation_id = samdb_ntds_invocation_id(ldb);
2263 if (invocation_id == NULL) {
2264 return LDB_ERR_OPERATIONS_ERROR;
2269 /* it's an old one that needs upgrading */
2270 ret = replmd_update_la_val(el->values, dns[i].v,
2271 dns[i].dsdb_dn, dns[i].dsdb_dn,
2272 invocation_id, 1, 1, 0, false);
2273 if (ret != LDB_SUCCESS) {
2279 * This sort() is critical for the operation of
2280 * get_parsed_dns_trusted() because callers of this function
2281 * expect a sorted list, and FL2000 style links are not
2282 * sorted. In particular, as well as the upgrade case,
2283 * get_parsed_dns_trusted() is called from
2284 * replmd_delete_remove_link() even in FL2000 mode
2286 * We do not normally pay the cost of the qsort() due to the
2287 * early return in the RMD_VERSION found case.
2289 TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2294 Sets the value for a linked attribute, including all meta data fields
2296 see replmd_build_la_val for value names
2298 static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2299 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2300 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2301 uint32_t version, bool deleted)
2303 struct ldb_dn *dn = dsdb_dn->dn;
2304 const char *tstring, *usn_string, *flags_string;
2305 struct ldb_val tval;
2307 struct ldb_val usnv, local_usnv;
2308 struct ldb_val vers, flagsv;
2309 const struct ldb_val *old_addtime = NULL;
2312 const char *dnstring;
2314 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2316 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2318 return LDB_ERR_OPERATIONS_ERROR;
2320 tval = data_blob_string_const(tstring);
2322 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2324 return LDB_ERR_OPERATIONS_ERROR;
2326 usnv = data_blob_string_const(usn_string);
2328 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2330 return LDB_ERR_OPERATIONS_ERROR;
2332 local_usnv = data_blob_string_const(usn_string);
2334 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2335 if (!NT_STATUS_IS_OK(status)) {
2336 return LDB_ERR_OPERATIONS_ERROR;
2339 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2340 if (!flags_string) {
2341 return LDB_ERR_OPERATIONS_ERROR;
2343 flagsv = data_blob_string_const(flags_string);
2345 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2346 if (ret != LDB_SUCCESS) return ret;
2348 /* get the ADDTIME from the original */
2349 if (old_dsdb_dn != NULL) {
2350 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2353 if (old_addtime == NULL) {
2354 old_addtime = &tval;
2356 if (dsdb_dn != old_dsdb_dn ||
2357 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2358 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2359 if (ret != LDB_SUCCESS) return ret;
2362 /* use our invocation id */
2363 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2364 if (ret != LDB_SUCCESS) return ret;
2366 /* changetime is the current time */
2367 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2368 if (ret != LDB_SUCCESS) return ret;
2370 /* update the USN */
2371 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2372 if (ret != LDB_SUCCESS) return ret;
2374 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2375 if (ret != LDB_SUCCESS) return ret;
2377 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2378 vers = data_blob_string_const(vstring);
2379 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2380 if (ret != LDB_SUCCESS) return ret;
2382 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2383 if (dnstring == NULL) {
2384 return LDB_ERR_OPERATIONS_ERROR;
2386 *v = data_blob_string_const(dnstring);
2392 * Updates the value for a linked attribute, including all meta data fields
2394 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2395 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2396 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2399 uint32_t old_version;
2400 uint32_t version = RMD_VERSION_INITIAL;
2404 * We're updating the linked attribute locally, so increase the version
2405 * by 1 so that other DCs will see the change when it gets replicated out
2407 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2410 if (NT_STATUS_IS_OK(status)) {
2411 version = old_version + 1;
2414 return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2415 usn, local_usn, nttime, version, deleted);
2419 handle adding a linked attribute
2421 static int replmd_modify_la_add(struct ldb_module *module,
2422 struct replmd_private *replmd_private,
2423 struct replmd_replicated_request *ac,
2424 struct ldb_message *msg,
2425 struct ldb_message_element *el,
2426 struct ldb_message_element *old_el,
2427 const struct dsdb_attribute *schema_attr,
2429 struct ldb_dn *msg_dn,
2430 struct ldb_request *parent)
2433 struct parsed_dn *dns, *old_dns;
2434 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2436 struct ldb_val *new_values = NULL;
2437 unsigned old_num_values = old_el ? old_el->num_values : 0;
2438 unsigned num_values = 0;
2439 unsigned max_num_values;
2440 struct ldb_context *ldb = ldb_module_get_ctx(module);
2442 unix_to_nt_time(&now, t);
2444 /* get the DNs to be added, fully parsed.
2446 * We need full parsing because they came off the wire and we don't
2447 * trust them, besides which we need their details to know where to put
2450 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2451 schema_attr->syntax->ldap_oid, parent);
2452 if (ret != LDB_SUCCESS) {
2453 talloc_free(tmp_ctx);
2457 /* get the existing DNs, lazily parsed */
2458 ret = get_parsed_dns_trusted(module, replmd_private,
2459 tmp_ctx, old_el, &old_dns,
2460 schema_attr->syntax->ldap_oid, parent);
2462 if (ret != LDB_SUCCESS) {
2463 talloc_free(tmp_ctx);
2467 max_num_values = old_num_values + el->num_values;
2468 if (max_num_values < old_num_values) {
2469 DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2470 "old values: %u, new values: %u, sum: %u\n",
2471 old_num_values, el->num_values, max_num_values));
2472 talloc_free(tmp_ctx);
2473 return LDB_ERR_OPERATIONS_ERROR;
2476 new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2478 if (new_values == NULL) {
2479 ldb_module_oom(module);
2480 talloc_free(tmp_ctx);
2481 return LDB_ERR_OPERATIONS_ERROR;
2485 * For each new value, find where it would go in the list. If there is
2486 * a matching GUID there, we update the existing value; otherwise we
2490 for (i = 0; i < el->num_values; i++) {
2491 struct parsed_dn *exact;
2492 struct parsed_dn *next;
2494 int err = parsed_dn_find(ldb, old_dns, old_num_values,
2497 dns[i].dsdb_dn->extra_part, 0,
2499 schema_attr->syntax->ldap_oid,
2501 if (err != LDB_SUCCESS) {
2502 talloc_free(tmp_ctx);
2506 if (ac->fix_link_sid) {
2507 char *fixed_dnstring = NULL;
2508 struct dom_sid tmp_sid = { 0, };
2509 DATA_BLOB sid_blob = data_blob_null;
2510 enum ndr_err_code ndr_err;
2514 if (exact == NULL) {
2515 talloc_free(tmp_ctx);
2516 return ldb_operr(ldb);
2519 if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2520 talloc_free(tmp_ctx);
2521 return ldb_operr(ldb);
2525 * Only "<GUID=...><SID=...>" is allowed.
2527 * We get the GUID to just to find the old
2528 * value and the SID in order to add it
2529 * to the found value.
2532 num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2534 talloc_free(tmp_ctx);
2535 return ldb_operr(ldb);
2538 num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2540 talloc_free(tmp_ctx);
2541 return ldb_operr(ldb);
2544 status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2546 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2547 /* this is what we expect */
2548 } else if (NT_STATUS_IS_OK(status)) {
2549 struct GUID_txt_buf guid_str;
2550 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2551 "i[%u] SID NOT MISSING... Attribute %s already "
2552 "exists for target GUID %s, SID %s, DN: %s",
2554 GUID_buf_string(&exact->guid,
2556 dom_sid_string(tmp_ctx, &tmp_sid),
2557 dsdb_dn_get_extended_linearized(tmp_ctx,
2558 exact->dsdb_dn, 1));
2559 talloc_free(tmp_ctx);
2560 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2562 talloc_free(tmp_ctx);
2563 return ldb_operr(ldb);
2566 status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2568 if (!NT_STATUS_IS_OK(status)) {
2569 struct GUID_txt_buf guid_str;
2570 ldb_asprintf_errstring(ldb,
2571 "NO SID PROVIDED... Attribute %s already "
2572 "exists for target GUID %s",
2574 GUID_buf_string(&exact->guid,
2576 talloc_free(tmp_ctx);
2577 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2580 ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2581 (ndr_push_flags_fn_t)ndr_push_dom_sid);
2582 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2583 talloc_free(tmp_ctx);
2584 return ldb_operr(ldb);
2587 ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2588 data_blob_free(&sid_blob);
2589 if (ret != LDB_SUCCESS) {
2590 talloc_free(tmp_ctx);
2594 fixed_dnstring = dsdb_dn_get_extended_linearized(
2595 new_values, exact->dsdb_dn, 1);
2596 if (fixed_dnstring == NULL) {
2597 talloc_free(tmp_ctx);
2598 return ldb_operr(ldb);
2602 * We just replace the existing value...
2604 *exact->v = data_blob_string_const(fixed_dnstring);
2609 if (exact != NULL) {
2611 * We are trying to add one that exists, which is only
2612 * allowed if it was previously deleted.
2614 * When we do undelete a link we change it in place.
2615 * It will be copied across into the right spot in due
2619 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2621 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2622 struct GUID_txt_buf guid_str;
2623 ldb_asprintf_errstring(ldb,
2624 "Attribute %s already "
2625 "exists for target GUID %s",
2627 GUID_buf_string(&exact->guid,
2629 talloc_free(tmp_ctx);
2630 /* error codes for 'member' need to be
2632 if (ldb_attr_cmp(el->name, "member") == 0) {
2633 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2635 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2639 ret = replmd_update_la_val(new_values, exact->v,
2642 &ac->our_invocation_id,
2643 ac->seq_num, ac->seq_num,
2645 if (ret != LDB_SUCCESS) {
2646 talloc_free(tmp_ctx);
2650 ret = replmd_add_backlink(module, replmd_private,
2657 if (ret != LDB_SUCCESS) {
2658 talloc_free(tmp_ctx);
2664 * Here we don't have an exact match.
2666 * If next is NULL, this one goes beyond the end of the
2667 * existing list, so we need to add all of those ones first.
2669 * If next is not NULL, we need to add all the ones before
2673 offset = old_num_values;
2675 /* next should have been parsed, but let's make sure */
2676 if (next->dsdb_dn == NULL) {
2677 ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2678 schema_attr->syntax->ldap_oid);
2679 if (ret != LDB_SUCCESS) {
2683 offset = MIN(next - old_dns, old_num_values);
2686 /* put all the old ones before next on the list */
2687 for (; j < offset; j++) {
2688 new_values[num_values] = *old_dns[j].v;
2692 ret = replmd_add_backlink(module, replmd_private,
2697 /* Make the new linked attribute ldb_val. */
2698 ret = replmd_build_la_val(new_values, &new_values[num_values],
2699 dns[i].dsdb_dn, &ac->our_invocation_id,
2701 if (ret != LDB_SUCCESS) {
2702 talloc_free(tmp_ctx);
2706 if (ret != LDB_SUCCESS) {
2707 talloc_free(tmp_ctx);
2711 /* copy the rest of the old ones (if any) */
2712 for (; j < old_num_values; j++) {
2713 new_values[num_values] = *old_dns[j].v;
2717 talloc_steal(msg->elements, new_values);
2718 if (old_el != NULL) {
2719 talloc_steal(msg->elements, old_el->values);
2721 el->values = new_values;
2722 el->num_values = num_values;
2724 talloc_free(tmp_ctx);
2726 /* we now tell the backend to replace all existing values
2727 with the one we have constructed */
2728 el->flags = LDB_FLAG_MOD_REPLACE;
2735 handle deleting all active linked attributes
2737 static int replmd_modify_la_delete(struct ldb_module *module,
2738 struct replmd_private *replmd_private,
2739 struct replmd_replicated_request *ac,
2740 struct ldb_message *msg,
2741 struct ldb_message_element *el,
2742 struct ldb_message_element *old_el,
2743 const struct dsdb_attribute *schema_attr,
2745 struct ldb_dn *msg_dn,
2746 struct ldb_request *parent)
2749 struct parsed_dn *dns, *old_dns;
2750 TALLOC_CTX *tmp_ctx = NULL;
2752 struct ldb_context *ldb = ldb_module_get_ctx(module);
2753 struct ldb_control *vanish_links_ctrl = NULL;
2754 bool vanish_links = false;
2755 unsigned int num_to_delete = el->num_values;
2759 unix_to_nt_time(&now, t);
2761 if (old_el == NULL || old_el->num_values == 0) {
2762 /* there is nothing to delete... */
2763 if (num_to_delete == 0) {
2764 /* and we're deleting nothing, so that's OK */
2767 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2770 tmp_ctx = talloc_new(msg);
2771 if (tmp_ctx == NULL) {
2772 return LDB_ERR_OPERATIONS_ERROR;
2775 ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2776 schema_attr->syntax->ldap_oid, parent);
2777 if (ret != LDB_SUCCESS) {
2778 talloc_free(tmp_ctx);
2782 ret = get_parsed_dns_trusted(module, replmd_private,
2783 tmp_ctx, old_el, &old_dns,
2784 schema_attr->syntax->ldap_oid, parent);
2786 if (ret != LDB_SUCCESS) {
2787 talloc_free(tmp_ctx);
2792 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2793 if (vanish_links_ctrl) {
2794 vanish_links = true;
2795 vanish_links_ctrl->critical = false;
2799 /* we empty out el->values here to avoid damage if we return early. */
2804 * If vanish links is set, we are actually removing members of
2805 * old_el->values; otherwise we are just marking them deleted.
2807 * There is a special case when no values are given: we remove them
2808 * all. When we have the vanish_links control we just have to remove
2809 * the backlinks and change our element to replace the existing values
2810 * with the empty list.
2813 if (num_to_delete == 0) {
2814 for (i = 0; i < old_el->num_values; i++) {
2815 struct parsed_dn *p = &old_dns[i];
2816 if (p->dsdb_dn == NULL) {
2817 ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2818 schema_attr->syntax->ldap_oid);
2819 if (ret != LDB_SUCCESS) {
2823 ret = replmd_add_backlink(module, replmd_private,
2824 ac->schema, msg_dn, &p->guid,
2827 if (ret != LDB_SUCCESS) {
2828 talloc_free(tmp_ctx);
2835 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2836 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2840 ret = replmd_update_la_val(old_el->values, p->v,
2841 p->dsdb_dn, p->dsdb_dn,
2842 &ac->our_invocation_id,
2843 ac->seq_num, ac->seq_num,
2845 if (ret != LDB_SUCCESS) {
2846 talloc_free(tmp_ctx);
2852 el->flags = LDB_FLAG_MOD_REPLACE;
2853 talloc_free(tmp_ctx);
2859 for (i = 0; i < num_to_delete; i++) {
2860 struct parsed_dn *p = &dns[i];
2861 struct parsed_dn *exact = NULL;
2862 struct parsed_dn *next = NULL;
2863 ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2866 p->dsdb_dn->extra_part, 0,
2868 schema_attr->syntax->ldap_oid,
2870 if (ret != LDB_SUCCESS) {
2871 talloc_free(tmp_ctx);
2874 if (exact == NULL) {
2875 struct GUID_txt_buf buf;
2876 ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2877 "exist for target GUID %s",
2879 GUID_buf_string(&p->guid, &buf));
2880 if (ldb_attr_cmp(el->name, "member") == 0) {
2881 talloc_free(tmp_ctx);
2882 return LDB_ERR_UNWILLING_TO_PERFORM;
2884 talloc_free(tmp_ctx);
2885 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2890 if (CHECK_DEBUGLVL(5)) {
2891 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2892 if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2893 struct GUID_txt_buf buf;
2894 const char *guid_str = \
2895 GUID_buf_string(&p->guid, &buf);
2896 DEBUG(5, ("Deleting deleted linked "
2897 "attribute %s to %s, because "
2898 "vanish_links control is set\n",
2899 el->name, guid_str));
2903 /* remove the backlink */
2904 ret = replmd_add_backlink(module,
2911 if (ret != LDB_SUCCESS) {
2912 talloc_free(tmp_ctx);
2916 /* We flag the deletion and tidy it up later. */
2921 rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2923 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2924 struct GUID_txt_buf buf;
2925 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2926 ldb_asprintf_errstring(ldb, "Attribute %s already "
2927 "deleted for target GUID %s",
2928 el->name, guid_str);
2929 if (ldb_attr_cmp(el->name, "member") == 0) {
2930 talloc_free(tmp_ctx);
2931 return LDB_ERR_UNWILLING_TO_PERFORM;
2933 talloc_free(tmp_ctx);
2934 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2938 ret = replmd_update_la_val(old_el->values, exact->v,
2939 exact->dsdb_dn, exact->dsdb_dn,
2940 &ac->our_invocation_id,
2941 ac->seq_num, ac->seq_num,
2943 if (ret != LDB_SUCCESS) {
2944 talloc_free(tmp_ctx);
2947 ret = replmd_add_backlink(module, replmd_private,
2952 if (ret != LDB_SUCCESS) {
2953 talloc_free(tmp_ctx);
2960 struct ldb_val *tmp_vals = NULL;
2962 tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
2963 old_el->num_values);
2964 if (tmp_vals == NULL) {
2965 talloc_free(tmp_ctx);
2966 return ldb_module_oom(module);
2968 for (i = 0; i < old_el->num_values; i++) {
2969 if (old_dns[i].v == NULL) {
2972 tmp_vals[j] = *old_dns[i].v;
2975 for (i = 0; i < j; i++) {
2976 old_el->values[i] = tmp_vals[i];
2978 old_el->num_values = j;
2981 el->values = talloc_steal(msg->elements, old_el->values);
2982 el->num_values = old_el->num_values;
2984 talloc_free(tmp_ctx);
2986 /* we now tell the backend to replace all existing values
2987 with the one we have constructed */
2988 el->flags = LDB_FLAG_MOD_REPLACE;
2994 handle replacing a linked attribute
2996 static int replmd_modify_la_replace(struct ldb_module *module,
2997 struct replmd_private *replmd_private,
2998 struct replmd_replicated_request *ac,
2999 struct ldb_message *msg,
3000 struct ldb_message_element *el,
3001 struct ldb_message_element *old_el,
3002 const struct dsdb_attribute *schema_attr,
3004 struct ldb_dn *msg_dn,
3005 struct ldb_request *parent)
3007 unsigned int i, old_i, new_i;
3008 struct parsed_dn *dns, *old_dns;
3009 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3011 struct ldb_context *ldb = ldb_module_get_ctx(module);
3012 struct ldb_val *new_values = NULL;
3013 const char *ldap_oid = schema_attr->syntax->ldap_oid;
3014 unsigned int old_num_values;
3015 unsigned int repl_num_values;
3016 unsigned int max_num_values;
3019 unix_to_nt_time(&now, t);
3022 * The replace operation is unlike the replace and delete cases in that
3023 * we need to look at every existing link to see whether it is being
3024 * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3026 * As we are trying to combine two sorted lists, the algorithm we use
3027 * is akin to the merge phase of a merge sort. We interleave the two
3028 * lists, doing different things depending on which side the current
3031 * There are three main cases, with some sub-cases.
3033 * - a DN is in the old list but not the new one. It needs to be
3034 * marked as deleted (but left in the list).
3035 * - maybe it is already deleted, and we have less to do.
3037 * - a DN is in both lists. The old data gets replaced by the new,
3038 * and the list doesn't grow. The old link may have been marked as
3039 * deleted, in which case we undelete it.
3041 * - a DN is in the new list only. We add it in the right place.
3044 old_num_values = old_el ? old_el->num_values : 0;
3045 repl_num_values = el->num_values;
3046 max_num_values = old_num_values + repl_num_values;
3048 if (max_num_values == 0) {
3049 /* There is nothing to do! */
3053 ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3054 if (ret != LDB_SUCCESS) {
3055 talloc_free(tmp_ctx);
3059 ret = check_parsed_dn_duplicates(module, el, dns);
3060 if (ret != LDB_SUCCESS) {
3061 talloc_free(tmp_ctx);
3065 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3067 if (ret != LDB_SUCCESS) {
3068 talloc_free(tmp_ctx);
3072 ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3074 if (ret != LDB_SUCCESS) {
3075 talloc_free(tmp_ctx);
3079 new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3080 if (new_values == NULL) {
3081 ldb_module_oom(module);
3082 talloc_free(tmp_ctx);
3083 return LDB_ERR_OPERATIONS_ERROR;
3088 for (i = 0; i < max_num_values; i++) {
3090 struct parsed_dn *old_p, *new_p;
3091 if (old_i < old_num_values && new_i < repl_num_values) {
3092 old_p = &old_dns[old_i];
3093 new_p = &dns[new_i];
3094 cmp = parsed_dn_compare(old_p, new_p);
3095 } else if (old_i < old_num_values) {
3096 /* the new list is empty, read the old list */
3097 old_p = &old_dns[old_i];
3100 } else if (new_i < repl_num_values) {
3101 /* the old list is empty, read new list */
3103 new_p = &dns[new_i];
3111 * An old ones that come before the next replacement
3112 * (if any). We mark it as deleted and add it to the
3115 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3116 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3117 ret = replmd_update_la_val(new_values, old_p->v,
3120 &ac->our_invocation_id,
3121 ac->seq_num, ac->seq_num,
3123 if (ret != LDB_SUCCESS) {
3124 talloc_free(tmp_ctx);
3128 ret = replmd_add_backlink(module, replmd_private,
3131 &old_p->guid, false,
3134 if (ret != LDB_SUCCESS) {
3135 talloc_free(tmp_ctx);
3139 new_values[i] = *old_p->v;
3141 } else if (cmp == 0) {
3143 * We are overwriting one. If it was previously
3144 * deleted, we need to add a backlink.
3146 * Note that if any RMD_FLAGs in an extended new DN
3151 ret = replmd_update_la_val(new_values, old_p->v,
3154 &ac->our_invocation_id,
3155 ac->seq_num, ac->seq_num,
3157 if (ret != LDB_SUCCESS) {
3158 talloc_free(tmp_ctx);
3162 rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3163 if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3164 ret = replmd_add_backlink(module, replmd_private,
3170 if (ret != LDB_SUCCESS) {
3171 talloc_free(tmp_ctx);
3176 new_values[i] = *old_p->v;
3181 * Replacements that don't match an existing one. We
3182 * just add them to the final list.
3184 ret = replmd_build_la_val(new_values,
3187 &ac->our_invocation_id,
3189 if (ret != LDB_SUCCESS) {
3190 talloc_free(tmp_ctx);
3193 ret = replmd_add_backlink(module, replmd_private,
3199 if (ret != LDB_SUCCESS) {
3200 talloc_free(tmp_ctx);
3203 new_values[i] = *new_p->v;
3207 if (old_el != NULL) {
3208 talloc_steal(msg->elements, old_el->values);
3210 el->values = talloc_steal(msg->elements, new_values);
3212 talloc_free(tmp_ctx);
3214 el->flags = LDB_FLAG_MOD_REPLACE;
3221 handle linked attributes in modify requests
3223 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3224 struct replmd_private *replmd_private,
3225 struct replmd_replicated_request *ac,
3226 struct ldb_message *msg,
3228 struct ldb_request *parent)
3230 struct ldb_result *res;
3233 struct ldb_context *ldb = ldb_module_get_ctx(module);
3234 struct ldb_message *old_msg;
3236 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3238 * Nothing special is required for modifying or vanishing links
3239 * in fl2000 since they are just strings in a multi-valued
3242 struct ldb_control *ctrl = ldb_request_get_control(parent,
3243 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3245 ctrl->critical = false;
3253 * We should restrict this to the intersection of the list of
3254 * linked attributes in the schema and the list of attributes
3257 * This will help performance a little, as otherwise we have
3258 * to allocate the entire object value-by-value.
3260 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3261 DSDB_FLAG_NEXT_MODULE |
3262 DSDB_SEARCH_SHOW_RECYCLED |
3263 DSDB_SEARCH_REVEAL_INTERNALS |
3264 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3266 if (ret != LDB_SUCCESS) {
3270 old_msg = res->msgs[0];
3272 for (i=0; i<msg->num_elements; i++) {
3273 struct ldb_message_element *el = &msg->elements[i];
3274 struct ldb_message_element *old_el, *new_el;
3275 unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3276 const struct dsdb_attribute *schema_attr
3277 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3279 ldb_asprintf_errstring(ldb,
3280 "%s: attribute %s is not a valid attribute in schema",
3281 __FUNCTION__, el->name);
3282 return LDB_ERR_OBJECT_CLASS_VIOLATION;
3284 if (schema_attr->linkID == 0) {
3287 if ((schema_attr->linkID & 1) == 1) {
3289 struct ldb_control *ctrl;
3291 ctrl = ldb_request_get_control(parent,
3292 DSDB_CONTROL_REPLMD_VANISH_LINKS);
3294 ctrl->critical = false;
3297 ctrl = ldb_request_get_control(parent,
3298 DSDB_CONTROL_DBCHECK);
3304 /* Odd is for the target. Illegal to modify */
3305 ldb_asprintf_errstring(ldb,
3306 "attribute %s must not be modified directly, it is a linked attribute", el->name);
3307 return LDB_ERR_UNWILLING_TO_PERFORM;
3309 old_el = ldb_msg_find_element(old_msg, el->name);
3311 case LDB_FLAG_MOD_REPLACE:
3312 ret = replmd_modify_la_replace(module, replmd_private,
3313 ac, msg, el, old_el,
3318 case LDB_FLAG_MOD_DELETE:
3319 ret = replmd_modify_la_delete(module, replmd_private,
3320 ac, msg, el, old_el,
3325 case LDB_FLAG_MOD_ADD:
3326 ret = replmd_modify_la_add(module, replmd_private,
3327 ac, msg, el, old_el,
3333 ldb_asprintf_errstring(ldb,
3334 "invalid flags 0x%x for %s linked attribute",
3335 el->flags, el->name);
3336 return LDB_ERR_UNWILLING_TO_PERFORM;
3338 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
3339 ldb_asprintf_errstring(ldb,
3340 "Attribute %s is single valued but more than one value has been supplied",
3342 /* Return codes as found on Windows 2012r2 */
3343 if (mod_type == LDB_FLAG_MOD_REPLACE) {
3344 return LDB_ERR_CONSTRAINT_VIOLATION;
3346 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3349 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3352 if (ret != LDB_SUCCESS) {
3356 ldb_msg_remove_attr(old_msg, el->name);
3358 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3359 new_el->num_values = el->num_values;
3360 new_el->values = talloc_steal(msg->elements, el->values);
3362 /* TODO: this relises a bit too heavily on the exact
3363 behaviour of ldb_msg_find_element and
3364 ldb_msg_remove_element */
3365 old_el = ldb_msg_find_element(msg, el->name);
3367 ldb_msg_remove_element(msg, old_el);
3377 static int send_rodc_referral(struct ldb_request *req,
3378 struct ldb_context *ldb,
3381 char *referral = NULL;
3382 struct loadparm_context *lp_ctx = NULL;
3383 struct ldb_dn *fsmo_role_dn = NULL;
3384 struct ldb_dn *role_owner_dn = NULL;
3385 const char *domain = NULL;
3388 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3389 struct loadparm_context);
3391 werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3392 &fsmo_role_dn, &role_owner_dn);
3394 if (W_ERROR_IS_OK(werr)) {
3395 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3396 if (server_dn != NULL) {
3397 ldb_dn_remove_child_components(server_dn, 1);
3398 domain = samdb_dn_to_dnshostname(ldb, req,
3403 if (domain == NULL) {
3404 domain = lpcfg_dnsdomain(lp_ctx);
3407 referral = talloc_asprintf(req, "ldap://%s/%s",
3409 ldb_dn_get_linearized(dn));
3410 if (referral == NULL) {
3412 return LDB_ERR_OPERATIONS_ERROR;
3415 return ldb_module_send_referral(req, referral);
3419 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3421 struct ldb_context *ldb;
3422 struct replmd_replicated_request *ac;
3423 struct ldb_request *down_req;
3424 struct ldb_message *msg;
3425 time_t t = time(NULL);
3427 bool is_urgent = false, rodc = false;
3428 bool is_schema_nc = false;
3429 unsigned int functional_level;
3430 const struct ldb_message_element *guid_el = NULL;
3431 struct ldb_control *sd_propagation_control;
3432 struct ldb_control *fix_links_control = NULL;
3433 struct ldb_control *fix_dn_name_control = NULL;
3434 struct ldb_control *fix_dn_sid_control = NULL;
3435 struct replmd_private *replmd_private =
3436 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3438 /* do not manipulate our control entries */
3439 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3440 return ldb_next_request(module, req);
3443 sd_propagation_control = ldb_request_get_control(req,
3444 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3445 if (sd_propagation_control != NULL) {
3446 if (req->op.mod.message->num_elements != 1) {
3447 return ldb_module_operr(module);
3449 ret = strcmp(req->op.mod.message->elements[0].name,
3450 "nTSecurityDescriptor");
3452 return ldb_module_operr(module);
3455 return ldb_next_request(module, req);
3458 ldb = ldb_module_get_ctx(module);
3460 fix_links_control = ldb_request_get_control(req,
3461 DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3462 if (fix_links_control != NULL) {
3463 struct dsdb_schema *schema = NULL;
3464 const struct dsdb_attribute *sa = NULL;
3466 if (req->op.mod.message->num_elements != 1) {
3467 return ldb_module_operr(module);
3470 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
3471 return ldb_module_operr(module);
3474 schema = dsdb_get_schema(ldb, req);
3475 if (schema == NULL) {
3476 return ldb_module_operr(module);
3479 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3480 req->op.mod.message->elements[0].name);
3482 return ldb_module_operr(module);
3485 if (sa->linkID == 0) {
3486 return ldb_module_operr(module);
3489 fix_links_control->critical = false;
3490 return ldb_next_request(module, req);
3493 fix_dn_name_control = ldb_request_get_control(req,
3494 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3495 if (fix_dn_name_control != NULL) {
3496 struct dsdb_schema *schema = NULL;
3497 const struct dsdb_attribute *sa = NULL;
3499 if (req->op.mod.message->num_elements != 2) {
3500 return ldb_module_operr(module);
3503 if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
3504 return ldb_module_operr(module);
3507 if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
3508 return ldb_module_operr(module);
3511 if (req->op.mod.message->elements[0].num_values != 1) {
3512 return ldb_module_operr(module);
3515 if (req->op.mod.message->elements[1].num_values != 1) {
3516 return ldb_module_operr(module);
3519 schema = dsdb_get_schema(ldb, req);
3520 if (schema == NULL) {
3521 return ldb_module_operr(module);
3524 if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3525 req->op.mod.message->elements[1].name) != 0) {
3526 return ldb_module_operr(module);
3529 sa = dsdb_attribute_by_lDAPDisplayName(schema,
3530 req->op.mod.message->elements[0].name);
3532 return ldb_module_operr(module);
3535 if (sa->dn_format == DSDB_INVALID_DN) {
3536 return ldb_module_operr(module);
3539 if (sa->linkID != 0) {
3540 return ldb_module_operr(module);
3544 * If we are run from dbcheck and we are not updating
3545 * a link (as these would need to be sorted and so
3546 * can't go via such a simple update, then do not
3547 * trigger replicated updates and a new USN from this
3548 * change, it wasn't a real change, just a new
3549 * (correct) string DN
3552 fix_dn_name_control->critical = false;
3553 return ldb_next_request(module, req);
3556 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3558 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3559 if (guid_el != NULL) {
3560 ldb_set_errstring(ldb,
3561 "replmd_modify: it's not allowed to change the objectGUID!");
3562 return LDB_ERR_CONSTRAINT_VIOLATION;
3565 ac = replmd_ctx_init(module, req);
3567 return ldb_module_oom(module);
3570 functional_level = dsdb_functional_level(ldb);
3572 /* we have to copy the message as the caller might have it as a const */
3573 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3577 return LDB_ERR_OPERATIONS_ERROR;
3580 fix_dn_sid_control = ldb_request_get_control(req,
3581 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3582 if (fix_dn_sid_control != NULL) {
3583 const struct dsdb_attribute *sa = NULL;
3585 if (msg->num_elements != 1) {
3587 return ldb_module_operr(module);
3590 if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
3592 return ldb_module_operr(module);
3595 if (msg->elements[0].num_values != 1) {
3597 return ldb_module_operr(module);
3600 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3601 msg->elements[0].name);
3604 return ldb_module_operr(module);
3607 if (sa->dn_format != DSDB_NORMAL_DN) {
3609 return ldb_module_operr(module);
3612 fix_dn_sid_control->critical = false;
3613 ac->fix_link_sid = true;
3615 goto handle_linked_attribs;
3618 ldb_msg_remove_attr(msg, "whenChanged");
3619 ldb_msg_remove_attr(msg, "uSNChanged");
3621 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3623 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3624 msg, &ac->seq_num, t, is_schema_nc,
3626 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3627 ret = send_rodc_referral(req, ldb, msg->dn);
3633 if (ret != LDB_SUCCESS) {
3638 handle_linked_attribs:
3639 ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3641 if (ret != LDB_SUCCESS) {
3647 * - replace the old object with the newly constructed one
3650 ac->is_urgent = is_urgent;
3652 ret = ldb_build_mod_req(&down_req, ldb, ac,
3655 ac, replmd_op_callback,
3657 LDB_REQ_SET_LOCATION(down_req);
3658 if (ret != LDB_SUCCESS) {
3663 /* current partition control is needed by "replmd_op_callback" */
3664 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3665 ret = ldb_request_add_control(down_req,
3666 DSDB_CONTROL_CURRENT_PARTITION_OID,
3668 if (ret != LDB_SUCCESS) {
3674 /* If we are in functional level 2000, then
3675 * replmd_modify_handle_linked_attribs will have done
3677 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3678 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3679 if (ret != LDB_SUCCESS) {
3685 talloc_steal(down_req, msg);
3687 /* we only change whenChanged and uSNChanged if the seq_num
3689 if (ac->seq_num != 0) {
3690 ret = add_time_element(msg, "whenChanged", t);
3691 if (ret != LDB_SUCCESS) {
3697 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3698 if (ret != LDB_SUCCESS) {
3705 /* go on with the call chain */
3706 return ldb_next_request(module, down_req);
3709 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3712 handle a rename request
3714 On a rename we need to do an extra ldb_modify which sets the
3715 whenChanged and uSNChanged attributes. We do this in a callback after the success.
3717 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3719 struct ldb_context *ldb;
3720 struct replmd_replicated_request *ac;
3722 struct ldb_request *down_req;
3724 /* do not manipulate our control entries */
3725 if (ldb_dn_is_special(req->op.mod.message->dn)) {
3726 return ldb_next_request(module, req);
3729 ldb = ldb_module_get_ctx(module);
3731 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3733 ac = replmd_ctx_init(module, req);
3735 return ldb_module_oom(module);
3738 ret = ldb_build_rename_req(&down_req, ldb, ac,
3739 ac->req->op.rename.olddn,
3740 ac->req->op.rename.newdn,
3742 ac, replmd_rename_callback,
3744 LDB_REQ_SET_LOCATION(down_req);
3745 if (ret != LDB_SUCCESS) {
3750 /* go on with the call chain */
3751 return ldb_next_request(module, down_req);
3754 /* After the rename is compleated, update the whenchanged etc */
3755 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3757 struct ldb_context *ldb;
3758 struct ldb_request *down_req;
3759 struct ldb_message *msg;
3760 const struct dsdb_attribute *rdn_attr;
3761 const char *rdn_name;
3762 const struct ldb_val *rdn_val;
3763 const char *attrs[5] = { NULL, };
3764 time_t t = time(NULL);
3766 bool is_urgent = false, rodc = false;
3768 struct replmd_replicated_request *ac =
3769 talloc_get_type(req->context, struct replmd_replicated_request);
3770 struct replmd_private *replmd_private =
3771 talloc_get_type(ldb_module_get_private(ac->module),
3772 struct replmd_private);
3774 ldb = ldb_module_get_ctx(ac->module);
3776 if (ares->error != LDB_SUCCESS) {
3777 return ldb_module_done(ac->req, ares->controls,
3778 ares->response, ares->error);
3781 if (ares->type != LDB_REPLY_DONE) {
3782 ldb_set_errstring(ldb,
3783 "invalid ldb_reply_type in callback");
3785 return ldb_module_done(ac->req, NULL, NULL,
3786 LDB_ERR_OPERATIONS_ERROR);
3790 * - replace the old object with the newly constructed one
3793 msg = ldb_msg_new(ac);
3796 return LDB_ERR_OPERATIONS_ERROR;
3799 msg->dn = ac->req->op.rename.newdn;
3801 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3803 rdn_name = ldb_dn_get_rdn_name(msg->dn);
3804 if (rdn_name == NULL) {
3806 return ldb_module_done(ac->req, NULL, NULL,
3810 /* normalize the rdn attribute name */
3811 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3812 if (rdn_attr == NULL) {
3814 return ldb_module_done(ac->req, NULL, NULL,
3817 rdn_name = rdn_attr->lDAPDisplayName;
3819 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3820 if (rdn_val == NULL) {
3822 return ldb_module_done(ac->req, NULL, NULL,
3826 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3828 return ldb_module_done(ac->req, NULL, NULL,
3831 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3833 return ldb_module_done(ac->req, NULL, NULL,
3836 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3838 return ldb_module_done(ac->req, NULL, NULL,
3841 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3843 return ldb_module_done(ac->req, NULL, NULL,
3848 * here we let replmd_update_rpmd() only search for
3849 * the existing "replPropertyMetaData" and rdn_name attributes.
3851 * We do not want the existing "name" attribute as
3852 * the "name" attribute needs to get the version
3853 * updated on rename even if the rdn value hasn't changed.
3855 * This is the diff of the meta data, for a moved user
3856 * on a w2k8r2 server:
3859 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3860 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3861 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3862 * version : 0x00000001 (1)
3863 * reserved : 0x00000000 (0)
3864 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3865 * local_usn : 0x00000000000037a5 (14245)
3866 * array: struct replPropertyMetaData1
3867 * attid : DRSUAPI_ATTID_name (0x90001)
3868 * - version : 0x00000001 (1)
3869 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3870 * + version : 0x00000002 (2)
3871 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3872 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3873 * - originating_usn : 0x00000000000037a5 (14245)
3874 * - local_usn : 0x00000000000037a5 (14245)
3875 * + originating_usn : 0x0000000000003834 (14388)
3876 * + local_usn : 0x0000000000003834 (14388)
3877 * array: struct replPropertyMetaData1
3878 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3879 * version : 0x00000004 (4)
3881 attrs[0] = "replPropertyMetaData";
3882 attrs[1] = "objectClass";
3883 attrs[2] = "instanceType";
3884 attrs[3] = rdn_name;
3887 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3888 msg, &ac->seq_num, t,
3889 is_schema_nc, &is_urgent, &rodc);
3890 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3891 ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3893 return ldb_module_done(req, NULL, NULL, ret);
3896 if (ret != LDB_SUCCESS) {
3898 return ldb_module_done(ac->req, NULL, NULL, ret);
3901 if (ac->seq_num == 0) {
3903 return ldb_module_done(ac->req, NULL, NULL,
3905 "internal error seq_num == 0"));
3907 ac->is_urgent = is_urgent;
3909 ret = ldb_build_mod_req(&down_req, ldb, ac,
3912 ac, replmd_op_callback,
3914 LDB_REQ_SET_LOCATION(down_req);
3915 if (ret != LDB_SUCCESS) {
3920 /* current partition control is needed by "replmd_op_callback" */
3921 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3922 ret = ldb_request_add_control(down_req,
3923 DSDB_CONTROL_CURRENT_PARTITION_OID,
3925 if (ret != LDB_SUCCESS) {
3931 talloc_steal(down_req, msg);
3933 ret = add_time_element(msg, "whenChanged", t);
3934 if (ret != LDB_SUCCESS) {
3940 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3941 if (ret != LDB_SUCCESS) {
3947 /* go on with the call chain - do the modify after the rename */
3948 return ldb_next_request(ac->module, down_req);
3952 * remove links from objects that point at this object when an object
3953 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3954 * RemoveObj which states that link removal due to the object being
3955 * deleted is NOT an originating update - they just go away!
3958 static int replmd_delete_remove_link(struct ldb_module *module,
3959 const struct dsdb_schema *schema,
3960 struct replmd_private *replmd_private,
3963 struct ldb_message_element *el,
3964 const struct dsdb_attribute *sa,
3965 struct ldb_request *parent)
3968 TALLOC_CTX *tmp_ctx = talloc_new(module);
3969 struct ldb_context *ldb = ldb_module_get_ctx(module);
3971 for (i=0; i<el->num_values; i++) {
3972 struct dsdb_dn *dsdb_dn;
3974 struct ldb_message *msg;
3975 const struct dsdb_attribute *target_attr;
3976 struct ldb_message_element *el2;
3978 struct ldb_val dn_val;
3979 uint32_t dsdb_flags = 0;
3980 const char *attrs[] = { NULL, NULL };
3981 struct ldb_result *link_res;
3982 struct ldb_message *link_msg;
3983 struct ldb_message_element *link_el;
3984 struct parsed_dn *link_dns;
3985 struct parsed_dn *p = NULL, *unused = NULL;
3987 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3991 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3993 talloc_free(tmp_ctx);
3994 return LDB_ERR_OPERATIONS_ERROR;
3997 /* remove the link */
3998 msg = ldb_msg_new(tmp_ctx);
4000 ldb_module_oom(module);
4001 talloc_free(tmp_ctx);
4002 return LDB_ERR_OPERATIONS_ERROR;
4006 msg->dn = dsdb_dn->dn;
4008 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4009 if (target_attr == NULL) {
4012 attrs[0] = target_attr->lDAPDisplayName;
4014 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4015 LDB_FLAG_MOD_DELETE, &el2);
4016 if (ret != LDB_SUCCESS) {
4017 ldb_module_oom(module);
4018 talloc_free(tmp_ctx);
4019 return LDB_ERR_OPERATIONS_ERROR;
4022 ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4024 DSDB_FLAG_NEXT_MODULE |
4025 DSDB_SEARCH_SHOW_EXTENDED_DN |
4026 DSDB_SEARCH_SHOW_RECYCLED,
4029 if (ret != LDB_SUCCESS) {
4030 talloc_free(tmp_ctx);
4034 link_msg = link_res->msgs[0];
4035 link_el = ldb_msg_find_element(link_msg,
4036 target_attr->lDAPDisplayName);
4037 if (link_el == NULL) {
4038 talloc_free(tmp_ctx);
4039 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4043 * This call 'upgrades' the links in link_dns, but we
4044 * do not commit the result back into the database, so
4045 * this is safe to call in FL2000 or on databases that
4046 * have been run at that level in the past.
4048 ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
4050 target_attr->syntax->ldap_oid, parent);
4051 if (ret != LDB_SUCCESS) {
4052 talloc_free(tmp_ctx);
4056 ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4060 target_attr->syntax->ldap_oid, false);
4061 if (ret != LDB_SUCCESS) {
4062 talloc_free(tmp_ctx);
4067 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4068 "Failed to find forward link on %s "
4069 "as %s to remove backlink %s on %s",
4070 ldb_dn_get_linearized(msg->dn),
4071 target_attr->lDAPDisplayName,
4072 sa->lDAPDisplayName,
4073 ldb_dn_get_linearized(dn));
4074 talloc_free(tmp_ctx);
4075 return LDB_ERR_NO_SUCH_ATTRIBUTE;
4079 /* This needs to get the Binary DN, by first searching */
4080 dn_str = dsdb_dn_get_linearized(tmp_ctx,
4083 dn_val = data_blob_string_const(dn_str);
4084 el2->values = &dn_val;
4085 el2->num_values = 1;
4088 * Ensure that we tell the modification to vanish any linked
4089 * attributes (not simply mark them as isDeleted = TRUE)
4091 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4093 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4094 if (ret != LDB_SUCCESS) {
4095 talloc_free(tmp_ctx);
4099 talloc_free(tmp_ctx);
4105 handle update of replication meta data for deletion of objects
4107 This also handles the mapping of delete to a rename operation
4108 to allow deletes to be replicated.
4110 It also handles the incoming deleted objects, to ensure they are
4111 fully deleted here. In that case re_delete is true, and we do not
4112 use this as a signal to change the deleted state, just reinforce it.
4115 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4117 int ret = LDB_ERR_OTHER;
4118 bool retb, disallow_move_on_delete;
4119 struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4120 const char *rdn_name;
4121 const struct ldb_val *rdn_value, *new_rdn_value;
4123 struct ldb_context *ldb = ldb_module_get_ctx(module);
4124 const struct dsdb_schema *schema;
4125 struct ldb_message *msg, *old_msg;
4126 struct ldb_message_element *el;
4127 TALLOC_CTX *tmp_ctx;
4128 struct ldb_result *res, *parent_res;
4129 static const char * const preserved_attrs[] = {
4130 /* yes, this really is a hard coded list. See MS-ADTS
4131 section 3.1.1.5.5.1.1 */
4134 "dNReferenceUpdate",
4145 "msDS-LastKnownRDN",
4151 "distinguishedName",
4155 "proxiedObjectName",
4157 "nTSecurityDescriptor",
4158 "replPropertyMetaData",
4160 "securityIdentifier",
4168 "userAccountControl",
4175 static const char * const all_attrs[] = {
4176 DSDB_SECRET_ATTRIBUTES,
4180 static const struct ldb_val true_val = {
4181 .data = discard_const_p(uint8_t, "TRUE"),
4186 uint32_t dsdb_flags = 0;
4187 struct replmd_private *replmd_private;
4188 enum deletion_state deletion_state, next_deletion_state;
4190 if (ldb_dn_is_special(req->op.del.dn)) {
4191 return ldb_next_request(module, req);
4195 * We have to allow dbcheck to remove an object that
4196 * is beyond repair, and to do so totally. This could
4197 * mean we we can get a partial object from the other
4198 * DC, causing havoc, so dbcheck suggests
4199 * re-replication first. dbcheck sets both DBCHECK
4200 * and RELAX in this situation.
4202 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4203 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4204 /* really, really remove it */
4205 return ldb_next_request(module, req);
4208 tmp_ctx = talloc_new(ldb);
4211 return LDB_ERR_OPERATIONS_ERROR;
4214 schema = dsdb_get_schema(ldb, tmp_ctx);
4216 talloc_free(tmp_ctx);
4217 return LDB_ERR_OPERATIONS_ERROR;
4220 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4222 /* we need the complete msg off disk, so we can work out which
4223 attributes need to be removed */
4224 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4225 DSDB_FLAG_NEXT_MODULE |
4226 DSDB_SEARCH_SHOW_RECYCLED |
4227 DSDB_SEARCH_REVEAL_INTERNALS |
4228 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4229 if (ret != LDB_SUCCESS) {
4230 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4231 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4232 re_delete ? "re-delete" : "delete",
4233 ldb_dn_get_linearized(old_dn),
4234 ldb_errstring(ldb_module_get_ctx(module)));
4235 talloc_free(tmp_ctx);
4238 old_msg = res->msgs[0];
4240 replmd_deletion_state(module, old_msg,
4242 &next_deletion_state);
4244 /* This supports us noticing an incoming isDeleted and acting on it */
4246 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4247 next_deletion_state = deletion_state;
4250 if (next_deletion_state == OBJECT_REMOVED) {
4252 * We have to prevent objects being deleted, even if
4253 * the administrator really wants them gone, as
4254 * without the tombstone, we can get a partial object
4255 * from the other DC, causing havoc.
4257 * The only other valid case is when the 180 day
4258 * timeout has expired, when relax is specified.
4260 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4261 /* it is already deleted - really remove it this time */
4262 talloc_free(tmp_ctx);
4263 return ldb_next_request(module, req);
4266 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4267 "This check is to prevent corruption of the replicated state.",
4268 ldb_dn_get_linearized(old_msg->dn));
4269 return LDB_ERR_UNWILLING_TO_PERFORM;
4272 rdn_name = ldb_dn_get_rdn_name(old_dn);
4273 rdn_value = ldb_dn_get_rdn_val(old_dn);
4274 if ((rdn_name == NULL) || (rdn_value == NULL)) {
4275 talloc_free(tmp_ctx);
4276 return ldb_operr(ldb);
4279 msg = ldb_msg_new(tmp_ctx);
4281 ldb_module_oom(module);
4282 talloc_free(tmp_ctx);
4283 return LDB_ERR_OPERATIONS_ERROR;
4288 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4289 disallow_move_on_delete =
4290 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4291 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4293 /* work out where we will be renaming this object to */
4294 if (!disallow_move_on_delete) {
4295 struct ldb_dn *deleted_objects_dn;
4296 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4297 &deleted_objects_dn);
4300 * We should not move objects if we can't find the
4301 * deleted objects DN. Not moving (or otherwise
4302 * harming) the Deleted Objects DN itself is handled
4305 if (re_delete && (ret != LDB_SUCCESS)) {
4306 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4307 if (new_dn == NULL) {
4308 ldb_module_oom(module);
4309 talloc_free(tmp_ctx);
4310 return LDB_ERR_OPERATIONS_ERROR;
4312 } else if (ret != LDB_SUCCESS) {
4313 /* this is probably an attempted delete on a partition
4314 * that doesn't allow delete operations, such as the
4315 * schema partition */
4316 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4317 ldb_dn_get_linearized(old_dn));
4318 talloc_free(tmp_ctx);
4319 return LDB_ERR_UNWILLING_TO_PERFORM;
4321 new_dn = deleted_objects_dn;
4324 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4325 if (new_dn == NULL) {
4326 ldb_module_oom(module);
4327 talloc_free(tmp_ctx);
4328 return LDB_ERR_OPERATIONS_ERROR;
4332 /* get the objects GUID from the search we just did */
4333 guid = samdb_result_guid(old_msg, "objectGUID");
4335 if (deletion_state == OBJECT_NOT_DELETED) {
4336 struct ldb_message_element *is_deleted_el;
4338 ret = replmd_make_deleted_child_dn(tmp_ctx,
4341 rdn_name, rdn_value,
4344 if (ret != LDB_SUCCESS) {
4345 talloc_free(tmp_ctx);
4349 ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4351 if (ret != LDB_SUCCESS) {
4352 ldb_asprintf_errstring(ldb, __location__
4353 ": Failed to add isDeleted string to the msg");
4354 talloc_free(tmp_ctx);
4357 is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4360 * No matter what has happened with other renames etc, try again to
4361 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4364 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4365 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4367 ldb_asprintf_errstring(ldb, __location__
4368 ": Unable to add a prepare rdn of %s",
4369 ldb_dn_get_linearized(rdn));
4370 talloc_free(tmp_ctx);
4371 return LDB_ERR_OPERATIONS_ERROR;
4373 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4375 retb = ldb_dn_add_child(new_dn, rdn);
4377 ldb_asprintf_errstring(ldb, __location__
4378 ": Unable to add rdn %s to base dn: %s",
4379 ldb_dn_get_linearized(rdn),
4380 ldb_dn_get_linearized(new_dn));
4381 talloc_free(tmp_ctx);
4382 return LDB_ERR_OPERATIONS_ERROR;
4387 now we need to modify the object in the following ways:
4389 - add isDeleted=TRUE
4390 - update rDN and name, with new rDN
4391 - remove linked attributes
4392 - remove objectCategory and sAMAccountType
4393 - remove attribs not on the preserved list
4394 - preserved if in above list, or is rDN
4395 - remove all linked attribs from this object
4396 - remove all links from other objects to this object
4397 (note we use the backlinks to do this, so we won't find one-way
4398 links that still point to this object, or deactivated two-way
4399 links, i.e. 'member' after the user has been removed from the
4401 - add lastKnownParent
4402 - update replPropertyMetaData?
4404 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4407 if (deletion_state == OBJECT_NOT_DELETED) {
4408 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4409 char *parent_dn_str = NULL;
4410 struct ldb_message_element *p_el;
4412 /* we need the storage form of the parent GUID */
4413 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4415 DSDB_FLAG_NEXT_MODULE |
4416 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4417 DSDB_SEARCH_REVEAL_INTERNALS|
4418 DSDB_SEARCH_SHOW_RECYCLED, req);
4419 if (ret != LDB_SUCCESS) {
4420 ldb_asprintf_errstring(ldb_module_get_ctx(module),
4421 "repmd_delete: Failed to %s %s, "
4422 "because we failed to find it's parent (%s): %s",
4423 re_delete ? "re-delete" : "delete",
4424 ldb_dn_get_linearized(old_dn),
4425 ldb_dn_get_linearized(parent_dn),
4426 ldb_errstring(ldb_module_get_ctx(module)));
4427 talloc_free(tmp_ctx);
4432 * Now we can use the DB version,
4433 * it will have the extended DN info in it
4435 parent_dn = parent_res->msgs[0]->dn;
4436 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4439 if (parent_dn_str == NULL) {
4440 talloc_free(tmp_ctx);
4441 return ldb_module_oom(module);
4444 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4446 if (ret != LDB_SUCCESS) {
4447 ldb_asprintf_errstring(ldb, __location__
4448 ": Failed to add lastKnownParent "
4449 "string when deleting %s",
4450 ldb_dn_get_linearized(old_dn));
4451 talloc_free(tmp_ctx);
4454 p_el = ldb_msg_find_element(msg,
4457 talloc_free(tmp_ctx);
4458 return ldb_module_operr(module);
4460 p_el->flags = LDB_FLAG_MOD_REPLACE;
4462 if (next_deletion_state == OBJECT_DELETED) {
4463 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4464 if (ret != LDB_SUCCESS) {
4465 ldb_asprintf_errstring(ldb, __location__
4466 ": Failed to add msDS-LastKnownRDN "
4467 "string when deleting %s",
4468 ldb_dn_get_linearized(old_dn));
4469 talloc_free(tmp_ctx);
4472 p_el = ldb_msg_find_element(msg,
4473 "msDS-LastKnownRDN");
4475 talloc_free(tmp_ctx);
4476 return ldb_module_operr(module);
4478 p_el->flags = LDB_FLAG_MOD_ADD;
4482 switch (next_deletion_state) {
4484 case OBJECT_RECYCLED:
4485 case OBJECT_TOMBSTONE:
4488 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4489 * describes what must be removed from a tombstone
4492 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4493 * describes what must be removed from a recycled
4499 * we also mark it as recycled, meaning this object can't be
4500 * recovered (we are stripping its attributes).
4501 * This is done only if we have this schema object of course ...
4502 * This behavior is identical to the one of Windows 2008R2 which
4503 * always set the isRecycled attribute, even if the recycle-bin is
4504 * not activated and what ever the forest level is.
4506 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4507 struct ldb_message_element *is_recycled_el;
4509 ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4511 if (ret != LDB_SUCCESS) {
4512 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4513 ldb_module_oom(module);
4514 talloc_free(tmp_ctx);
4517 is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4520 replmd_private = talloc_get_type(ldb_module_get_private(module),
4521 struct replmd_private);
4522 /* work out which of the old attributes we will be removing */
4523 for (i=0; i<old_msg->num_elements; i++) {
4524 const struct dsdb_attribute *sa;
4525 el = &old_msg->elements[i];
4526 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4528 talloc_free(tmp_ctx);
4529 return LDB_ERR_OPERATIONS_ERROR;
4531 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4532 /* don't remove the rDN */
4536 if (sa->linkID & 1) {
4538 * we have a backlink in this object
4539 * that needs to be removed. We're not
4540 * allowed to remove it directly
4541 * however, so we instead setup a
4542 * modify to delete the corresponding
4545 ret = replmd_delete_remove_link(module, schema,
4549 if (ret == LDB_SUCCESS) {
4551 * now we continue, which means we
4552 * won't remove this backlink
4558 if (ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
4559 const char *old_dn_str
4560 = ldb_dn_get_linearized(old_dn);
4561 ldb_asprintf_errstring(ldb,
4563 ": Failed to remove backlink of "
4564 "%s when deleting %s: %s",
4567 ldb_errstring(ldb));
4568 talloc_free(tmp_ctx);
4569 return LDB_ERR_OPERATIONS_ERROR;
4573 * Otherwise vanish the link, we are
4574 * out of sync and the controlling
4575 * object does not have the source
4579 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4581 } else if (sa->linkID == 0) {
4582 if (ldb_attr_in_list(preserved_attrs, el->name)) {
4585 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4590 * Ensure that we tell the modification to vanish any linked
4591 * attributes (not simply mark them as isDeleted = TRUE)
4593 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4595 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4596 if (ret != LDB_SUCCESS) {
4597 talloc_free(tmp_ctx);
4598 ldb_module_oom(module);
4605 case OBJECT_DELETED:
4607 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4608 * describes what must be removed from a deleted
4612 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4613 if (ret != LDB_SUCCESS) {
4614 talloc_free(tmp_ctx);
4615 ldb_module_oom(module);
4619 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4620 if (ret != LDB_SUCCESS) {
4621 talloc_free(tmp_ctx);
4622 ldb_module_oom(module);
4632 if (deletion_state == OBJECT_NOT_DELETED) {
4633 const struct dsdb_attribute *sa;
4635 /* work out what the new rdn value is, for updating the
4636 rDN and name fields */
4637 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4638 if (new_rdn_value == NULL) {
4639 talloc_free(tmp_ctx);
4640 return ldb_operr(ldb);
4643 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4645 talloc_free(tmp_ctx);
4646 return LDB_ERR_OPERATIONS_ERROR;
4649 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4651 if (ret != LDB_SUCCESS) {
4652 talloc_free(tmp_ctx);
4655 el->flags = LDB_FLAG_MOD_REPLACE;
4657 el = ldb_msg_find_element(old_msg, "name");
4659 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4660 if (ret != LDB_SUCCESS) {
4661 talloc_free(tmp_ctx);
4664 el->flags = LDB_FLAG_MOD_REPLACE;
4669 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4674 * No matter what has happned with other renames, try again to
4675 * get this to be under the deleted DN.
4677 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4678 /* now rename onto the new DN */
4679 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4680 if (ret != LDB_SUCCESS){
4681 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4682 ldb_dn_get_linearized(old_dn),
4683 ldb_dn_get_linearized(new_dn),
4684 ldb_errstring(ldb)));
4685 talloc_free(tmp_ctx);
4691 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4692 if (ret != LDB_SUCCESS) {
4693 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
4694 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
4695 talloc_free(tmp_ctx);
4699 talloc_free(tmp_ctx);
4701 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4704 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4706 return replmd_delete_internals(module, req, false);
4710 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4715 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4717 int ret = LDB_ERR_OTHER;
4718 /* TODO: do some error mapping */
4720 /* Let the caller know the full WERROR */
4721 ar->objs->error = status;
4727 static struct replPropertyMetaData1 *
4728 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4729 enum drsuapi_DsAttributeId attid)
4732 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4734 for (i = 0; i < rpmd_ctr->count; i++) {
4735 if (rpmd_ctr->array[i].attid == attid) {
4736 return &rpmd_ctr->array[i];
4744 return true if an update is newer than an existing entry
4745 see section 5.11 of MS-ADTS
4747 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4748 const struct GUID *update_invocation_id,
4749 uint32_t current_version,
4750 uint32_t update_version,
4751 NTTIME current_change_time,
4752 NTTIME update_change_time)
4754 if (update_version != current_version) {
4755 return update_version > current_version;
4757 if (update_change_time != current_change_time) {
4758 return update_change_time > current_change_time;
4760 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4763 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4764 struct replPropertyMetaData1 *new_m)
4766 return replmd_update_is_newer(&cur_m->originating_invocation_id,
4767 &new_m->originating_invocation_id,
4770 cur_m->originating_change_time,
4771 new_m->originating_change_time);
4774 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4775 struct replPropertyMetaData1 *cur_m,
4776 struct replPropertyMetaData1 *new_m)
4781 * If the new replPropertyMetaData entry for this attribute is
4782 * not provided (this happens in the case where we look for
4783 * ATTID_name, but the name was not changed), then the local
4784 * state is clearly still current, as the remote
4785 * server didn't send it due to being older the high watermark
4788 if (new_m == NULL) {
4792 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4794 * if we compare equal then do an
4795 * update. This is used when a client
4796 * asks for a FULL_SYNC, and can be
4797 * used to recover a corrupt
4800 * This call is a bit tricky, what we
4801 * are doing it turning the 'is_newer'
4802 * call into a 'not is older' by
4803 * swapping cur_m and new_m, and negating the
4806 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4809 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4817 form a DN for a deleted (DEL:) or conflict (CNF:) DN
4819 static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4820 struct ldb_context *ldb,
4822 const char *four_char_prefix,
4823 const char *rdn_name,
4824 const struct ldb_val *rdn_value,
4827 struct ldb_val deleted_child_rdn_val;
4828 struct GUID_txt_buf guid_str;
4832 GUID_buf_string(&guid, &guid_str);
4834 retb = ldb_dn_add_child_fmt(dn, "X=Y");
4836 ldb_asprintf_errstring(ldb, __location__
4837 ": Unable to add a formatted child to dn: %s",
4838 ldb_dn_get_linearized(dn));
4839 return LDB_ERR_OPERATIONS_ERROR;
4843 * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4844 * we should truncate this value to ensure the RDN is not more than 255 chars.
4846 * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
4848 * "Naming constraints are not enforced for replicated
4849 * updates." so this is safe and we don't have to work out not
4850 * splitting a UTF8 char right now.
4852 deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
4855 * sizeof(guid_str.buf) will always be longer than
4856 * strlen(guid_str.buf) but we allocate using this and
4857 * waste the trailing bytes to avoid scaring folks
4858 * with memcpy() using strlen() below
4861 deleted_child_rdn_val.data
4862 = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
4864 rdn_value->length + 5
4865 + sizeof(guid_str.buf));
4866 if (!deleted_child_rdn_val.data) {
4867 ldb_asprintf_errstring(ldb, __location__
4868 ": Unable to add a formatted child to dn: %s",
4869 ldb_dn_get_linearized(dn));
4870 return LDB_ERR_OPERATIONS_ERROR;
4873 deleted_child_rdn_val.length =
4874 rdn_value->length + 5
4875 + strlen(guid_str.buf);
4877 SMB_ASSERT(deleted_child_rdn_val.length <
4878 talloc_get_size(deleted_child_rdn_val.data));
4881 * talloc won't allocate more than 256MB so we can't
4882 * overflow but just to be sure
4884 if (deleted_child_rdn_val.length < rdn_value->length) {
4885 return LDB_ERR_OPERATIONS_ERROR;
4888 deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
4889 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
4890 four_char_prefix, 4);
4891 memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
4893 sizeof(guid_str.buf));
4895 /* Now set the value into the RDN, without parsing it */
4896 ret = ldb_dn_set_component(
4900 deleted_child_rdn_val);
4909 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
4910 struct ldb_context *ldb,
4914 const struct ldb_val *rdn_val;
4915 const char *rdn_name;
4916 struct ldb_dn *new_dn;
4919 rdn_val = ldb_dn_get_rdn_val(dn);
4920 rdn_name = ldb_dn_get_rdn_name(dn);
4921 if (!rdn_val || !rdn_name) {
4925 new_dn = ldb_dn_get_parent(mem_ctx, dn);
4930 ret = replmd_make_prefix_child_dn(mem_ctx,
4936 if (ret != LDB_SUCCESS) {
4945 static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
4946 struct ldb_context *ldb,
4948 const char *rdn_name,
4949 const struct ldb_val *rdn_value,
4952 return replmd_make_prefix_child_dn(tmp_ctx,
4962 perform a modify operation which sets the rDN and name attributes to
4963 their current values. This has the effect of changing these
4964 attributes to have been last updated by the current DC. This is
4965 needed to ensure that renames performed as part of conflict
4966 resolution are propagated to other DCs
4968 static int replmd_name_modify(struct replmd_replicated_request *ar,
4969 struct ldb_request *req, struct ldb_dn *dn)
4971 struct ldb_message *msg;
4972 const char *rdn_name;
4973 const struct ldb_val *rdn_val;
4974 const struct dsdb_attribute *rdn_attr;
4977 msg = ldb_msg_new(req);
4983 rdn_name = ldb_dn_get_rdn_name(dn);
4984 if (rdn_name == NULL) {
4988 /* normalize the rdn attribute name */
4989 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4990 if (rdn_attr == NULL) {
4993 rdn_name = rdn_attr->lDAPDisplayName;
4995 rdn_val = ldb_dn_get_rdn_val(dn);
4996 if (rdn_val == NULL) {
5000 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5003 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
5006 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
5009 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
5014 * We have to mark this as a replicated update otherwise
5015 * schema_data may reject a rename in the schema partition
5018 ret = dsdb_module_modify(ar->module, msg,
5019 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5021 if (ret != LDB_SUCCESS) {
5022 DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5023 ldb_dn_get_linearized(dn),
5024 ldb_errstring(ldb_module_get_ctx(ar->module))));
5034 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5035 ldb_dn_get_linearized(dn)));
5036 return LDB_ERR_OPERATIONS_ERROR;
5041 callback for conflict DN handling where we have renamed the incoming
5042 record. After renaming it, we need to ensure the change of name and
5043 rDN for the incoming record is seen as an originating update by this DC.
5045 This also handles updating lastKnownParent for entries sent to lostAndFound
5047 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5049 struct replmd_replicated_request *ar =
5050 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5051 struct ldb_dn *conflict_dn = NULL;
5054 if (ares->error != LDB_SUCCESS) {
5055 /* call the normal callback for everything except success */
5056 return replmd_op_callback(req, ares);
5059 switch (req->operation) {
5061 conflict_dn = req->op.add.message->dn;
5064 conflict_dn = req->op.mod.message->dn;
5067 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5070 /* perform a modify of the rDN and name of the record */
5071 ret = replmd_name_modify(ar, req, conflict_dn);
5072 if (ret != LDB_SUCCESS) {
5074 return replmd_op_callback(req, ares);
5077 if (ar->objs->objects[ar->index_current].last_known_parent) {
5078 struct ldb_message *msg = ldb_msg_new(req);
5080 ldb_module_oom(ar->module);
5081 return LDB_ERR_OPERATIONS_ERROR;
5084 msg->dn = req->op.add.message->dn;
5086 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5087 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5088 if (ret != LDB_SUCCESS) {
5089 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5090 ldb_module_oom(ar->module);
5093 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5095 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5096 if (ret != LDB_SUCCESS) {
5097 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5098 ldb_dn_get_linearized(msg->dn),
5099 ldb_errstring(ldb_module_get_ctx(ar->module))));
5105 return replmd_op_callback(req, ares);
5109 callback for replmd_replicated_apply_add()
5110 This copes with the creation of conflict records in the case where
5111 the DN exists, but with a different objectGUID
5113 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))
5115 struct ldb_dn *conflict_dn;
5116 struct replmd_replicated_request *ar =
5117 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5118 struct ldb_result *res;
5119 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5121 const struct ldb_val *omd_value;
5122 struct replPropertyMetaDataBlob omd, *rmd;
5123 enum ndr_err_code ndr_err;
5124 bool rename_incoming_record, rodc;
5125 struct replPropertyMetaData1 *rmd_name, *omd_name;
5126 struct ldb_message *msg;
5127 struct ldb_request *down_req = NULL;
5129 /* call the normal callback for success */
5130 if (ares->error == LDB_SUCCESS) {
5131 return callback(req, ares);
5135 * we have a conflict, and need to decide if we will keep the
5136 * new record or the old record
5139 msg = ar->objs->objects[ar->index_current].msg;
5140 conflict_dn = msg->dn;
5142 /* For failures other than conflicts, fail the whole operation here */
5143 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5144 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5145 ldb_dn_get_linearized(conflict_dn),
5146 ldb_errstring(ldb_module_get_ctx(ar->module)));
5148 return ldb_module_done(ar->req, NULL, NULL,
5149 LDB_ERR_OPERATIONS_ERROR);
5152 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5153 if (ret != LDB_SUCCESS) {
5154 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)));
5155 return ldb_module_done(ar->req, NULL, NULL,
5156 LDB_ERR_OPERATIONS_ERROR);
5162 * We are on an RODC, or were a GC for this
5163 * partition, so we have to fail this until
5164 * someone who owns the partition sorts it
5167 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5168 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
5169 " - We must fail the operation until a master for this partition resolves the conflict",
5170 ldb_dn_get_linearized(conflict_dn));
5171 ret = LDB_ERR_OPERATIONS_ERROR;
5176 * first we need the replPropertyMetaData attribute from the
5177 * local, conflicting record
5179 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
5181 DSDB_FLAG_NEXT_MODULE |
5182 DSDB_SEARCH_SHOW_DELETED |
5183 DSDB_SEARCH_SHOW_RECYCLED, req);
5184 if (ret != LDB_SUCCESS) {
5185 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5186 ldb_dn_get_linearized(conflict_dn)));
5190 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5191 if (omd_value == NULL) {
5192 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5193 ldb_dn_get_linearized(conflict_dn)));
5197 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5198 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5199 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5200 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5201 ldb_dn_get_linearized(conflict_dn)));
5205 rmd = ar->objs->objects[ar->index_current].meta_data;
5208 * we decide which is newer based on the RPMD on the name
5209 * attribute. See [MS-DRSR] ResolveNameConflict.
5211 * We expect omd_name to be present, as this is from a local
5212 * search, but while rmd_name should have been given to us by
5213 * the remote server, if it is missing we just prefer the
5215 * replmd_replPropertyMetaData1_new_should_be_taken()
5217 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5218 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5220 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5221 ldb_dn_get_linearized(conflict_dn)));
5226 * Should we preserve the current record, and so rename the
5227 * incoming record to be a conflict?
5229 rename_incoming_record
5230 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5231 omd_name, rmd_name);
5233 if (rename_incoming_record) {
5235 struct ldb_dn *new_dn;
5237 guid = samdb_result_guid(msg, "objectGUID");
5238 if (GUID_all_zero(&guid)) {
5239 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5240 ldb_dn_get_linearized(conflict_dn)));
5243 new_dn = replmd_conflict_dn(req,
5244 ldb_module_get_ctx(ar->module),
5245 conflict_dn, &guid);
5246 if (new_dn == NULL) {
5247 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5248 ldb_dn_get_linearized(conflict_dn)));
5252 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5253 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5255 /* re-submit the request, but with the new DN */
5256 callback = replmd_op_name_modify_callback;
5259 /* we are renaming the existing record */
5261 struct ldb_dn *new_dn;
5263 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5264 if (GUID_all_zero(&guid)) {
5265 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5266 ldb_dn_get_linearized(conflict_dn)));
5270 new_dn = replmd_conflict_dn(req,
5271 ldb_module_get_ctx(ar->module),
5272 conflict_dn, &guid);
5273 if (new_dn == NULL) {
5274 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5275 ldb_dn_get_linearized(conflict_dn)));
5279 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5280 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5282 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5283 DSDB_FLAG_OWN_MODULE, req);
5284 if (ret != LDB_SUCCESS) {
5285 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5286 ldb_dn_get_linearized(conflict_dn),
5287 ldb_dn_get_linearized(new_dn),
5288 ldb_errstring(ldb_module_get_ctx(ar->module))));
5293 * now we need to ensure that the rename is seen as an
5294 * originating update. We do that with a modify.
5296 ret = replmd_name_modify(ar, req, new_dn);
5297 if (ret != LDB_SUCCESS) {
5301 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5302 ldb_dn_get_linearized(req->op.add.message->dn)));
5305 ret = ldb_build_add_req(&down_req,
5306 ldb_module_get_ctx(ar->module),
5313 if (ret != LDB_SUCCESS) {
5316 LDB_REQ_SET_LOCATION(down_req);
5318 /* current partition control needed by "repmd_op_callback" */
5319 ret = ldb_request_add_control(down_req,
5320 DSDB_CONTROL_CURRENT_PARTITION_OID,
5322 if (ret != LDB_SUCCESS) {
5323 return replmd_replicated_request_error(ar, ret);
5326 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5327 /* this tells the partition module to make it a
5328 partial replica if creating an NC */
5329 ret = ldb_request_add_control(down_req,
5330 DSDB_CONTROL_PARTIAL_REPLICA,
5332 if (ret != LDB_SUCCESS) {
5333 return replmd_replicated_request_error(ar, ret);
5338 * Finally we re-run the add, otherwise the new record won't
5339 * exist, as we are here because of that exact failure!
5341 return ldb_next_request(ar->module, down_req);
5344 /* on failure make the caller get the error. This means
5345 * replication will stop with an error, but there is not much
5348 if (ret == LDB_SUCCESS) {
5349 ret = LDB_ERR_OPERATIONS_ERROR;
5351 return ldb_module_done(ar->req, NULL, NULL,
5356 callback for replmd_replicated_apply_add()
5357 This copes with the creation of conflict records in the case where
5358 the DN exists, but with a different objectGUID
5360 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5362 struct replmd_replicated_request *ar =
5363 talloc_get_type_abort(req->context, struct replmd_replicated_request);
5365 if (ar->objs->objects[ar->index_current].last_known_parent) {
5366 /* This is like a conflict DN, where we put the object in LostAndFound
5367 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5368 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5371 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5375 this is called when a new object comes in over DRS
5377 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5379 struct ldb_context *ldb;
5380 struct ldb_request *change_req;
5381 enum ndr_err_code ndr_err;
5382 struct ldb_message *msg;
5383 struct replPropertyMetaDataBlob *md;
5384 struct ldb_val md_value;
5387 bool remote_isDeleted = false;
5390 time_t t = time(NULL);
5391 const struct ldb_val *rdn_val;
5392 struct replmd_private *replmd_private =
5393 talloc_get_type(ldb_module_get_private(ar->module),
5394 struct replmd_private);
5395 unix_to_nt_time(&now, t);
5397 ldb = ldb_module_get_ctx(ar->module);
5398 msg = ar->objs->objects[ar->index_current].msg;
5399 md = ar->objs->objects[ar->index_current].meta_data;
5400 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5402 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5403 if (ret != LDB_SUCCESS) {
5404 return replmd_replicated_request_error(ar, ret);
5407 ret = dsdb_msg_add_guid(msg,
5408 &ar->objs->objects[ar->index_current].object_guid,
5410 if (ret != LDB_SUCCESS) {
5411 return replmd_replicated_request_error(ar, ret);
5414 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5415 if (ret != LDB_SUCCESS) {
5416 return replmd_replicated_request_error(ar, ret);
5419 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5420 if (ret != LDB_SUCCESS) {
5421 return replmd_replicated_request_error(ar, ret);
5424 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5425 if (ret != LDB_SUCCESS) {
5426 return replmd_replicated_request_error(ar, ret);
5429 /* remove any message elements that have zero values */
5430 for (i=0; i<msg->num_elements; i++) {
5431 struct ldb_message_element *el = &msg->elements[i];
5433 if (el->num_values == 0) {
5434 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5435 ldb_asprintf_errstring(ldb, __location__
5436 ": empty objectClass sent on %s, aborting replication\n",
5437 ldb_dn_get_linearized(msg->dn));
5438 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5441 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5443 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
5444 msg->num_elements--;
5451 struct GUID_txt_buf guid_txt;
5453 char *s = ldb_ldif_message_redacted_string(ldb, ar,
5456 DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5457 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5460 } else if (DEBUGLVL(4)) {
5461 struct GUID_txt_buf guid_txt;
5462 DEBUG(4, ("DRS replication add DN of %s is %s\n",
5463 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5464 ldb_dn_get_linearized(msg->dn)));
5466 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5467 "isDeleted", false);
5470 * the meta data array is already sorted by the caller, except
5471 * for the RDN, which needs to be added.
5475 rdn_val = ldb_dn_get_rdn_val(msg->dn);
5476 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5477 md, ar, now, is_schema_nc,
5479 if (ret != LDB_SUCCESS) {
5480 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5481 return replmd_replicated_request_error(ar, ret);
5484 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5485 if (ret != LDB_SUCCESS) {
5486 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5487 return replmd_replicated_request_error(ar, ret);
5490 for (i=0; i < md->ctr.ctr1.count; i++) {
5491 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5493 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5494 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5495 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5496 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5497 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5499 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5500 if (ret != LDB_SUCCESS) {
5501 return replmd_replicated_request_error(ar, ret);
5504 replmd_ldb_message_sort(msg, ar->schema);
5506 if (!remote_isDeleted) {
5507 ret = dsdb_module_schedule_sd_propagation(ar->module,
5508 ar->objs->partition_dn,
5510 if (ret != LDB_SUCCESS) {
5511 return replmd_replicated_request_error(ar, ret);
5515 ar->isDeleted = remote_isDeleted;
5517 ret = ldb_build_add_req(&change_req,
5523 replmd_op_add_callback,
5525 LDB_REQ_SET_LOCATION(change_req);
5526 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5528 /* current partition control needed by "repmd_op_callback" */
5529 ret = ldb_request_add_control(change_req,
5530 DSDB_CONTROL_CURRENT_PARTITION_OID,
5532 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5534 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5535 /* this tells the partition module to make it a
5536 partial replica if creating an NC */
5537 ret = ldb_request_add_control(change_req,
5538 DSDB_CONTROL_PARTIAL_REPLICA,
5540 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5543 return ldb_next_request(ar->module, change_req);
5546 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5547 struct ldb_reply *ares)
5549 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5550 struct replmd_replicated_request);
5554 return ldb_module_done(ar->req, NULL, NULL,
5555 LDB_ERR_OPERATIONS_ERROR);
5559 * The error NO_SUCH_OBJECT is not expected, unless the search
5560 * base is the partition DN, and that case doesn't happen here
5561 * because then we wouldn't get a parent_guid_value in any
5564 if (ares->error != LDB_SUCCESS) {
5565 return ldb_module_done(ar->req, ares->controls,
5566 ares->response, ares->error);
5569 switch (ares->type) {
5570 case LDB_REPLY_ENTRY:
5572 struct ldb_message *parent_msg = ares->message;
5573 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5574 struct ldb_dn *parent_dn = NULL;
5577 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5578 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5579 /* Per MS-DRSR 4.1.10.6.10
5580 * FindBestParentObject we need to move this
5581 * new object under a deleted object to
5583 struct ldb_dn *nc_root;
5585 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5586 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5587 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5588 "No suitable NC root found for %s. "
5589 "We need to move this object because parent object %s "
5590 "is deleted, but this object is not.",
5591 ldb_dn_get_linearized(msg->dn),
5592 ldb_dn_get_linearized(parent_msg->dn));
5593 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5594 } else if (ret != LDB_SUCCESS) {
5595 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5596 "Unable to find NC root for %s: %s. "
5597 "We need to move this object because parent object %s "
5598 "is deleted, but this object is not.",
5599 ldb_dn_get_linearized(msg->dn),
5600 ldb_errstring(ldb_module_get_ctx(ar->module)),
5601 ldb_dn_get_linearized(parent_msg->dn));
5602 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5605 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5607 DS_GUID_LOSTANDFOUND_CONTAINER,
5609 if (ret != LDB_SUCCESS) {
5610 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5611 "Unable to find LostAndFound Container for %s "
5612 "in partition %s: %s. "
5613 "We need to move this object because parent object %s "
5614 "is deleted, but this object is not.",
5615 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5616 ldb_errstring(ldb_module_get_ctx(ar->module)),
5617 ldb_dn_get_linearized(parent_msg->dn));
5618 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5620 ar->objs->objects[ar->index_current].last_known_parent
5621 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5625 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5628 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5630 comp_num = ldb_dn_get_comp_num(msg->dn);
5632 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5634 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5637 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5639 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5643 case LDB_REPLY_REFERRAL:
5644 /* we ignore referrals */
5647 case LDB_REPLY_DONE:
5649 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5650 struct GUID_txt_buf str_buf;
5651 if (ar->search_msg != NULL) {
5652 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5653 "No parent with GUID %s found for object locally known as %s",
5654 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5655 ldb_dn_get_linearized(ar->search_msg->dn));
5657 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5658 "No parent with GUID %s found for object remotely known as %s",
5659 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5660 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5664 * This error code is really important, as it
5665 * is the flag back to the callers to retry
5666 * this with DRSUAPI_DRS_GET_ANC, and so get
5667 * the parent objects before the child
5670 return ldb_module_done(ar->req, NULL, NULL,
5671 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5674 if (ar->search_msg != NULL) {
5675 ret = replmd_replicated_apply_merge(ar);
5677 ret = replmd_replicated_apply_add(ar);
5679 if (ret != LDB_SUCCESS) {
5680 return ldb_module_done(ar->req, NULL, NULL, ret);
5689 * Look for the parent object, so we put the new object in the right
5690 * place This is akin to NameObject in MS-DRSR - this routine and the
5691 * callbacks find the right parent name, and correct name for this
5695 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5697 struct ldb_context *ldb;
5701 struct ldb_request *search_req;
5702 static const char *attrs[] = {"isDeleted", NULL};
5703 struct GUID_txt_buf guid_str_buf;
5705 ldb = ldb_module_get_ctx(ar->module);
5707 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5708 if (ar->search_msg != NULL) {
5709 return replmd_replicated_apply_merge(ar);
5711 return replmd_replicated_apply_add(ar);
5715 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5718 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5719 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5721 ret = ldb_build_search_req(&search_req,
5724 ar->objs->partition_dn,
5730 replmd_replicated_apply_search_for_parent_callback,
5732 LDB_REQ_SET_LOCATION(search_req);
5734 ret = dsdb_request_add_controls(search_req,
5735 DSDB_SEARCH_SHOW_RECYCLED|
5736 DSDB_SEARCH_SHOW_DELETED|
5737 DSDB_SEARCH_SHOW_EXTENDED_DN);
5738 if (ret != LDB_SUCCESS) {
5742 return ldb_next_request(ar->module, search_req);
5746 handle renames that come in over DRS replication
5748 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5749 struct ldb_message *msg,
5750 struct ldb_request *parent,
5754 TALLOC_CTX *tmp_ctx = talloc_new(msg);
5755 struct ldb_result *res;
5756 struct ldb_dn *conflict_dn;
5757 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5758 const struct ldb_val *omd_value;
5759 struct replPropertyMetaDataBlob omd, *rmd;
5760 enum ndr_err_code ndr_err;
5761 bool rename_incoming_record, rodc;
5762 struct replPropertyMetaData1 *rmd_name, *omd_name;
5763 struct ldb_dn *new_dn;
5766 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5767 ldb_dn_get_linearized(ar->search_msg->dn),
5768 ldb_dn_get_linearized(msg->dn)));
5771 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5772 DSDB_FLAG_NEXT_MODULE, ar->req);
5773 if (ret == LDB_SUCCESS) {
5774 talloc_free(tmp_ctx);
5779 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5780 talloc_free(tmp_ctx);
5781 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5782 ldb_dn_get_linearized(ar->search_msg->dn),
5783 ldb_dn_get_linearized(msg->dn),
5784 ldb_errstring(ldb_module_get_ctx(ar->module)));
5788 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
5789 if (ret != LDB_SUCCESS) {
5790 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5791 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
5792 ldb_errstring(ldb_module_get_ctx(ar->module)));
5793 return LDB_ERR_OPERATIONS_ERROR;
5796 * we have a conflict, and need to decide if we will keep the
5797 * new record or the old record
5800 conflict_dn = msg->dn;
5804 * We are on an RODC, or were a GC for this
5805 * partition, so we have to fail this until
5806 * someone who owns the partition sorts it
5809 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5810 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
5811 " - We must fail the operation until a master for this partition resolves the conflict",
5812 ldb_dn_get_linearized(conflict_dn));
5813 ret = LDB_ERR_OPERATIONS_ERROR;
5818 * first we need the replPropertyMetaData attribute from the
5821 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
5823 DSDB_FLAG_NEXT_MODULE |
5824 DSDB_SEARCH_SHOW_DELETED |
5825 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5826 if (ret != LDB_SUCCESS) {
5827 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
5828 ldb_dn_get_linearized(conflict_dn)));
5832 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
5833 if (omd_value == NULL) {
5834 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
5835 ldb_dn_get_linearized(conflict_dn)));
5839 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
5840 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5841 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5842 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
5843 ldb_dn_get_linearized(conflict_dn)));
5847 rmd = ar->objs->objects[ar->index_current].meta_data;
5850 * we decide which is newer based on the RPMD on the name
5851 * attribute. See [MS-DRSR] ResolveNameConflict.
5853 * We expect omd_name to be present, as this is from a local
5854 * search, but while rmd_name should have been given to us by
5855 * the remote server, if it is missing we just prefer the
5857 * replmd_replPropertyMetaData1_new_should_be_taken()
5859 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5860 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5862 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5863 ldb_dn_get_linearized(conflict_dn)));
5868 * Should we preserve the current record, and so rename the
5869 * incoming record to be a conflict?
5871 rename_incoming_record =
5872 !replmd_replPropertyMetaData1_new_should_be_taken(
5873 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5874 omd_name, rmd_name);
5876 if (rename_incoming_record) {
5878 new_dn = replmd_conflict_dn(msg,
5879 ldb_module_get_ctx(ar->module),
5881 &ar->objs->objects[ar->index_current].object_guid);
5882 if (new_dn == NULL) {
5883 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5884 "Failed to form conflict DN for %s\n",
5885 ldb_dn_get_linearized(msg->dn));
5887 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5890 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
5891 DSDB_FLAG_NEXT_MODULE, ar->req);
5892 if (ret != LDB_SUCCESS) {
5893 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5894 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
5895 ldb_dn_get_linearized(conflict_dn),
5896 ldb_dn_get_linearized(ar->search_msg->dn),
5897 ldb_dn_get_linearized(new_dn),
5898 ldb_errstring(ldb_module_get_ctx(ar->module)));
5899 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5907 /* we are renaming the existing record */
5909 guid = samdb_result_guid(res->msgs[0], "objectGUID");
5910 if (GUID_all_zero(&guid)) {
5911 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5912 ldb_dn_get_linearized(conflict_dn)));
5916 new_dn = replmd_conflict_dn(tmp_ctx,
5917 ldb_module_get_ctx(ar->module),
5918 conflict_dn, &guid);
5919 if (new_dn == NULL) {
5920 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5921 ldb_dn_get_linearized(conflict_dn)));
5925 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5926 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5928 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5929 DSDB_FLAG_OWN_MODULE, ar->req);
5930 if (ret != LDB_SUCCESS) {
5931 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5932 ldb_dn_get_linearized(conflict_dn),
5933 ldb_dn_get_linearized(new_dn),
5934 ldb_errstring(ldb_module_get_ctx(ar->module))));
5939 * now we need to ensure that the rename is seen as an
5940 * originating update. We do that with a modify.
5942 ret = replmd_name_modify(ar, ar->req, new_dn);
5943 if (ret != LDB_SUCCESS) {
5947 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
5948 ldb_dn_get_linearized(ar->search_msg->dn),
5949 ldb_dn_get_linearized(msg->dn)));
5952 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5953 DSDB_FLAG_NEXT_MODULE, ar->req);
5954 if (ret != LDB_SUCCESS) {
5955 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
5956 ldb_dn_get_linearized(ar->search_msg->dn),
5957 ldb_dn_get_linearized(msg->dn),
5958 ldb_errstring(ldb_module_get_ctx(ar->module))));
5962 talloc_free(tmp_ctx);
5966 * On failure make the caller get the error
5967 * This means replication will stop with an error,
5968 * but there is not much else we can do. In the
5969 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
5972 if (ret == LDB_SUCCESS) {
5973 ret = LDB_ERR_OPERATIONS_ERROR;
5976 talloc_free(tmp_ctx);
5981 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
5983 struct ldb_context *ldb;
5984 struct ldb_request *change_req;
5985 enum ndr_err_code ndr_err;
5986 struct ldb_message *msg;
5987 struct replPropertyMetaDataBlob *rmd;
5988 struct replPropertyMetaDataBlob omd;
5989 const struct ldb_val *omd_value;
5990 struct replPropertyMetaDataBlob nmd;
5991 struct ldb_val nmd_value;
5992 struct GUID remote_parent_guid;
5995 unsigned int removed_attrs = 0;
5997 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
5998 bool isDeleted = false;
5999 bool local_isDeleted = false;
6000 bool remote_isDeleted = false;
6001 bool take_remote_isDeleted = false;
6002 bool sd_updated = false;
6003 bool renamed = false;
6004 bool is_schema_nc = false;
6006 const struct ldb_val *old_rdn, *new_rdn;
6007 struct replmd_private *replmd_private =
6008 talloc_get_type(ldb_module_get_private(ar->module),
6009 struct replmd_private);
6011 time_t t = time(NULL);
6012 unix_to_nt_time(&now, t);
6014 ldb = ldb_module_get_ctx(ar->module);
6015 msg = ar->objs->objects[ar->index_current].msg;
6017 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6019 rmd = ar->objs->objects[ar->index_current].meta_data;
6023 /* find existing meta data */
6024 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6026 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6027 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6028 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6029 nt_status = ndr_map_error2ntstatus(ndr_err);
6030 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6033 if (omd.version != 1) {
6034 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6039 struct GUID_txt_buf guid_txt;
6041 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6042 LDB_CHANGETYPE_MODIFY, msg);
6043 DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6046 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6048 ndr_print_struct_string(s,
6049 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6050 "existing replPropertyMetaData",
6052 ndr_print_struct_string(s,
6053 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6054 "incoming replPropertyMetaData",
6057 } else if (DEBUGLVL(4)) {
6058 struct GUID_txt_buf guid_txt;
6060 DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6061 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6063 ldb_dn_get_linearized(msg->dn)));
6066 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6067 "isDeleted", false);
6068 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6069 "isDeleted", false);
6072 * Fill in the remote_parent_guid with the GUID or an all-zero
6075 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6076 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6078 remote_parent_guid = GUID_zero();
6082 * To ensure we follow a complex rename chain around, we have
6083 * to confirm that the DN is the same (mostly to confirm the
6084 * RDN) and the parentGUID is the same.
6086 * This ensures we keep things under the correct parent, which
6087 * replmd_replicated_handle_rename() will do.
6090 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6091 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6095 * handle renames, even just by case that come in over
6096 * DRS. Changes in the parent DN don't hit us here,
6097 * because the search for a parent will clean up those
6100 * We also have already filtered out the case where
6101 * the peer has an older name to what we have (see
6102 * replmd_replicated_apply_search_callback())
6104 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
6107 if (ret != LDB_SUCCESS) {
6108 ldb_debug(ldb, LDB_DEBUG_FATAL,
6109 "replmd_replicated_request rename %s => %s failed - %s\n",
6110 ldb_dn_get_linearized(ar->search_msg->dn),
6111 ldb_dn_get_linearized(msg->dn),
6112 ldb_errstring(ldb));
6113 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6116 if (renamed == true) {
6118 * Set the callback to one that will fix up the name
6119 * metadata on the new conflict DN
6121 callback = replmd_op_name_modify_callback;
6126 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6127 nmd.ctr.ctr1.array = talloc_array(ar,
6128 struct replPropertyMetaData1,
6129 nmd.ctr.ctr1.count);
6130 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6132 /* first copy the old meta data */
6133 for (i=0; i < omd.ctr.ctr1.count; i++) {
6134 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6139 /* now merge in the new meta data */
6140 for (i=0; i < rmd->ctr.ctr1.count; i++) {
6143 for (j=0; j < ni; j++) {
6146 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6150 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6151 ar->objs->dsdb_repl_flags,
6152 &nmd.ctr.ctr1.array[j],
6153 &rmd->ctr.ctr1.array[i]);
6155 /* replace the entry */
6156 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6157 if (ar->seq_num == 0) {
6158 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6159 if (ret != LDB_SUCCESS) {
6160 return replmd_replicated_request_error(ar, ret);
6163 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6164 switch (nmd.ctr.ctr1.array[j].attid) {
6165 case DRSUAPI_ATTID_ntSecurityDescriptor:
6168 case DRSUAPI_ATTID_isDeleted:
6169 take_remote_isDeleted = true;
6178 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6179 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6180 msg->elements[i-removed_attrs].name,
6181 ldb_dn_get_linearized(msg->dn),
6182 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6185 /* we don't want to apply this change so remove the attribute */
6186 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6193 if (found) continue;
6195 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6196 if (ar->seq_num == 0) {
6197 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6198 if (ret != LDB_SUCCESS) {
6199 return replmd_replicated_request_error(ar, ret);
6202 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6203 switch (nmd.ctr.ctr1.array[ni].attid) {
6204 case DRSUAPI_ATTID_ntSecurityDescriptor:
6207 case DRSUAPI_ATTID_isDeleted:
6208 take_remote_isDeleted = true;
6217 * finally correct the size of the meta_data array
6219 nmd.ctr.ctr1.count = ni;
6221 new_rdn = ldb_dn_get_rdn_val(msg->dn);
6222 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6225 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6226 &nmd, ar, now, is_schema_nc,
6228 if (ret != LDB_SUCCESS) {
6229 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6230 return replmd_replicated_request_error(ar, ret);
6234 * sort the new meta data array
6236 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6237 if (ret != LDB_SUCCESS) {
6238 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6243 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6246 * This also controls SD propagation below
6248 if (take_remote_isDeleted) {
6249 isDeleted = remote_isDeleted;
6251 isDeleted = local_isDeleted;
6254 ar->isDeleted = isDeleted;
6257 * check if some replicated attributes left, otherwise skip the ldb_modify() call
6259 if (msg->num_elements == 0) {
6260 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6263 return replmd_replicated_apply_isDeleted(ar);
6266 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6267 ar->index_current, msg->num_elements);
6273 if (sd_updated && !isDeleted) {
6274 ret = dsdb_module_schedule_sd_propagation(ar->module,
6275 ar->objs->partition_dn,
6277 if (ret != LDB_SUCCESS) {
6278 return ldb_operr(ldb);
6282 /* create the meta data value */
6283 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6284 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6285 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6286 nt_status = ndr_map_error2ntstatus(ndr_err);
6287 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6291 * when we know that we'll modify the record, add the whenChanged, uSNChanged
6292 * and replPopertyMetaData attributes
6294 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6295 if (ret != LDB_SUCCESS) {
6296 return replmd_replicated_request_error(ar, ret);
6298 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6299 if (ret != LDB_SUCCESS) {
6300 return replmd_replicated_request_error(ar, ret);
6302 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6303 if (ret != LDB_SUCCESS) {
6304 return replmd_replicated_request_error(ar, ret);
6307 replmd_ldb_message_sort(msg, ar->schema);
6309 /* we want to replace the old values */
6310 for (i=0; i < msg->num_elements; i++) {
6311 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6312 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6313 if (msg->elements[i].num_values == 0) {
6314 ldb_asprintf_errstring(ldb, __location__
6315 ": objectClass removed on %s, aborting replication\n",
6316 ldb_dn_get_linearized(msg->dn));
6317 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6323 struct GUID_txt_buf guid_txt;
6325 char *s = ldb_ldif_message_redacted_string(ldb, ar,
6326 LDB_CHANGETYPE_MODIFY,
6328 DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6329 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6333 } else if (DEBUGLVL(4)) {
6334 struct GUID_txt_buf guid_txt;
6336 DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6337 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6339 ldb_dn_get_linearized(msg->dn)));
6342 ret = ldb_build_mod_req(&change_req,
6350 LDB_REQ_SET_LOCATION(change_req);
6351 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6353 /* current partition control needed by "repmd_op_callback" */
6354 ret = ldb_request_add_control(change_req,
6355 DSDB_CONTROL_CURRENT_PARTITION_OID,
6357 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6359 return ldb_next_request(ar->module, change_req);
6362 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6363 struct ldb_reply *ares)
6365 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6366 struct replmd_replicated_request);
6370 return ldb_module_done(ar->req, NULL, NULL,
6371 LDB_ERR_OPERATIONS_ERROR);
6373 if (ares->error != LDB_SUCCESS &&
6374 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6375 return ldb_module_done(ar->req, ares->controls,
6376 ares->response, ares->error);
6379 switch (ares->type) {
6380 case LDB_REPLY_ENTRY:
6381 ar->search_msg = talloc_steal(ar, ares->message);
6384 case LDB_REPLY_REFERRAL:
6385 /* we ignore referrals */
6388 case LDB_REPLY_DONE:
6390 struct replPropertyMetaData1 *md_remote;
6391 struct replPropertyMetaData1 *md_local;
6393 struct replPropertyMetaDataBlob omd;
6394 const struct ldb_val *omd_value;
6395 struct replPropertyMetaDataBlob *rmd;
6396 struct ldb_message *msg;
6398 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6399 ar->objs->objects[ar->index_current].last_known_parent = NULL;
6402 * This is the ADD case, find the appropriate parent,
6403 * as this object doesn't exist locally:
6405 if (ar->search_msg == NULL) {
6406 ret = replmd_replicated_apply_search_for_parent(ar);
6407 if (ret != LDB_SUCCESS) {
6408 return ldb_module_done(ar->req, NULL, NULL, ret);
6415 * Otherwise, in the MERGE case, work out if we are
6416 * attempting a rename, and if so find the parent the
6417 * newly renamed object wants to belong under (which
6418 * may not be the parent in it's attached string DN
6420 rmd = ar->objs->objects[ar->index_current].meta_data;
6424 /* find existing meta data */
6425 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6427 enum ndr_err_code ndr_err;
6428 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6429 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6430 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6431 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6432 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6435 if (omd.version != 1) {
6436 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6440 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6442 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6443 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6444 && GUID_all_zero(&ar->local_parent_guid)) {
6445 DEBUG(0, ("Refusing to replicate new version of %s "
6446 "as local object has an all-zero parentGUID attribute, "
6447 "despite not being an NC root\n",
6448 ldb_dn_get_linearized(ar->search_msg->dn)));
6449 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6453 * now we need to check for double renames. We could have a
6454 * local rename pending which our replication partner hasn't
6455 * received yet. We choose which one wins by looking at the
6456 * attribute stamps on the two objects, the newer one wins.
6458 * This also simply applies the correct algorithms for
6459 * determining if a change was made to name at all, or
6460 * if the object has just been renamed under the same
6463 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6464 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6466 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6467 ldb_dn_get_linearized(ar->search_msg->dn)));
6468 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6472 * if there is no name attribute given then we have to assume the
6473 * object we've received has the older name
6475 if (replmd_replPropertyMetaData1_new_should_be_taken(
6476 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6477 md_local, md_remote)) {
6478 struct GUID_txt_buf p_guid_local;
6479 struct GUID_txt_buf p_guid_remote;
6480 msg = ar->objs->objects[ar->index_current].msg;
6482 /* Merge on the existing object, with rename */
6484 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6485 "as incoming object changing to %s under %s\n",
6486 ldb_dn_get_linearized(ar->search_msg->dn),
6487 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6488 ldb_dn_get_linearized(msg->dn),
6489 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6491 ret = replmd_replicated_apply_search_for_parent(ar);
6493 struct GUID_txt_buf p_guid_local;
6494 struct GUID_txt_buf p_guid_remote;
6495 msg = ar->objs->objects[ar->index_current].msg;
6498 * Merge on the existing object, force no
6499 * rename (code below just to explain why in
6503 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6504 ldb_dn_get_linearized(msg->dn)) == 0) {
6505 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6506 GUID_equal(&ar->local_parent_guid,
6507 ar->objs->objects[ar->index_current].parent_guid)
6509 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6510 "despite incoming object changing parent to %s\n",
6511 ldb_dn_get_linearized(ar->search_msg->dn),
6512 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6513 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6517 DEBUG(4,(__location__ ": Keeping object %s at under %s "
6518 " and rejecting older rename to %s under %s\n",
6519 ldb_dn_get_linearized(ar->search_msg->dn),
6520 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6521 ldb_dn_get_linearized(msg->dn),
6522 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6526 * This assignment ensures that the strcmp()
6527 * and GUID_equal() calls in
6528 * replmd_replicated_apply_merge() avoids the
6531 ar->objs->objects[ar->index_current].parent_guid =
6532 &ar->local_parent_guid;
6534 msg->dn = ar->search_msg->dn;
6535 ret = replmd_replicated_apply_merge(ar);
6537 if (ret != LDB_SUCCESS) {
6538 return ldb_module_done(ar->req, NULL, NULL, ret);
6548 * Returns true if we can group together processing this link attribute,
6549 * i.e. it has the same source-object and attribute ID as other links
6550 * already in the group
6552 static bool la_entry_matches_group(struct la_entry *la_entry,
6553 struct la_group *la_group)
6555 struct la_entry *prev = la_group->la_entries;
6557 return (la_entry->la->attid == prev->la->attid &&
6558 GUID_equal(&la_entry->la->identifier->guid,
6559 &prev->la->identifier->guid));
6563 * Stores the linked attributes received in the replication chunk - these get
6564 * applied at the end of the transaction. We also check that each linked
6565 * attribute is valid, i.e. source and target objects are known.
6567 static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6569 int ret = LDB_SUCCESS;
6571 struct ldb_module *module = ar->module;
6572 struct replmd_private *replmd_private =
6573 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6574 struct la_group *la_group = NULL;
6575 struct ldb_context *ldb;
6577 ldb = ldb_module_get_ctx(module);
6579 DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6581 /* save away the linked attributes for the end of the transaction */
6582 for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6583 struct la_entry *la_entry;
6585 if (replmd_private->la_ctx == NULL) {
6586 replmd_private->la_ctx = talloc_new(replmd_private);
6588 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6589 if (la_entry == NULL) {
6591 return LDB_ERR_OPERATIONS_ERROR;
6593 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6594 if (la_entry->la == NULL) {
6595 talloc_free(la_entry);
6597 return LDB_ERR_OPERATIONS_ERROR;
6599 *la_entry->la = ar->objs->linked_attributes[i];
6600 la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
6602 /* we need to steal the non-scalars so they stay
6603 around until the end of the transaction */
6604 talloc_steal(la_entry->la, la_entry->la->identifier);
6605 talloc_steal(la_entry->la, la_entry->la->value.blob);
6607 ret = replmd_verify_linked_attribute(ar, la_entry);
6609 if (ret != LDB_SUCCESS) {
6613 /* group the links together by source-object for efficiency */
6614 if (la_group == NULL ||
6615 !la_entry_matches_group(la_entry, la_group)) {
6617 la_group = talloc_zero(replmd_private->la_ctx,
6619 if (la_group == NULL) {
6621 return LDB_ERR_OPERATIONS_ERROR;
6623 DLIST_ADD(replmd_private->la_list, la_group);
6625 DLIST_ADD(la_group->la_entries, la_entry);
6626 replmd_private->total_links++;
6632 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6634 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6636 struct ldb_context *ldb;
6640 struct ldb_request *search_req;
6641 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6642 "parentGUID", "instanceType",
6643 "replPropertyMetaData", "nTSecurityDescriptor",
6644 "isDeleted", NULL };
6645 struct GUID_txt_buf guid_str_buf;
6647 if (ar->index_current >= ar->objs->num_objects) {
6650 * Now that we've applied all the objects, check the new linked
6651 * attributes and store them (we apply them in .prepare_commit)
6653 ret = replmd_store_linked_attributes(ar);
6655 if (ret != LDB_SUCCESS) {
6659 /* done applying objects, move on to the next stage */
6660 return replmd_replicated_uptodate_vector(ar);
6663 ldb = ldb_module_get_ctx(ar->module);
6664 ar->search_msg = NULL;
6665 ar->isDeleted = false;
6667 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6670 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6671 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6673 ret = ldb_build_search_req(&search_req,
6676 ar->objs->partition_dn,
6682 replmd_replicated_apply_search_callback,
6684 LDB_REQ_SET_LOCATION(search_req);
6686 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
6688 if (ret != LDB_SUCCESS) {
6692 return ldb_next_request(ar->module, search_req);
6696 * This is essentially a wrapper for replmd_replicated_apply_next()
6698 * This is needed to ensure that both codepaths call this handler.
6700 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6702 struct ldb_dn *deleted_objects_dn;
6703 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6704 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
6705 &deleted_objects_dn);
6706 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
6708 * Do a delete here again, so that if there is
6709 * anything local that conflicts with this
6710 * object being deleted, it is removed. This
6711 * includes links. See MS-DRSR 4.1.10.6.9
6714 * If the object is already deleted, and there
6715 * is no more work required, it doesn't do
6719 /* This has been updated to point to the DN we eventually did the modify on */
6721 struct ldb_request *del_req;
6722 struct ldb_result *res;
6724 TALLOC_CTX *tmp_ctx = talloc_new(ar);
6726 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6730 res = talloc_zero(tmp_ctx, struct ldb_result);
6732 ret = ldb_oom(ldb_module_get_ctx(ar->module));
6733 talloc_free(tmp_ctx);
6737 /* Build a delete request, which hopefully will artually turn into nothing */
6738 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
6742 ldb_modify_default_callback,
6744 LDB_REQ_SET_LOCATION(del_req);
6745 if (ret != LDB_SUCCESS) {
6746 talloc_free(tmp_ctx);
6751 * This is the guts of the call, call back
6752 * into our delete code, but setting the
6753 * re_delete flag so we delete anything that
6754 * shouldn't be there on a deleted or recycled
6757 ret = replmd_delete_internals(ar->module, del_req, true);
6758 if (ret == LDB_SUCCESS) {
6759 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
6762 talloc_free(tmp_ctx);
6763 if (ret != LDB_SUCCESS) {
6768 ar->index_current++;
6769 return replmd_replicated_apply_next(ar);
6772 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
6773 struct ldb_reply *ares)
6775 struct ldb_context *ldb;
6776 struct replmd_replicated_request *ar = talloc_get_type(req->context,
6777 struct replmd_replicated_request);
6778 ldb = ldb_module_get_ctx(ar->module);
6781 return ldb_module_done(ar->req, NULL, NULL,
6782 LDB_ERR_OPERATIONS_ERROR);
6784 if (ares->error != LDB_SUCCESS) {
6785 return ldb_module_done(ar->req, ares->controls,
6786 ares->response, ares->error);
6789 if (ares->type != LDB_REPLY_DONE) {
6790 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
6791 return ldb_module_done(ar->req, NULL, NULL,
6792 LDB_ERR_OPERATIONS_ERROR);
6797 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6800 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
6802 struct ldb_context *ldb;
6803 struct ldb_request *change_req;
6804 enum ndr_err_code ndr_err;
6805 struct ldb_message *msg;
6806 struct replUpToDateVectorBlob ouv;
6807 const struct ldb_val *ouv_value;
6808 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
6809 struct replUpToDateVectorBlob nuv;
6810 struct ldb_val nuv_value;
6811 struct ldb_message_element *nuv_el = NULL;
6812 struct ldb_message_element *orf_el = NULL;
6813 struct repsFromToBlob nrf;
6814 struct ldb_val *nrf_value = NULL;
6815 struct ldb_message_element *nrf_el = NULL;
6819 time_t t = time(NULL);
6822 uint32_t instanceType;
6824 ldb = ldb_module_get_ctx(ar->module);
6825 ruv = ar->objs->uptodateness_vector;
6831 unix_to_nt_time(&now, t);
6833 if (ar->search_msg == NULL) {
6834 /* this happens for a REPL_OBJ call where we are
6835 creating the target object by replicating it. The
6836 subdomain join code does this for the partition DN
6838 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
6839 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6842 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
6843 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
6844 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
6845 ldb_dn_get_linearized(ar->search_msg->dn)));
6846 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
6850 * first create the new replUpToDateVector
6852 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
6854 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
6855 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
6856 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6857 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6858 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6861 if (ouv.version != 2) {
6862 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6867 * the new uptodateness vector will at least
6868 * contain 1 entry, one for the source_dsa
6870 * plus optional values from our old vector and the one from the source_dsa
6872 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
6873 if (ruv) nuv.ctr.ctr2.count += ruv->count;
6874 nuv.ctr.ctr2.cursors = talloc_array(ar,
6875 struct drsuapi_DsReplicaCursor2,
6876 nuv.ctr.ctr2.count);
6877 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6879 /* first copy the old vector */
6880 for (i=0; i < ouv.ctr.ctr2.count; i++) {
6881 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
6885 /* merge in the source_dsa vector is available */
6886 for (i=0; (ruv && i < ruv->count); i++) {
6889 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6890 &ar->our_invocation_id)) {
6894 for (j=0; j < ni; j++) {
6895 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
6896 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
6902 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
6903 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
6908 if (found) continue;
6910 /* if it's not there yet, add it */
6911 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
6916 * finally correct the size of the cursors array
6918 nuv.ctr.ctr2.count = ni;
6923 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
6926 * create the change ldb_message
6928 msg = ldb_msg_new(ar);
6929 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6930 msg->dn = ar->search_msg->dn;
6932 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
6933 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
6934 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6935 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6936 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6938 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
6939 if (ret != LDB_SUCCESS) {
6940 return replmd_replicated_request_error(ar, ret);
6942 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
6945 * now create the new repsFrom value from the given repsFromTo1 structure
6949 nrf.ctr.ctr1 = *ar->objs->source_dsa;
6950 nrf.ctr.ctr1.last_attempt = now;
6951 nrf.ctr.ctr1.last_success = now;
6952 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
6955 * first see if we already have a repsFrom value for the current source dsa
6956 * if so we'll later replace this value
6958 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
6960 for (i=0; i < orf_el->num_values; i++) {
6961 struct repsFromToBlob *trf;
6963 trf = talloc(ar, struct repsFromToBlob);
6964 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6966 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
6967 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
6968 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6969 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6970 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6973 if (trf->version != 1) {
6974 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6978 * we compare the source dsa objectGUID not the invocation_id
6979 * because we want only one repsFrom value per source dsa
6980 * and when the invocation_id of the source dsa has changed we don't need
6981 * the old repsFrom with the old invocation_id
6983 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
6984 &ar->objs->source_dsa->source_dsa_obj_guid)) {
6990 nrf_value = &orf_el->values[i];
6995 * copy over all old values to the new ldb_message
6997 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
6998 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7003 * if we haven't found an old repsFrom value for the current source dsa
7004 * we'll add a new value
7007 struct ldb_val zero_value;
7008 ZERO_STRUCT(zero_value);
7009 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7010 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7012 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7015 /* we now fill the value which is already attached to ldb_message */
7016 ndr_err = ndr_push_struct_blob(nrf_value, msg,
7018 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7019 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7020 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7021 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7025 * the ldb_message_element for the attribute, has all the old values and the new one
7026 * so we'll replace the whole attribute with all values
7028 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7030 if (CHECK_DEBUGLVL(4)) {
7031 char *s = ldb_ldif_message_redacted_string(ldb, ar,
7032 LDB_CHANGETYPE_MODIFY,
7034 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7038 /* prepare the ldb_modify() request */
7039 ret = ldb_build_mod_req(&change_req,
7045 replmd_replicated_uptodate_modify_callback,
7047 LDB_REQ_SET_LOCATION(change_req);
7048 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7050 return ldb_next_request(ar->module, change_req);
7053 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7054 struct ldb_reply *ares)
7056 struct replmd_replicated_request *ar = talloc_get_type(req->context,
7057 struct replmd_replicated_request);
7061 return ldb_module_done(ar->req, NULL, NULL,
7062 LDB_ERR_OPERATIONS_ERROR);
7064 if (ares->error != LDB_SUCCESS &&
7065 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7066 return ldb_module_done(ar->req, ares->controls,
7067 ares->response, ares->error);
7070 switch (ares->type) {
7071 case LDB_REPLY_ENTRY:
7072 ar->search_msg = talloc_steal(ar, ares->message);
7075 case LDB_REPLY_REFERRAL:
7076 /* we ignore referrals */
7079 case LDB_REPLY_DONE:
7080 ret = replmd_replicated_uptodate_modify(ar);
7081 if (ret != LDB_SUCCESS) {
7082 return ldb_module_done(ar->req, NULL, NULL, ret);
7091 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7093 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7094 struct replmd_private *replmd_private =
7095 talloc_get_type_abort(ldb_module_get_private(ar->module),
7096 struct replmd_private);
7098 static const char *attrs[] = {
7099 "replUpToDateVector",
7104 struct ldb_request *search_req;
7106 ar->search_msg = NULL;
7109 * Let the caller know that we did an originating updates
7111 ar->objs->originating_updates = replmd_private->originating_updates;
7113 ret = ldb_build_search_req(&search_req,
7116 ar->objs->partition_dn,
7122 replmd_replicated_uptodate_search_callback,
7124 LDB_REQ_SET_LOCATION(search_req);
7125 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7127 return ldb_next_request(ar->module, search_req);
7132 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7134 struct ldb_context *ldb;
7135 struct dsdb_extended_replicated_objects *objs;
7136 struct replmd_replicated_request *ar;
7137 struct ldb_control **ctrls;
7140 ldb = ldb_module_get_ctx(module);
7142 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7144 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7146 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7147 return LDB_ERR_PROTOCOL_ERROR;
7150 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7151 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7152 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7153 return LDB_ERR_PROTOCOL_ERROR;
7156 ar = replmd_ctx_init(module, req);
7158 return LDB_ERR_OPERATIONS_ERROR;
7160 /* Set the flags to have the replmd_op_callback run over the full set of objects */
7161 ar->apply_mode = true;
7163 ar->schema = dsdb_get_schema(ldb, ar);
7165 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7167 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7168 return LDB_ERR_CONSTRAINT_VIOLATION;
7171 ctrls = req->controls;
7173 if (req->controls) {
7174 req->controls = talloc_memdup(ar, req->controls,
7175 talloc_get_size(req->controls));
7176 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7179 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7180 if (ret != LDB_SUCCESS) {
7184 /* If this change contained linked attributes in the body
7185 * (rather than in the links section) we need to update
7186 * backlinks in linked_attributes */
7187 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7188 if (ret != LDB_SUCCESS) {
7192 ar->controls = req->controls;
7193 req->controls = ctrls;
7195 return replmd_replicated_apply_next(ar);
7199 * Checks how to handle an missing target - either we need to fail the
7200 * replication and retry with GET_TGT, ignore the link and continue, or try to
7201 * add a partial link to an unknown target.
7203 static int replmd_allow_missing_target(struct ldb_module *module,
7204 TALLOC_CTX *mem_ctx,
7205 struct ldb_dn *target_dn,
7206 struct ldb_dn *source_dn,
7209 uint32_t dsdb_repl_flags,
7211 const char * missing_str)
7213 struct ldb_context *ldb = ldb_module_get_ctx(module);
7217 * we may not be able to resolve link targets properly when
7218 * dealing with subsets of objects, e.g. the source is a
7219 * critical object and the target isn't
7222 * When we implement Trusted Domains we need to consider
7223 * whether they get treated as an incomplete replica here or not
7225 if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7228 * Ignore the link. We don't increase the highwater-mark in
7229 * the object subset cases, so subsequent replications should
7230 * resolve any missing links
7232 DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7233 ldb_dn_get_linearized(target_dn),
7234 ldb_dn_get_linearized(source_dn)));
7235 *ignore_link = true;
7239 if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7242 * target should already be up-to-date so there's no point in
7243 * retrying. This could be due to bad timing, or if a target
7244 * on a one-way link was deleted. We ignore the link rather
7245 * than failing the replication cycle completely
7247 *ignore_link = true;
7248 DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
7249 ldb_dn_get_linearized(target_dn), missing_str,
7250 ldb_dn_get_linearized(source_dn));
7254 is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7258 if (is_in_same_nc) {
7259 /* fail the replication and retry with GET_TGT */
7260 ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7262 ldb_dn_get_linearized(target_dn),
7263 GUID_string(mem_ctx, guid),
7264 ldb_dn_get_linearized(source_dn));
7265 return LDB_ERR_NO_SUCH_OBJECT;
7269 * The target of the cross-partition link is missing. Continue
7270 * and try to at least add the forward-link. This isn't great,
7271 * but a partial link can be fixed by dbcheck, so it's better
7272 * than dropping the link completely.
7274 *ignore_link = false;
7276 if (is_obj_commit) {
7279 * Only log this when we're actually committing the objects.
7280 * This avoids spurious logs, i.e. if we're just verifying the
7281 * received link during a join.
7283 DBG_WARNING("%s cross-partition target %s linked from %s\n",
7284 missing_str, ldb_dn_get_linearized(target_dn),
7285 ldb_dn_get_linearized(source_dn));
7292 * Checks that the target object for a linked attribute exists.
7293 * @param guid returns the target object's GUID (is returned)if it exists)
7294 * @param ignore_link set to true if the linked attribute should be ignored
7295 * (i.e. the target doesn't exist, but that it's OK to skip the link)
7297 static int replmd_check_target_exists(struct ldb_module *module,
7298 struct dsdb_dn *dsdb_dn,
7299 struct la_entry *la_entry,
7300 struct ldb_dn *source_dn,
7305 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7306 struct ldb_context *ldb = ldb_module_get_ctx(module);
7307 struct ldb_result *target_res;
7308 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7309 const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7312 enum deletion_state target_deletion_state = OBJECT_REMOVED;
7313 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7315 *ignore_link = false;
7316 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7318 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7321 * This strange behaviour (allowing a NULL/missing
7322 * GUID) originally comes from:
7324 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7325 * Author: Andrew Tridgell <tridge@samba.org>
7326 * Date: Mon Dec 21 21:21:55 2009 +1100
7328 * s4-drs: cope better with NULL GUIDS from DRS
7330 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7331 * need to match by DN if possible when seeing if we should update an
7334 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7336 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7338 DSDB_FLAG_NEXT_MODULE |
7339 DSDB_SEARCH_SHOW_RECYCLED |
7340 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7341 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7343 } else if (!NT_STATUS_IS_OK(ntstatus)) {
7344 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7346 ldb_dn_get_linearized(dsdb_dn->dn),
7347 ldb_dn_get_linearized(source_dn));
7348 talloc_free(tmp_ctx);
7349 return LDB_ERR_OPERATIONS_ERROR;
7351 ret = dsdb_module_search(module, tmp_ctx, &target_res,
7352 NULL, LDB_SCOPE_SUBTREE,
7354 DSDB_FLAG_NEXT_MODULE |
7355 DSDB_SEARCH_SHOW_RECYCLED |
7356 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7357 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7360 GUID_string(tmp_ctx, guid));
7363 if (ret != LDB_SUCCESS) {
7364 ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7365 GUID_string(tmp_ctx, guid),
7366 ldb_errstring(ldb));
7367 talloc_free(tmp_ctx);
7371 if (target_res->count == 0) {
7374 * target object is unknown. Check whether to ignore the link,
7375 * fail the replication, or add a partial link
7377 ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7378 source_dn, is_obj_commit, guid,
7379 la_entry->dsdb_repl_flags,
7380 ignore_link, "Unknown");
7382 } else if (target_res->count != 1) {
7383 ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7384 GUID_string(tmp_ctx, guid));
7385 ret = LDB_ERR_OPERATIONS_ERROR;
7387 struct ldb_message *target_msg = target_res->msgs[0];
7389 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7391 /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7392 replmd_deletion_state(module, target_msg,
7393 &target_deletion_state, NULL);
7396 * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7397 * ProcessLinkValue(). Link updates should not be sent for
7398 * recycled and tombstone objects (deleting the links should
7399 * happen when we delete the object). This probably means our
7400 * copy of the target object isn't up to date.
7402 if (target_deletion_state >= OBJECT_RECYCLED) {
7405 * target object is deleted. Check whether to ignore the
7406 * link, fail the replication, or add a partial link
7408 ret = replmd_allow_missing_target(module, tmp_ctx,
7409 dsdb_dn->dn, source_dn,
7410 is_obj_commit, guid,
7411 la_entry->dsdb_repl_flags,
7412 ignore_link, "Deleted");
7416 talloc_free(tmp_ctx);
7421 * Extracts the key details about the source object for a
7422 * linked-attribute entry.
7423 * This returns the following details:
7424 * @param ret_attr the schema details for the linked attribute
7425 * @param source_msg the search result for the source object
7427 static int replmd_get_la_entry_source(struct ldb_module *module,
7428 struct la_entry *la_entry,
7429 TALLOC_CTX *mem_ctx,
7430 const struct dsdb_attribute **ret_attr,
7431 struct ldb_message **source_msg)
7433 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7434 struct ldb_context *ldb = ldb_module_get_ctx(module);
7435 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7437 const struct dsdb_attribute *attr;
7438 struct ldb_result *res;
7439 const char *attrs[4];
7442 linked_attributes[0]:
7443 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7445 identifier: struct drsuapi_DsReplicaObjectIdentifier
7446 __ndr_size : 0x0000003a (58)
7447 __ndr_size_sid : 0x00000000 (0)
7448 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7450 __ndr_size_dn : 0x00000000 (0)
7452 attid : DRSUAPI_ATTID_member (0x1F)
7453 value: struct drsuapi_DsAttributeValue
7454 __ndr_size : 0x0000007e (126)
7456 blob : DATA_BLOB length=126
7457 flags : 0x00000001 (1)
7458 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7459 originating_add_time : Wed Sep 2 22:20:01 2009 EST
7460 meta_data: struct drsuapi_DsReplicaMetaData
7461 version : 0x00000015 (21)
7462 originating_change_time : Wed Sep 2 23:39:07 2009 EST
7463 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7464 originating_usn : 0x000000000001e19c (123292)
7466 (for cases where the link is to a normal DN)
7467 &target: struct drsuapi_DsReplicaObjectIdentifier3
7468 __ndr_size : 0x0000007e (126)
7469 __ndr_size_sid : 0x0000001c (28)
7470 guid : 7639e594-db75-4086-b0d4-67890ae46031
7471 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7472 __ndr_size_dn : 0x00000022 (34)
7473 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7476 /* find the attribute being modified */
7477 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7479 struct GUID_txt_buf guid_str;
7480 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7482 GUID_buf_string(&la->identifier->guid,
7484 return LDB_ERR_OPERATIONS_ERROR;
7488 * All attributes listed here must be dealt with in some way
7489 * by replmd_process_linked_attribute() otherwise in the case
7490 * of isDeleted: FALSE the modify will fail with:
7492 * Failed to apply linked attribute change 'attribute 'isDeleted':
7493 * invalid modify flags on
7494 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7497 * This is becaue isDeleted is a Boolean, so FALSE is a
7498 * legitimate value (set by Samba's deletetest.py)
7500 attrs[0] = attr->lDAPDisplayName;
7501 attrs[1] = "isDeleted";
7502 attrs[2] = "isRecycled";
7506 * get the existing message from the db for the object with
7507 * this GUID, returning attribute being modified. We will then
7508 * use this msg as the basis for a modify call
7510 ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7511 DSDB_FLAG_NEXT_MODULE |
7512 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7513 DSDB_SEARCH_SHOW_RECYCLED |
7514 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7515 DSDB_SEARCH_REVEAL_INTERNALS,
7517 "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7518 if (ret != LDB_SUCCESS) {
7521 if (res->count != 1) {
7522 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7523 GUID_string(mem_ctx, &la->identifier->guid));
7524 return LDB_ERR_NO_SUCH_OBJECT;
7527 *source_msg = res->msgs[0];
7534 * Verifies the source and target objects are known for a linked attribute
7536 static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
7537 struct la_entry *la_entry)
7539 int ret = LDB_SUCCESS;
7540 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7541 struct ldb_module *module = ar->module;
7542 struct ldb_message *src_msg;
7543 const struct dsdb_attribute *attr;
7544 struct dsdb_dn *tgt_dsdb_dn = NULL;
7545 struct GUID guid = GUID_zero();
7548 struct ldb_context *ldb = ldb_module_get_ctx(module);
7549 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7550 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
7552 ret = replmd_get_la_entry_source(module, la_entry, tmp_ctx, &attr,
7556 * When we fail to find the source object, the error code we pass
7557 * back here is really important. It flags back to the callers to
7558 * retry this request with DRSUAPI_DRS_GET_ANC. This case should
7559 * never happen if we're replicating from a Samba DC, but it is
7560 * needed to talk to a Windows DC
7562 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7563 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
7566 if (ret != LDB_SUCCESS) {
7567 talloc_free(tmp_ctx);
7571 /* the value blob for the attribute holds the target object DN */
7572 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx,
7573 la->value.blob, &tgt_dsdb_dn);
7574 if (!W_ERROR_IS_OK(status)) {
7575 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7576 attr->lDAPDisplayName,
7577 ldb_dn_get_linearized(src_msg->dn),
7578 win_errstr(status));
7579 return LDB_ERR_OPERATIONS_ERROR;
7583 * We can skip the target object checks if we're only syncing critical
7584 * objects, or we know the target is up-to-date. If either case, we
7585 * still continue even if the target doesn't exist
7587 if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7588 DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7590 ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7591 src_msg->dn, false, &guid,
7596 * When we fail to find the target object, the error code we pass
7597 * back here is really important. It flags back to the callers to
7598 * retry this request with DRSUAPI_DRS_GET_TGT
7600 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7601 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7604 talloc_free(tmp_ctx);
7609 * Finds the current active Parsed-DN value for a single-valued linked
7610 * attribute, if one exists.
7611 * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7612 * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7615 static int replmd_get_active_singleval_link(struct ldb_module *module,
7616 TALLOC_CTX *mem_ctx,
7617 struct parsed_dn pdn_list[],
7619 const struct dsdb_attribute *attr,
7620 struct parsed_dn **ret_pdn)
7626 if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7628 /* nothing to do for multi-valued linked attributes */
7632 for (i = 0; i < count; i++) {
7633 int ret = LDB_SUCCESS;
7634 struct parsed_dn *pdn = &pdn_list[i];
7636 /* skip any inactive links */
7637 if (dsdb_dn_is_deleted_val(pdn->v)) {
7641 /* we've found an active value for this attribute */
7644 if (pdn->dsdb_dn == NULL) {
7645 struct ldb_context *ldb = ldb_module_get_ctx(module);
7647 ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7648 attr->syntax->ldap_oid);
7654 /* no active link found */
7659 * @returns true if the replication linked attribute info is newer than we
7660 * already have in our DB
7661 * @param pdn the existing linked attribute info in our DB
7662 * @param la the new linked attribute info received during replication
7664 static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7665 struct drsuapi_DsReplicaLinkedAttribute *la)
7667 /* see if this update is newer than what we have already */
7668 struct GUID invocation_id = GUID_zero();
7669 uint32_t version = 0;
7670 NTTIME change_time = 0;
7674 /* no existing info so update is newer */
7678 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7679 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7680 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7682 return replmd_update_is_newer(&invocation_id,
7683 &la->meta_data.originating_invocation_id,
7685 la->meta_data.version,
7687 la->meta_data.originating_change_time);
7691 * Marks an existing linked attribute value as deleted in the DB
7692 * @param pdn the parsed-DN of the target-value to delete
7694 static int replmd_delete_link_value(struct ldb_module *module,
7695 struct replmd_private *replmd_private,
7696 TALLOC_CTX *mem_ctx,
7697 struct ldb_dn *src_obj_dn,
7698 const struct dsdb_schema *schema,
7699 const struct dsdb_attribute *attr,
7702 struct GUID *target_guid,
7703 struct dsdb_dn *target_dsdb_dn,
7704 struct ldb_val *output_val)
7706 struct ldb_context *ldb = ldb_module_get_ctx(module);
7709 const struct GUID *invocation_id = NULL;
7713 unix_to_nt_time(&now, t);
7715 invocation_id = samdb_ntds_invocation_id(ldb);
7716 if (invocation_id == NULL) {
7717 return LDB_ERR_OPERATIONS_ERROR;
7720 /* if the existing link is active, remove its backlink */
7724 * NOTE WELL: After this we will never (at runtime) be
7725 * able to find this forward link (for instant
7726 * removal) if/when the link target is deleted.
7728 * We have dbcheck rules to cover this and cope otherwise
7729 * by filtering at runtime (i.e. in the extended_dn module).
7731 ret = replmd_add_backlink(module, replmd_private, schema,
7732 src_obj_dn, target_guid, false,
7734 if (ret != LDB_SUCCESS) {
7739 /* mark the existing value as deleted */
7740 ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
7741 target_dsdb_dn, invocation_id, seq_num,
7742 seq_num, now, true);
7747 * Checks for a conflict in single-valued link attributes, and tries to
7748 * resolve the problem if possible.
7750 * Single-valued links should only ever have one active value. If we already
7751 * have an active link value, and during replication we receive an active link
7752 * value for a different target DN, then we need to resolve this inconsistency
7753 * and determine which value should be active. If the received info is better/
7754 * newer than the existing link attribute, then we need to set our existing
7755 * link as deleted. If the received info is worse/older, then we should continue
7756 * to add it, but set it as an inactive link.
7758 * Note that this is a corner-case that is unlikely to happen (but if it does
7759 * happen, we don't want it to break replication completely).
7761 * @param pdn_being_modified the parsed DN corresponding to the received link
7762 * target (note this is NULL if the link does not already exist in our DB)
7763 * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
7764 * any existing active or inactive values for the attribute in our DB.
7765 * @param dsdb_dn the target DN for the received link attribute
7766 * @param add_as_inactive gets set to true if the received link is worse than
7767 * the existing link - it should still be added, but as an inactive link.
7769 static int replmd_check_singleval_la_conflict(struct ldb_module *module,
7770 struct replmd_private *replmd_private,
7771 TALLOC_CTX *mem_ctx,
7772 struct ldb_dn *src_obj_dn,
7773 struct drsuapi_DsReplicaLinkedAttribute *la,
7774 struct dsdb_dn *dsdb_dn,
7775 struct parsed_dn *pdn_being_modified,
7776 struct parsed_dn *pdn_list,
7777 struct ldb_message_element *old_el,
7778 const struct dsdb_schema *schema,
7779 const struct dsdb_attribute *attr,
7781 bool *add_as_inactive)
7783 struct parsed_dn *active_pdn = NULL;
7784 bool update_is_newer = false;
7788 * check if there's a conflict for single-valued links, i.e. an active
7789 * linked attribute already exists, but it has a different target value
7791 ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
7792 old_el->num_values, attr,
7795 if (ret != LDB_SUCCESS) {
7800 * If no active value exists (or the received info is for the currently
7801 * active value), then no conflict exists
7803 if (active_pdn == NULL || active_pdn == pdn_being_modified) {
7807 DBG_WARNING("Link conflict for %s attribute on %s\n",
7808 attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
7810 /* Work out how to resolve the conflict based on which info is better */
7811 update_is_newer = replmd_link_update_is_newer(active_pdn, la);
7813 if (update_is_newer) {
7814 DBG_WARNING("Using received value %s, over existing target %s\n",
7815 ldb_dn_get_linearized(dsdb_dn->dn),
7816 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
7819 * Delete our existing active link. The received info will then
7820 * be added (through normal link processing) as the active value
7822 ret = replmd_delete_link_value(module, replmd_private, old_el,
7823 src_obj_dn, schema, attr,
7824 seq_num, true, &active_pdn->guid,
7825 active_pdn->dsdb_dn,
7828 if (ret != LDB_SUCCESS) {
7832 DBG_WARNING("Using existing target %s, over received value %s\n",
7833 ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
7834 ldb_dn_get_linearized(dsdb_dn->dn));
7837 * we want to keep our existing active link and add the
7838 * received link as inactive
7840 *add_as_inactive = true;
7847 process one linked attribute structure
7849 static int replmd_process_linked_attribute(struct ldb_module *module,
7850 TALLOC_CTX *mem_ctx,
7851 struct replmd_private *replmd_private,
7852 struct ldb_message *msg,
7853 const struct dsdb_attribute *attr,
7854 struct la_entry *la_entry,
7855 struct ldb_request *parent)
7857 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7858 struct ldb_context *ldb = ldb_module_get_ctx(module);
7859 const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7861 struct dsdb_dn *dsdb_dn = NULL;
7862 uint64_t seq_num = 0;
7863 struct ldb_message_element *old_el;
7864 time_t t = time(NULL);
7865 struct parsed_dn *pdn_list, *pdn, *next;
7866 struct GUID guid = GUID_zero();
7867 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
7869 struct dsdb_dn *old_dsdb_dn = NULL;
7870 struct ldb_val *val_to_update = NULL;
7871 bool add_as_inactive = false;
7874 /* the value blob for the attribute holds the target object DN */
7875 status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7876 la->value.blob, &dsdb_dn);
7877 if (!W_ERROR_IS_OK(status)) {
7878 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7879 attr->lDAPDisplayName,
7880 ldb_dn_get_linearized(msg->dn),
7881 win_errstr(status));
7882 return LDB_ERR_OPERATIONS_ERROR;
7885 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
7886 if (old_el == NULL) {
7887 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
7888 if (ret != LDB_SUCCESS) {
7889 ldb_module_oom(module);
7890 return LDB_ERR_OPERATIONS_ERROR;
7893 old_el->flags = LDB_FLAG_MOD_REPLACE;
7896 /* parse the existing links */
7897 ret = get_parsed_dns_trusted(module, replmd_private, mem_ctx, old_el, &pdn_list,
7898 attr->syntax->ldap_oid, parent);
7900 if (ret != LDB_SUCCESS) {
7904 ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
7905 true, &guid, &ignore_link);
7907 if (ret != LDB_SUCCESS) {
7912 * there are some cases where the target object doesn't exist, but it's
7913 * OK to ignore the linked attribute
7919 /* see if this link already exists */
7920 ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
7923 dsdb_dn->extra_part, 0,
7925 attr->syntax->ldap_oid,
7927 if (ret != LDB_SUCCESS) {
7931 if (!replmd_link_update_is_newer(pdn, la)) {
7932 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
7933 old_el->name, ldb_dn_get_linearized(msg->dn),
7934 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
7938 /* get a seq_num for this change */
7939 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
7940 if (ret != LDB_SUCCESS) {
7945 * check for single-valued link conflicts, i.e. an active linked
7946 * attribute already exists, but it has a different target value
7949 ret = replmd_check_singleval_la_conflict(module, replmd_private,
7950 mem_ctx, msg->dn, la,
7951 dsdb_dn, pdn, pdn_list,
7952 old_el, schema, attr,
7955 if (ret != LDB_SUCCESS) {
7961 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
7963 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
7964 /* remove the existing backlink */
7965 ret = replmd_add_backlink(module, replmd_private,
7968 &pdn->guid, false, attr,
7970 if (ret != LDB_SUCCESS) {
7975 val_to_update = pdn->v;
7976 old_dsdb_dn = pdn->dsdb_dn;
7982 * We know where the new one needs to be, from the *next
7983 * pointer into pdn_list.
7986 offset = old_el->num_values;
7988 if (next->dsdb_dn == NULL) {
7989 ret = really_parse_trusted_dn(mem_ctx, ldb, next,
7990 attr->syntax->ldap_oid);
7991 if (ret != LDB_SUCCESS) {
7995 offset = next - pdn_list;
7996 if (offset > old_el->num_values) {
7997 return LDB_ERR_OPERATIONS_ERROR;
8001 old_el->values = talloc_realloc(msg->elements, old_el->values,
8002 struct ldb_val, old_el->num_values+1);
8003 if (!old_el->values) {
8004 ldb_module_oom(module);
8005 return LDB_ERR_OPERATIONS_ERROR;
8008 if (offset != old_el->num_values) {
8009 memmove(&old_el->values[offset + 1], &old_el->values[offset],
8010 (old_el->num_values - offset) * sizeof(old_el->values[0]));
8013 old_el->num_values++;
8015 val_to_update = &old_el->values[offset];
8019 /* set the link attribute's value to the info that was received */
8020 ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8021 &la->meta_data.originating_invocation_id,
8022 la->meta_data.originating_usn, seq_num,
8023 la->meta_data.originating_change_time,
8024 la->meta_data.version,
8026 if (ret != LDB_SUCCESS) {
8030 if (add_as_inactive) {
8032 /* Set the new link as inactive/deleted to avoid conflicts */
8033 ret = replmd_delete_link_value(module, replmd_private, old_el,
8034 msg->dn, schema, attr, seq_num,
8035 false, &guid, dsdb_dn,
8038 if (ret != LDB_SUCCESS) {
8042 } else if (active) {
8044 /* if the new link is active, then add the new backlink */
8045 ret = replmd_add_backlink(module, replmd_private,
8050 if (ret != LDB_SUCCESS) {
8055 /* we only change whenChanged and uSNChanged if the seq_num
8057 ldb_msg_remove_attr(msg, "whenChanged");
8058 ldb_msg_remove_attr(msg, "uSNChanged");
8059 ret = add_time_element(msg, "whenChanged", t);
8060 if (ret != LDB_SUCCESS) {
8065 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8066 if (ret != LDB_SUCCESS) {
8071 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8072 if (old_el == NULL) {
8073 return ldb_operr(ldb);
8076 ret = dsdb_check_single_valued_link(attr, old_el);
8077 if (ret != LDB_SUCCESS) {
8081 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8086 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8088 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8089 return replmd_extended_replicated_objects(module, req);
8092 return ldb_next_request(module, req);
8097 we hook into the transaction operations to allow us to
8098 perform the linked attribute updates at the end of the whole
8099 transaction. This allows a forward linked attribute to be created
8100 before the object is created. During a vampire, w2k8 sends us linked
8101 attributes before the objects they are part of.
8103 static int replmd_start_transaction(struct ldb_module *module)
8105 /* create our private structure for this transaction */
8106 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8107 struct replmd_private);
8108 replmd_txn_cleanup(replmd_private);
8110 /* free any leftover mod_usn records from cancelled
8112 while (replmd_private->ncs) {
8113 struct nc_entry *e = replmd_private->ncs;
8114 DLIST_REMOVE(replmd_private->ncs, e);
8118 replmd_private->originating_updates = false;
8120 return ldb_next_start_trans(module);
8124 * Processes a group of linked attributes that apply to the same source-object
8127 static int replmd_process_la_group(struct ldb_module *module,
8128 struct replmd_private *replmd_private,
8129 struct la_group *la_group)
8131 struct la_entry *la = NULL;
8132 struct la_entry *prev = NULL;
8134 TALLOC_CTX *tmp_ctx = talloc_new(la_group);
8135 struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8136 struct ldb_message *msg = NULL;
8137 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8138 struct ldb_context *ldb = ldb_module_get_ctx(module);
8139 const struct dsdb_attribute *attr = NULL;
8142 * get the attribute being modified and the search result for the
8145 ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8148 if (ret != LDB_SUCCESS) {
8153 * Check for deleted objects per MS-DRSR 4.1.10.6.14
8154 * ProcessLinkValue, because link updates are not applied to
8155 * recycled and tombstone objects. We don't have to delete
8156 * any existing link, that should have happened when the
8157 * object deletion was replicated or initiated.
8159 * This needs isDeleted and isRecycled to be included as
8160 * attributes in the search and so in msg if set.
8162 replmd_deletion_state(module, msg, &deletion_state, NULL);
8164 if (deletion_state >= OBJECT_RECYCLED) {
8165 TALLOC_FREE(tmp_ctx);
8170 * Now that we know the deletion_state, remove the extra
8171 * attributes added for that purpose. We need to do this
8172 * otherwise in the case of isDeleted: FALSE the modify will
8175 * Failed to apply linked attribute change 'attribute 'isDeleted':
8176 * invalid modify flags on
8177 * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8180 * This is becaue isDeleted is a Boolean, so FALSE is a
8181 * legitimate value (set by Samba's deletetest.py)
8183 ldb_msg_remove_attr(msg, "isDeleted");
8184 ldb_msg_remove_attr(msg, "isRecycled");
8186 /* go through and process the link targets for this source object */
8187 for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8188 prev = DLIST_PREV(la);
8189 DLIST_REMOVE(la_group->la_entries, la);
8190 ret = replmd_process_linked_attribute(module, tmp_ctx,
8192 msg, attr, la, NULL);
8193 if (ret != LDB_SUCCESS) {
8194 replmd_txn_cleanup(replmd_private);
8198 if ((++replmd_private->num_processed % 8192) == 0) {
8199 DBG_NOTICE("Processed %u/%u linked attributes\n",
8200 replmd_private->num_processed,
8201 replmd_private->total_links);
8205 /* apply the link changes to the source object */
8206 ret = linked_attr_modify(module, msg, NULL);
8207 if (ret != LDB_SUCCESS) {
8208 ldb_debug(ldb, LDB_DEBUG_WARNING,
8209 "Failed to apply linked attribute change '%s'\n%s\n",
8211 ldb_ldif_message_redacted_string(ldb,
8213 LDB_CHANGETYPE_MODIFY,
8215 TALLOC_FREE(tmp_ctx);
8219 TALLOC_FREE(tmp_ctx);
8224 on prepare commit we loop over our queued la_context structures and
8227 static int replmd_prepare_commit(struct ldb_module *module)
8229 struct replmd_private *replmd_private =
8230 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8231 struct la_group *la_group, *prev;
8234 if (replmd_private->la_list != NULL) {
8235 DBG_NOTICE("Processing linked attributes\n");
8239 * Walk the list of linked attributes from DRS replication.
8241 * We walk backwards, to do the first entry first, as we
8242 * added the entries with DLIST_ADD() which puts them at the
8245 * Links are grouped together so we process links for the same
8246 * source object in one go.
8248 for (la_group = DLIST_TAIL(replmd_private->la_list);
8252 prev = DLIST_PREV(la_group);
8253 DLIST_REMOVE(replmd_private->la_list, la_group);
8254 ret = replmd_process_la_group(module, replmd_private,
8256 if (ret != LDB_SUCCESS) {
8257 replmd_txn_cleanup(replmd_private);
8262 replmd_txn_cleanup(replmd_private);
8264 /* possibly change @REPLCHANGED */
8265 ret = replmd_notify_store(module, NULL);
8266 if (ret != LDB_SUCCESS) {
8270 return ldb_next_prepare_commit(module);
8273 static int replmd_del_transaction(struct ldb_module *module)
8275 struct replmd_private *replmd_private =
8276 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8277 replmd_txn_cleanup(replmd_private);
8279 return ldb_next_del_trans(module);
8283 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8284 .name = "repl_meta_data",
8285 .init_context = replmd_init,
8287 .modify = replmd_modify,
8288 .rename = replmd_rename,
8289 .del = replmd_delete,
8290 .extended = replmd_extended,
8291 .start_transaction = replmd_start_transaction,
8292 .prepare_commit = replmd_prepare_commit,
8293 .del_transaction = replmd_del_transaction,
8296 int ldb_repl_meta_data_module_init(const char *version)
8298 LDB_MODULE_CHECK_VERSION(version);
8299 return ldb_register_module(&ldb_repl_meta_data_module_ops);