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 "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
54 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
55 * Deleted Objects Container
57 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
59 struct replmd_private {
61 struct la_entry *la_list;
63 struct la_backlink *la_backlinks;
65 struct nc_entry *prev, *next;
68 uint64_t mod_usn_urgent;
70 struct ldb_dn *schema_dn;
74 struct la_entry *next, *prev;
75 struct drsuapi_DsReplicaLinkedAttribute *la;
78 struct replmd_replicated_request {
79 struct ldb_module *module;
80 struct ldb_request *req;
82 const struct dsdb_schema *schema;
83 struct GUID our_invocation_id;
85 /* the controls we pass down */
86 struct ldb_control **controls;
88 /* details for the mode where we apply a bunch of inbound replication meessages */
90 uint32_t index_current;
91 struct dsdb_extended_replicated_objects *objs;
93 struct ldb_message *search_msg;
94 struct GUID local_parent_guid;
102 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
103 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
105 enum urgent_situation {
106 REPL_URGENT_ON_CREATE = 1,
107 REPL_URGENT_ON_UPDATE = 2,
108 REPL_URGENT_ON_DELETE = 4
111 enum deletion_state {
112 OBJECT_NOT_DELETED=1,
119 static void replmd_deletion_state(struct ldb_module *module,
120 const struct ldb_message *msg,
121 enum deletion_state *current_state,
122 enum deletion_state *next_state)
125 bool enabled = false;
128 *current_state = OBJECT_REMOVED;
129 if (next_state != NULL) {
130 *next_state = OBJECT_REMOVED;
135 ret = dsdb_recyclebin_enabled(module, &enabled);
136 if (ret != LDB_SUCCESS) {
140 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
142 *current_state = OBJECT_TOMBSTONE;
143 if (next_state != NULL) {
144 *next_state = OBJECT_REMOVED;
149 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
150 *current_state = OBJECT_RECYCLED;
151 if (next_state != NULL) {
152 *next_state = OBJECT_REMOVED;
157 *current_state = OBJECT_DELETED;
158 if (next_state != NULL) {
159 *next_state = OBJECT_RECYCLED;
164 *current_state = OBJECT_NOT_DELETED;
165 if (next_state == NULL) {
170 *next_state = OBJECT_DELETED;
172 *next_state = OBJECT_TOMBSTONE;
176 static const struct {
177 const char *update_name;
178 enum urgent_situation repl_situation;
179 } urgent_objects[] = {
180 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
181 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
182 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
183 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
184 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
185 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
189 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
190 static const char *urgent_attrs[] = {
193 "userAccountControl",
198 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
199 enum urgent_situation situation)
202 for (i=0; urgent_objects[i].update_name; i++) {
204 if ((situation & urgent_objects[i].repl_situation) == 0) {
208 for (j=0; j<objectclass_el->num_values; j++) {
209 const struct ldb_val *v = &objectclass_el->values[j];
210 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
218 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
220 if (ldb_attr_in_list(urgent_attrs, el->name)) {
227 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
230 initialise the module
231 allocate the private structure and build the list
232 of partition DNs for use by replmd_notify()
234 static int replmd_init(struct ldb_module *module)
236 struct replmd_private *replmd_private;
237 struct ldb_context *ldb = ldb_module_get_ctx(module);
239 replmd_private = talloc_zero(module, struct replmd_private);
240 if (replmd_private == NULL) {
242 return LDB_ERR_OPERATIONS_ERROR;
244 ldb_module_set_private(module, replmd_private);
246 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
248 return ldb_next_init(module);
252 cleanup our per-transaction contexts
254 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
256 talloc_free(replmd_private->la_ctx);
257 replmd_private->la_list = NULL;
258 replmd_private->la_ctx = NULL;
260 talloc_free(replmd_private->bl_ctx);
261 replmd_private->la_backlinks = NULL;
262 replmd_private->bl_ctx = NULL;
267 struct la_backlink *next, *prev;
268 const char *attr_name;
269 struct GUID forward_guid, target_guid;
274 process a backlinks we accumulated during a transaction, adding and
275 deleting the backlinks from the target objects
277 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
279 struct ldb_dn *target_dn, *source_dn;
281 struct ldb_context *ldb = ldb_module_get_ctx(module);
282 struct ldb_message *msg;
283 TALLOC_CTX *tmp_ctx = talloc_new(bl);
289 - construct ldb_message
290 - either an add or a delete
292 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
293 if (ret != LDB_SUCCESS) {
294 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
295 GUID_string(bl, &bl->target_guid)));
299 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
300 if (ret != LDB_SUCCESS) {
301 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
302 GUID_string(bl, &bl->forward_guid));
303 talloc_free(tmp_ctx);
307 msg = ldb_msg_new(tmp_ctx);
309 ldb_module_oom(module);
310 talloc_free(tmp_ctx);
311 return LDB_ERR_OPERATIONS_ERROR;
314 /* construct a ldb_message for adding/deleting the backlink */
316 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
318 ldb_module_oom(module);
319 talloc_free(tmp_ctx);
320 return LDB_ERR_OPERATIONS_ERROR;
322 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
323 if (ret != LDB_SUCCESS) {
324 talloc_free(tmp_ctx);
327 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
329 /* a backlink should never be single valued. Unfortunately the
330 exchange schema has a attribute
331 msExchBridgeheadedLocalConnectorsDNBL which is single
332 valued and a backlink. We need to cope with that by
333 ignoring the single value flag */
334 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
336 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
337 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
338 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
339 cope with possible corruption where the backlink has
340 already been removed */
341 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
342 ldb_dn_get_linearized(target_dn),
343 ldb_dn_get_linearized(source_dn),
344 ldb_errstring(ldb)));
346 } else if (ret != LDB_SUCCESS) {
347 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
348 bl->active?"add":"remove",
349 ldb_dn_get_linearized(source_dn),
350 ldb_dn_get_linearized(target_dn),
352 talloc_free(tmp_ctx);
355 talloc_free(tmp_ctx);
360 add a backlink to the list of backlinks to add/delete in the prepare
363 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
364 struct GUID *forward_guid, struct GUID *target_guid,
365 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
367 const struct dsdb_attribute *target_attr;
368 struct la_backlink *bl;
369 struct replmd_private *replmd_private =
370 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
372 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
375 * windows 2003 has a broken schema where the
376 * definition of msDS-IsDomainFor is missing (which is
377 * supposed to be the backlink of the
378 * msDS-HasDomainNCs attribute
383 /* see if its already in the list */
384 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
385 if (GUID_equal(forward_guid, &bl->forward_guid) &&
386 GUID_equal(target_guid, &bl->target_guid) &&
387 (target_attr->lDAPDisplayName == bl->attr_name ||
388 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
394 /* we found an existing one */
395 if (bl->active == active) {
398 DLIST_REMOVE(replmd_private->la_backlinks, bl);
403 if (replmd_private->bl_ctx == NULL) {
404 replmd_private->bl_ctx = talloc_new(replmd_private);
405 if (replmd_private->bl_ctx == NULL) {
406 ldb_module_oom(module);
407 return LDB_ERR_OPERATIONS_ERROR;
412 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
414 ldb_module_oom(module);
415 return LDB_ERR_OPERATIONS_ERROR;
418 /* Ensure the schema does not go away before the bl->attr_name is used */
419 if (!talloc_reference(bl, schema)) {
421 ldb_module_oom(module);
422 return LDB_ERR_OPERATIONS_ERROR;
425 bl->attr_name = target_attr->lDAPDisplayName;
426 bl->forward_guid = *forward_guid;
427 bl->target_guid = *target_guid;
430 /* the caller may ask for this backlink to be processed
433 int ret = replmd_process_backlink(module, bl, NULL);
438 DLIST_ADD(replmd_private->la_backlinks, bl);
445 * Callback for most write operations in this module:
447 * notify the repl task that a object has changed. The notifies are
448 * gathered up in the replmd_private structure then written to the
449 * @REPLCHANGED object in each partition during the prepare_commit
451 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
454 struct replmd_replicated_request *ac =
455 talloc_get_type_abort(req->context, struct replmd_replicated_request);
456 struct replmd_private *replmd_private =
457 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
458 struct nc_entry *modified_partition;
459 struct ldb_control *partition_ctrl;
460 const struct dsdb_control_current_partition *partition;
462 struct ldb_control **controls;
464 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
466 controls = ares->controls;
467 if (ldb_request_get_control(ac->req,
468 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
470 * Remove the current partition control from what we pass up
471 * the chain if it hasn't been requested manually.
473 controls = ldb_controls_except_specified(ares->controls, ares,
477 if (ares->error != LDB_SUCCESS) {
478 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
479 return ldb_module_done(ac->req, controls,
480 ares->response, ares->error);
483 if (ares->type != LDB_REPLY_DONE) {
484 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
485 return ldb_module_done(ac->req, NULL,
486 NULL, LDB_ERR_OPERATIONS_ERROR);
489 if (!partition_ctrl) {
490 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
491 return ldb_module_done(ac->req, NULL,
492 NULL, LDB_ERR_OPERATIONS_ERROR);
495 partition = talloc_get_type_abort(partition_ctrl->data,
496 struct dsdb_control_current_partition);
498 if (ac->seq_num > 0) {
499 for (modified_partition = replmd_private->ncs; modified_partition;
500 modified_partition = modified_partition->next) {
501 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
506 if (modified_partition == NULL) {
507 modified_partition = talloc_zero(replmd_private, struct nc_entry);
508 if (!modified_partition) {
509 ldb_oom(ldb_module_get_ctx(ac->module));
510 return ldb_module_done(ac->req, NULL,
511 NULL, LDB_ERR_OPERATIONS_ERROR);
513 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
514 if (!modified_partition->dn) {
515 ldb_oom(ldb_module_get_ctx(ac->module));
516 return ldb_module_done(ac->req, NULL,
517 NULL, LDB_ERR_OPERATIONS_ERROR);
519 DLIST_ADD(replmd_private->ncs, modified_partition);
522 if (ac->seq_num > modified_partition->mod_usn) {
523 modified_partition->mod_usn = ac->seq_num;
525 modified_partition->mod_usn_urgent = ac->seq_num;
530 if (ac->apply_mode) {
531 ret = replmd_replicated_apply_isDeleted(ac);
532 if (ret != LDB_SUCCESS) {
533 return ldb_module_done(ac->req, NULL, NULL, ret);
537 /* free the partition control container here, for the
538 * common path. Other cases will have it cleaned up
539 * eventually with the ares */
540 talloc_free(partition_ctrl);
541 return ldb_module_done(ac->req, controls,
542 ares->response, LDB_SUCCESS);
548 * update a @REPLCHANGED record in each partition if there have been
549 * any writes of replicated data in the partition
551 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
553 struct replmd_private *replmd_private =
554 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
556 while (replmd_private->ncs) {
558 struct nc_entry *modified_partition = replmd_private->ncs;
560 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
561 modified_partition->mod_usn,
562 modified_partition->mod_usn_urgent, parent);
563 if (ret != LDB_SUCCESS) {
564 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
565 ldb_dn_get_linearized(modified_partition->dn)));
568 DLIST_REMOVE(replmd_private->ncs, modified_partition);
569 talloc_free(modified_partition);
577 created a replmd_replicated_request context
579 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
580 struct ldb_request *req)
582 struct ldb_context *ldb;
583 struct replmd_replicated_request *ac;
584 const struct GUID *our_invocation_id;
586 ldb = ldb_module_get_ctx(module);
588 ac = talloc_zero(req, struct replmd_replicated_request);
597 ac->schema = dsdb_get_schema(ldb, ac);
599 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
600 "replmd_modify: no dsdb_schema loaded");
601 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
606 /* get our invocationId */
607 our_invocation_id = samdb_ntds_invocation_id(ldb);
608 if (!our_invocation_id) {
609 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
610 "replmd_add: unable to find invocationId\n");
614 ac->our_invocation_id = *our_invocation_id;
620 add a time element to a record
622 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
624 struct ldb_message_element *el;
628 if (ldb_msg_find_element(msg, attr) != NULL) {
632 s = ldb_timestring(msg, t);
634 return LDB_ERR_OPERATIONS_ERROR;
637 ret = ldb_msg_add_string(msg, attr, s);
638 if (ret != LDB_SUCCESS) {
642 el = ldb_msg_find_element(msg, attr);
643 /* always set as replace. This works because on add ops, the flag
645 el->flags = LDB_FLAG_MOD_REPLACE;
651 add a uint64_t element to a record
653 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
654 const char *attr, uint64_t v)
656 struct ldb_message_element *el;
659 if (ldb_msg_find_element(msg, attr) != NULL) {
663 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
664 if (ret != LDB_SUCCESS) {
668 el = ldb_msg_find_element(msg, attr);
669 /* always set as replace. This works because on add ops, the flag
671 el->flags = LDB_FLAG_MOD_REPLACE;
676 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
677 const struct replPropertyMetaData1 *m2,
678 const uint32_t *rdn_attid)
681 * This assignment seems inoccous, but it is critical for the
682 * system, as we need to do the comparisons as a unsigned
683 * quantity, not signed (enums are signed integers)
685 uint32_t attid_1 = m1->attid;
686 uint32_t attid_2 = m2->attid;
688 if (attid_1 == attid_2) {
693 * See above regarding this being an unsigned comparison.
694 * Otherwise when the high bit is set on non-standard
695 * attributes, they would end up first, before objectClass
698 return attid_1 > attid_2 ? 1 : -1;
701 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
702 struct replPropertyMetaDataCtr1 *ctr1,
705 if (ctr1->count == 0) {
706 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
707 "No elements found in replPropertyMetaData for %s!\n",
708 ldb_dn_get_linearized(dn));
709 return LDB_ERR_CONSTRAINT_VIOLATION;
712 /* the objectClass attribute is value 0x00000000, so must be first */
713 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
714 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
715 "No objectClass found in replPropertyMetaData for %s!\n",
716 ldb_dn_get_linearized(dn));
717 return LDB_ERR_OBJECT_CLASS_VIOLATION;
723 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
724 struct replPropertyMetaDataCtr1 *ctr1,
727 /* Note this is O(n^2) for the almost-sorted case, which this is */
728 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
729 replmd_replPropertyMetaData1_attid_sort);
730 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
733 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
734 const struct ldb_message_element *e2,
735 const struct dsdb_schema *schema)
737 const struct dsdb_attribute *a1;
738 const struct dsdb_attribute *a2;
741 * TODO: make this faster by caching the dsdb_attribute pointer
742 * on the ldb_messag_element
745 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
746 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
749 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
753 return strcasecmp(e1->name, e2->name);
755 if (a1->attributeID_id == a2->attributeID_id) {
758 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
761 static void replmd_ldb_message_sort(struct ldb_message *msg,
762 const struct dsdb_schema *schema)
764 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
767 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
768 const struct GUID *invocation_id, uint64_t seq_num,
769 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
773 fix up linked attributes in replmd_add.
774 This involves setting up the right meta-data in extended DN
775 components, and creating backlinks to the object
777 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
778 uint64_t seq_num, const struct GUID *invocationId, time_t t,
779 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
782 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
783 struct ldb_context *ldb = ldb_module_get_ctx(module);
785 /* We will take a reference to the schema in replmd_add_backlink */
786 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
789 unix_to_nt_time(&now, t);
791 for (i=0; i<el->num_values; i++) {
792 struct ldb_val *v = &el->values[i];
793 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
794 struct GUID target_guid;
798 /* note that the DN already has the extended
799 components from the extended_dn_store module */
800 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
801 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
802 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
803 if (ret != LDB_SUCCESS) {
804 talloc_free(tmp_ctx);
807 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
808 if (ret != LDB_SUCCESS) {
809 talloc_free(tmp_ctx);
814 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
815 seq_num, seq_num, now, 0, false);
816 if (ret != LDB_SUCCESS) {
817 talloc_free(tmp_ctx);
821 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
822 if (ret != LDB_SUCCESS) {
823 talloc_free(tmp_ctx);
828 talloc_free(tmp_ctx);
834 intercept add requests
836 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
838 struct samldb_msds_intid_persistant *msds_intid_struct;
839 struct ldb_context *ldb;
840 struct ldb_control *control;
841 struct replmd_replicated_request *ac;
842 enum ndr_err_code ndr_err;
843 struct ldb_request *down_req;
844 struct ldb_message *msg;
845 const DATA_BLOB *guid_blob;
847 struct replPropertyMetaDataBlob nmd;
848 struct ldb_val nmd_value;
851 * The use of a time_t here seems odd, but as the NTTIME
852 * elements are actually declared as NTTIME_1sec in the IDL,
853 * getting a higher resolution timestamp is not required.
855 time_t t = time(NULL);
860 unsigned int functional_level;
862 bool allow_add_guid = false;
863 bool remove_current_guid = false;
864 bool is_urgent = false;
865 bool is_schema_nc = false;
866 struct ldb_message_element *objectclass_el;
867 struct replmd_private *replmd_private =
868 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
870 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
871 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
873 allow_add_guid = true;
876 /* do not manipulate our control entries */
877 if (ldb_dn_is_special(req->op.add.message->dn)) {
878 return ldb_next_request(module, req);
881 ldb = ldb_module_get_ctx(module);
883 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
885 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
886 if (guid_blob != NULL) {
887 if (!allow_add_guid) {
888 ldb_set_errstring(ldb,
889 "replmd_add: it's not allowed to add an object with objectGUID!");
890 return LDB_ERR_UNWILLING_TO_PERFORM;
892 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
893 if (!NT_STATUS_IS_OK(status)) {
894 ldb_set_errstring(ldb,
895 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
896 return LDB_ERR_UNWILLING_TO_PERFORM;
898 /* we remove this attribute as it can be a string and
899 * will not be treated correctly and then we will re-add
900 * it later on in the good format */
901 remove_current_guid = true;
905 guid = GUID_random();
908 ac = replmd_ctx_init(module, req);
910 return ldb_module_oom(module);
913 functional_level = dsdb_functional_level(ldb);
915 /* Get a sequence number from the backend */
916 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
917 if (ret != LDB_SUCCESS) {
922 /* we have to copy the message as the caller might have it as a const */
923 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
927 return LDB_ERR_OPERATIONS_ERROR;
930 /* generated times */
931 unix_to_nt_time(&now, t);
932 time_str = ldb_timestring(msg, t);
936 return LDB_ERR_OPERATIONS_ERROR;
938 if (remove_current_guid) {
939 ldb_msg_remove_attr(msg,"objectGUID");
943 * remove autogenerated attributes
945 ldb_msg_remove_attr(msg, "whenCreated");
946 ldb_msg_remove_attr(msg, "whenChanged");
947 ldb_msg_remove_attr(msg, "uSNCreated");
948 ldb_msg_remove_attr(msg, "uSNChanged");
949 ldb_msg_remove_attr(msg, "replPropertyMetaData");
952 * readd replicated attributes
954 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
955 if (ret != LDB_SUCCESS) {
961 /* build the replication meta_data */
964 nmd.ctr.ctr1.count = msg->num_elements;
965 nmd.ctr.ctr1.array = talloc_array(msg,
966 struct replPropertyMetaData1,
968 if (!nmd.ctr.ctr1.array) {
971 return LDB_ERR_OPERATIONS_ERROR;
974 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
976 for (i=0; i < msg->num_elements; i++) {
977 struct ldb_message_element *e = &msg->elements[i];
978 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
979 const struct dsdb_attribute *sa;
981 if (e->name[0] == '@') continue;
983 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
985 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
986 "replmd_add: attribute '%s' not defined in schema\n",
989 return LDB_ERR_NO_SUCH_ATTRIBUTE;
992 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
993 /* if the attribute is not replicated (0x00000001)
994 * or constructed (0x00000004) it has no metadata
999 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1000 ret = replmd_add_fix_la(module, e, ac->seq_num, &ac->our_invocation_id, t, &guid, sa, req);
1001 if (ret != LDB_SUCCESS) {
1005 /* linked attributes are not stored in
1006 replPropertyMetaData in FL above w2k */
1010 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1012 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1013 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1016 if (rdn_val == NULL) {
1019 return LDB_ERR_OPERATIONS_ERROR;
1022 rdn = (const char*)rdn_val->data;
1023 if (strcmp(rdn, "Deleted Objects") == 0) {
1025 * Set the originating_change_time to 29/12/9999 at 23:59:59
1026 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1028 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1030 m->originating_change_time = now;
1033 m->originating_change_time = now;
1035 m->originating_invocation_id = ac->our_invocation_id;
1036 m->originating_usn = ac->seq_num;
1037 m->local_usn = ac->seq_num;
1041 /* fix meta data count */
1042 nmd.ctr.ctr1.count = ni;
1045 * sort meta data array
1047 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1048 if (ret != LDB_SUCCESS) {
1049 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1054 /* generated NDR encoded values */
1055 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1057 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1058 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1061 return LDB_ERR_OPERATIONS_ERROR;
1065 * add the autogenerated values
1067 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1068 if (ret != LDB_SUCCESS) {
1073 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1074 if (ret != LDB_SUCCESS) {
1079 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1080 if (ret != LDB_SUCCESS) {
1085 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1086 if (ret != LDB_SUCCESS) {
1091 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1092 if (ret != LDB_SUCCESS) {
1099 * sort the attributes by attid before storing the object
1101 replmd_ldb_message_sort(msg, ac->schema);
1104 * Assert that we do have an objectClass
1106 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1107 if (objectclass_el == NULL) {
1108 ldb_asprintf_errstring(ldb, __location__
1109 ": objectClass missing on %s\n",
1110 ldb_dn_get_linearized(msg->dn));
1112 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1114 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1115 REPL_URGENT_ON_CREATE);
1117 ac->is_urgent = is_urgent;
1118 ret = ldb_build_add_req(&down_req, ldb, ac,
1121 ac, replmd_op_callback,
1124 LDB_REQ_SET_LOCATION(down_req);
1125 if (ret != LDB_SUCCESS) {
1130 /* current partition control is needed by "replmd_op_callback" */
1131 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1132 ret = ldb_request_add_control(down_req,
1133 DSDB_CONTROL_CURRENT_PARTITION_OID,
1135 if (ret != LDB_SUCCESS) {
1141 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1142 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1143 if (ret != LDB_SUCCESS) {
1149 /* mark the control done */
1151 control->critical = 0;
1153 if (ldb_dn_compare_base(replmd_private->schema_dn, req->op.add.message->dn) != 0) {
1155 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1156 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1157 if (msds_intid_struct) {
1158 msds_intid_struct->usn = ac->seq_num;
1161 /* go on with the call chain */
1162 return ldb_next_request(module, down_req);
1167 * update the replPropertyMetaData for one element
1169 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1170 struct ldb_message *msg,
1171 struct ldb_message_element *el,
1172 struct ldb_message_element *old_el,
1173 struct replPropertyMetaDataBlob *omd,
1174 const struct dsdb_schema *schema,
1176 const struct GUID *our_invocation_id,
1179 struct ldb_request *req)
1182 const struct dsdb_attribute *a;
1183 struct replPropertyMetaData1 *md1;
1184 bool may_skip = false;
1187 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1189 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1190 /* allow this to make it possible for dbcheck
1191 to remove bad attributes */
1195 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1197 return LDB_ERR_OPERATIONS_ERROR;
1200 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1202 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1207 * if the attribute's value haven't changed, and this isn't
1208 * just a delete of everything then return LDB_SUCCESS Unless
1209 * we have the provision control or if the attribute is
1210 * interSiteTopologyGenerator as this page explain:
1211 * http://support.microsoft.com/kb/224815 this attribute is
1212 * periodicaly written by the DC responsible for the intersite
1213 * generation in a given site
1215 * Unchanged could be deleting or replacing an already-gone
1216 * thing with an unconstrained delete/empty replace or a
1217 * replace with the same value, but not an add with the same
1218 * value because that could be about adding a duplicate (which
1219 * is for someone else to error out on).
1221 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1222 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1225 } else if (old_el == NULL && el->num_values == 0) {
1226 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1228 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1234 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1235 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1237 * allow this to make it possible for dbcheck
1238 * to rebuild broken metadata
1244 for (i=0; i<omd->ctr.ctr1.count; i++) {
1246 * First check if we find it under the msDS-IntID,
1247 * then check if we find it under the OID and
1250 * This allows the administrator to simply re-write
1251 * the attributes and so restore replication, which is
1252 * likely what they will try to do.
1254 if (attid == omd->ctr.ctr1.array[i].attid) {
1258 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1263 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1264 /* linked attributes are not stored in
1265 replPropertyMetaData in FL above w2k, but we do
1266 raise the seqnum for the object */
1267 if (*seq_num == 0 &&
1268 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1269 return LDB_ERR_OPERATIONS_ERROR;
1274 if (i == omd->ctr.ctr1.count) {
1275 /* we need to add a new one */
1276 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1277 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1278 if (omd->ctr.ctr1.array == NULL) {
1280 return LDB_ERR_OPERATIONS_ERROR;
1282 omd->ctr.ctr1.count++;
1283 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1286 /* Get a new sequence number from the backend. We only do this
1287 * if we have a change that requires a new
1288 * replPropertyMetaData element
1290 if (*seq_num == 0) {
1291 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1292 if (ret != LDB_SUCCESS) {
1293 return LDB_ERR_OPERATIONS_ERROR;
1297 md1 = &omd->ctr.ctr1.array[i];
1300 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1301 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1304 if (rdn_val == NULL) {
1306 return LDB_ERR_OPERATIONS_ERROR;
1309 rdn = (const char*)rdn_val->data;
1310 if (strcmp(rdn, "Deleted Objects") == 0) {
1312 * Set the originating_change_time to 29/12/9999 at 23:59:59
1313 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1315 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1317 md1->originating_change_time = now;
1320 md1->originating_change_time = now;
1322 md1->originating_invocation_id = *our_invocation_id;
1323 md1->originating_usn = *seq_num;
1324 md1->local_usn = *seq_num;
1330 * Bump the replPropertyMetaData version on an attribute, and if it
1331 * has changed (or forced by leaving rdn_old NULL), update the value
1334 * This is important, as calling a modify operation may not change the
1335 * version number if the values appear unchanged, but a rename between
1336 * parents bumps this value.
1339 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1340 struct ldb_message *msg,
1341 const struct ldb_val *rdn_new,
1342 const struct ldb_val *rdn_old,
1343 struct replPropertyMetaDataBlob *omd,
1344 struct replmd_replicated_request *ar,
1348 struct ldb_message_element new_el = {
1349 .flags = LDB_FLAG_MOD_REPLACE,
1350 .name = ldb_dn_get_rdn_name(msg->dn),
1352 .values = discard_const_p(struct ldb_val, rdn_new)
1354 struct ldb_message_element old_el = {
1355 .flags = LDB_FLAG_MOD_REPLACE,
1356 .name = ldb_dn_get_rdn_name(msg->dn),
1357 .num_values = rdn_old ? 1 : 0,
1358 .values = discard_const_p(struct ldb_val, rdn_old)
1361 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1362 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1363 if (ret != LDB_SUCCESS) {
1364 return ldb_oom(ldb);
1368 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1369 omd, ar->schema, &ar->seq_num,
1370 &ar->our_invocation_id,
1371 now, is_schema_nc, ar->req);
1375 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1377 uint32_t count = omd.ctr.ctr1.count;
1380 for (i=0; i < count; i++) {
1381 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1382 if (max < m.local_usn) {
1390 * update the replPropertyMetaData object each time we modify an
1391 * object. This is needed for DRS replication, as the merge on the
1392 * client is based on this object
1394 static int replmd_update_rpmd(struct ldb_module *module,
1395 const struct dsdb_schema *schema,
1396 struct ldb_request *req,
1397 const char * const *rename_attrs,
1398 struct ldb_message *msg, uint64_t *seq_num,
1399 time_t t, bool is_schema_nc,
1400 bool *is_urgent, bool *rodc)
1402 const struct ldb_val *omd_value;
1403 enum ndr_err_code ndr_err;
1404 struct replPropertyMetaDataBlob omd;
1407 const struct GUID *our_invocation_id;
1409 const char * const *attrs = NULL;
1410 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1411 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1412 struct ldb_result *res;
1413 struct ldb_context *ldb;
1414 struct ldb_message_element *objectclass_el;
1415 enum urgent_situation situation;
1416 bool rmd_is_provided;
1417 bool rmd_is_just_resorted = false;
1420 attrs = rename_attrs;
1425 ldb = ldb_module_get_ctx(module);
1427 our_invocation_id = samdb_ntds_invocation_id(ldb);
1428 if (!our_invocation_id) {
1429 /* this happens during an initial vampire while
1430 updating the schema */
1431 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1435 unix_to_nt_time(&now, t);
1437 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1438 rmd_is_provided = true;
1439 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1440 rmd_is_just_resorted = true;
1443 rmd_is_provided = false;
1446 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1447 * otherwise we consider we are updating */
1448 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1449 situation = REPL_URGENT_ON_DELETE;
1450 } else if (rename_attrs) {
1451 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1453 situation = REPL_URGENT_ON_UPDATE;
1456 if (rmd_is_provided) {
1457 /* In this case the change_replmetadata control was supplied */
1458 /* We check that it's the only attribute that is provided
1459 * (it's a rare case so it's better to keep the code simplier)
1460 * We also check that the highest local_usn is bigger or the same as
1463 if( msg->num_elements != 1 ||
1464 strncmp(msg->elements[0].name,
1465 "replPropertyMetaData", 20) ) {
1466 DEBUG(0,(__location__ ": changereplmetada control called without "\
1467 "a specified replPropertyMetaData attribute or with others\n"));
1468 return LDB_ERR_OPERATIONS_ERROR;
1470 if (situation != REPL_URGENT_ON_UPDATE) {
1471 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1472 return LDB_ERR_OPERATIONS_ERROR;
1474 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1476 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1477 ldb_dn_get_linearized(msg->dn)));
1478 return LDB_ERR_OPERATIONS_ERROR;
1480 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1481 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1482 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1483 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1484 ldb_dn_get_linearized(msg->dn)));
1485 return LDB_ERR_OPERATIONS_ERROR;
1488 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1489 DSDB_FLAG_NEXT_MODULE |
1490 DSDB_SEARCH_SHOW_RECYCLED |
1491 DSDB_SEARCH_SHOW_EXTENDED_DN |
1492 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1493 DSDB_SEARCH_REVEAL_INTERNALS, req);
1495 if (ret != LDB_SUCCESS) {
1499 if (rmd_is_just_resorted == false) {
1500 *seq_num = find_max_local_usn(omd);
1502 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1505 * The test here now allows for a new
1506 * replPropertyMetaData with no change, if was
1507 * just dbcheck re-sorting the values.
1509 if (*seq_num <= db_seq) {
1510 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1511 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1512 (long long)*seq_num, (long long)db_seq));
1513 return LDB_ERR_OPERATIONS_ERROR;
1518 /* search for the existing replPropertyMetaDataBlob. We need
1519 * to use REVEAL and ask for DNs in storage format to support
1520 * the check for values being the same in
1521 * replmd_update_rpmd_element()
1523 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1524 DSDB_FLAG_NEXT_MODULE |
1525 DSDB_SEARCH_SHOW_RECYCLED |
1526 DSDB_SEARCH_SHOW_EXTENDED_DN |
1527 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1528 DSDB_SEARCH_REVEAL_INTERNALS, req);
1529 if (ret != LDB_SUCCESS) {
1533 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1535 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1536 ldb_dn_get_linearized(msg->dn)));
1537 return LDB_ERR_OPERATIONS_ERROR;
1540 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1541 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1542 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1543 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1544 ldb_dn_get_linearized(msg->dn)));
1545 return LDB_ERR_OPERATIONS_ERROR;
1548 if (omd.version != 1) {
1549 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1550 omd.version, ldb_dn_get_linearized(msg->dn)));
1551 return LDB_ERR_OPERATIONS_ERROR;
1554 for (i=0; i<msg->num_elements; i++) {
1555 struct ldb_message_element *old_el;
1556 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1557 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1561 if (ret != LDB_SUCCESS) {
1565 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1566 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1573 * Assert that we have an objectClass attribute - this is major
1574 * corruption if we don't have this!
1576 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1577 if (objectclass_el != NULL) {
1579 * Now check if this objectClass means we need to do urgent replication
1581 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1585 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1586 ldb_asprintf_errstring(ldb, __location__
1587 ": objectClass missing on %s\n",
1588 ldb_dn_get_linearized(msg->dn));
1589 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1593 * replmd_update_rpmd_element has done an update if the
1596 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1597 struct ldb_val *md_value;
1598 struct ldb_message_element *el;
1600 /*if we are RODC and this is a DRSR update then its ok*/
1601 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1602 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1603 unsigned instanceType;
1605 ret = samdb_rodc(ldb, rodc);
1606 if (ret != LDB_SUCCESS) {
1607 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1609 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1610 return LDB_ERR_REFERRAL;
1613 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1614 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1615 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1616 "cannot change replicated attribute on partial replica");
1620 md_value = talloc(msg, struct ldb_val);
1621 if (md_value == NULL) {
1623 return LDB_ERR_OPERATIONS_ERROR;
1626 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1627 if (ret != LDB_SUCCESS) {
1628 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1632 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1633 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1634 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1635 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1636 ldb_dn_get_linearized(msg->dn)));
1637 return LDB_ERR_OPERATIONS_ERROR;
1640 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1641 if (ret != LDB_SUCCESS) {
1642 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1643 ldb_dn_get_linearized(msg->dn)));
1648 el->values = md_value;
1655 struct dsdb_dn *dsdb_dn;
1660 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1662 return GUID_compare(pdn1->guid, pdn2->guid);
1665 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1666 unsigned int count, struct GUID *guid,
1669 struct parsed_dn *ret;
1671 if (dn && GUID_all_zero(guid)) {
1672 /* when updating a link using DRS, we sometimes get a
1673 NULL GUID. We then need to try and match by DN */
1674 for (i=0; i<count; i++) {
1675 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1676 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1682 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1687 get a series of message element values as an array of DNs and GUIDs
1688 the result is sorted by GUID
1690 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1691 struct ldb_message_element *el, struct parsed_dn **pdn,
1692 const char *ldap_oid, struct ldb_request *parent)
1695 struct ldb_context *ldb = ldb_module_get_ctx(module);
1702 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1704 ldb_module_oom(module);
1705 return LDB_ERR_OPERATIONS_ERROR;
1708 for (i=0; i<el->num_values; i++) {
1709 struct ldb_val *v = &el->values[i];
1712 struct parsed_dn *p;
1716 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1717 if (p->dsdb_dn == NULL) {
1718 return LDB_ERR_INVALID_DN_SYNTAX;
1721 dn = p->dsdb_dn->dn;
1723 p->guid = talloc(*pdn, struct GUID);
1724 if (p->guid == NULL) {
1725 ldb_module_oom(module);
1726 return LDB_ERR_OPERATIONS_ERROR;
1729 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1730 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1731 /* we got a DN without a GUID - go find the GUID */
1732 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1733 if (ret != LDB_SUCCESS) {
1734 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1735 ldb_dn_get_linearized(dn));
1736 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1737 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1738 ldb_attr_cmp(el->name, "member") == 0) {
1739 return LDB_ERR_UNWILLING_TO_PERFORM;
1743 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1744 if (ret != LDB_SUCCESS) {
1747 } else if (!NT_STATUS_IS_OK(status)) {
1748 return LDB_ERR_OPERATIONS_ERROR;
1751 /* keep a pointer to the original ldb_val */
1755 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1761 build a new extended DN, including all meta data fields
1763 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1764 RMD_ADDTIME = originating_add_time
1765 RMD_INVOCID = originating_invocation_id
1766 RMD_CHANGETIME = originating_change_time
1767 RMD_ORIGINATING_USN = originating_usn
1768 RMD_LOCAL_USN = local_usn
1769 RMD_VERSION = version
1771 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1772 const struct GUID *invocation_id, uint64_t seq_num,
1773 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1775 struct ldb_dn *dn = dsdb_dn->dn;
1776 const char *tstring, *usn_string, *flags_string;
1777 struct ldb_val tval;
1779 struct ldb_val usnv, local_usnv;
1780 struct ldb_val vers, flagsv;
1783 const char *dnstring;
1785 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1787 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1789 return LDB_ERR_OPERATIONS_ERROR;
1791 tval = data_blob_string_const(tstring);
1793 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1795 return LDB_ERR_OPERATIONS_ERROR;
1797 usnv = data_blob_string_const(usn_string);
1799 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1801 return LDB_ERR_OPERATIONS_ERROR;
1803 local_usnv = data_blob_string_const(usn_string);
1805 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1807 return LDB_ERR_OPERATIONS_ERROR;
1809 vers = data_blob_string_const(vstring);
1811 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1812 if (!NT_STATUS_IS_OK(status)) {
1813 return LDB_ERR_OPERATIONS_ERROR;
1816 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1817 if (!flags_string) {
1818 return LDB_ERR_OPERATIONS_ERROR;
1820 flagsv = data_blob_string_const(flags_string);
1822 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1823 if (ret != LDB_SUCCESS) return ret;
1824 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1825 if (ret != LDB_SUCCESS) return ret;
1826 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1827 if (ret != LDB_SUCCESS) return ret;
1828 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1829 if (ret != LDB_SUCCESS) return ret;
1830 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1831 if (ret != LDB_SUCCESS) return ret;
1832 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1833 if (ret != LDB_SUCCESS) return ret;
1834 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1835 if (ret != LDB_SUCCESS) return ret;
1837 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1838 if (dnstring == NULL) {
1839 return LDB_ERR_OPERATIONS_ERROR;
1841 *v = data_blob_string_const(dnstring);
1846 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1847 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1848 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1849 uint32_t version, bool deleted);
1852 check if any links need upgrading from w2k format
1854 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1856 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1859 for (i=0; i<count; i++) {
1864 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1865 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1869 /* it's an old one that needs upgrading */
1870 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1872 if (ret != LDB_SUCCESS) {
1880 update an extended DN, including all meta data fields
1882 see replmd_build_la_val for value names
1884 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1885 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1886 uint64_t usn, uint64_t local_usn, NTTIME nttime,
1887 uint32_t version, bool deleted)
1889 struct ldb_dn *dn = dsdb_dn->dn;
1890 const char *tstring, *usn_string, *flags_string;
1891 struct ldb_val tval;
1893 struct ldb_val usnv, local_usnv;
1894 struct ldb_val vers, flagsv;
1895 const struct ldb_val *old_addtime;
1896 uint32_t old_version;
1899 const char *dnstring;
1901 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1903 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1905 return LDB_ERR_OPERATIONS_ERROR;
1907 tval = data_blob_string_const(tstring);
1909 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
1911 return LDB_ERR_OPERATIONS_ERROR;
1913 usnv = data_blob_string_const(usn_string);
1915 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1917 return LDB_ERR_OPERATIONS_ERROR;
1919 local_usnv = data_blob_string_const(usn_string);
1921 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1922 if (!NT_STATUS_IS_OK(status)) {
1923 return LDB_ERR_OPERATIONS_ERROR;
1926 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1927 if (!flags_string) {
1928 return LDB_ERR_OPERATIONS_ERROR;
1930 flagsv = data_blob_string_const(flags_string);
1932 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1933 if (ret != LDB_SUCCESS) return ret;
1935 /* get the ADDTIME from the original */
1936 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1937 if (old_addtime == NULL) {
1938 old_addtime = &tval;
1940 if (dsdb_dn != old_dsdb_dn ||
1941 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1942 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1943 if (ret != LDB_SUCCESS) return ret;
1946 /* use our invocation id */
1947 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1948 if (ret != LDB_SUCCESS) return ret;
1950 /* changetime is the current time */
1951 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1952 if (ret != LDB_SUCCESS) return ret;
1954 /* update the USN */
1955 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1956 if (ret != LDB_SUCCESS) return ret;
1958 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1959 if (ret != LDB_SUCCESS) return ret;
1961 /* increase the version by 1 */
1962 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1963 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1964 version = old_version+1;
1966 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1967 vers = data_blob_string_const(vstring);
1968 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1969 if (ret != LDB_SUCCESS) return ret;
1971 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1972 if (dnstring == NULL) {
1973 return LDB_ERR_OPERATIONS_ERROR;
1975 *v = data_blob_string_const(dnstring);
1981 handle adding a linked attribute
1983 static int replmd_modify_la_add(struct ldb_module *module,
1984 const struct dsdb_schema *schema,
1985 struct ldb_message *msg,
1986 struct ldb_message_element *el,
1987 struct ldb_message_element *old_el,
1988 const struct dsdb_attribute *schema_attr,
1991 struct GUID *msg_guid,
1992 struct ldb_request *parent)
1995 struct parsed_dn *dns, *old_dns;
1996 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1998 struct ldb_val *new_values = NULL;
1999 unsigned int num_new_values = 0;
2000 unsigned old_num_values = old_el?old_el->num_values:0;
2001 const struct GUID *invocation_id;
2002 struct ldb_context *ldb = ldb_module_get_ctx(module);
2005 unix_to_nt_time(&now, t);
2007 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2008 if (ret != LDB_SUCCESS) {
2009 talloc_free(tmp_ctx);
2013 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2014 if (ret != LDB_SUCCESS) {
2015 talloc_free(tmp_ctx);
2019 invocation_id = samdb_ntds_invocation_id(ldb);
2020 if (!invocation_id) {
2021 talloc_free(tmp_ctx);
2022 return LDB_ERR_OPERATIONS_ERROR;
2025 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2026 if (ret != LDB_SUCCESS) {
2027 talloc_free(tmp_ctx);
2031 /* for each new value, see if it exists already with the same GUID */
2032 for (i=0; i<el->num_values; i++) {
2033 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
2035 /* this is a new linked attribute value */
2036 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
2037 if (new_values == NULL) {
2038 ldb_module_oom(module);
2039 talloc_free(tmp_ctx);
2040 return LDB_ERR_OPERATIONS_ERROR;
2042 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2043 invocation_id, seq_num, seq_num, now, 0, false);
2044 if (ret != LDB_SUCCESS) {
2045 talloc_free(tmp_ctx);
2050 /* this is only allowed if the GUID was
2051 previously deleted. */
2052 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2054 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2055 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2056 el->name, GUID_string(tmp_ctx, p->guid));
2057 talloc_free(tmp_ctx);
2058 /* error codes for 'member' need to be
2060 if (ldb_attr_cmp(el->name, "member") == 0) {
2061 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2063 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2066 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2067 invocation_id, seq_num, seq_num, now, 0, false);
2068 if (ret != LDB_SUCCESS) {
2069 talloc_free(tmp_ctx);
2074 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
2075 if (ret != LDB_SUCCESS) {
2076 talloc_free(tmp_ctx);
2081 /* add the new ones on to the end of the old values, constructing a new el->values */
2082 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2084 old_num_values+num_new_values);
2085 if (el->values == NULL) {
2086 ldb_module_oom(module);
2087 return LDB_ERR_OPERATIONS_ERROR;
2090 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2091 el->num_values = old_num_values + num_new_values;
2093 talloc_steal(msg->elements, el->values);
2094 talloc_steal(el->values, new_values);
2096 talloc_free(tmp_ctx);
2098 /* we now tell the backend to replace all existing values
2099 with the one we have constructed */
2100 el->flags = LDB_FLAG_MOD_REPLACE;
2107 handle deleting all active linked attributes
2109 static int replmd_modify_la_delete(struct ldb_module *module,
2110 const struct dsdb_schema *schema,
2111 struct ldb_message *msg,
2112 struct ldb_message_element *el,
2113 struct ldb_message_element *old_el,
2114 const struct dsdb_attribute *schema_attr,
2117 struct GUID *msg_guid,
2118 struct ldb_request *parent)
2121 struct parsed_dn *dns, *old_dns;
2122 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2124 const struct GUID *invocation_id;
2125 struct ldb_context *ldb = ldb_module_get_ctx(module);
2128 unix_to_nt_time(&now, t);
2130 /* check if there is nothing to delete */
2131 if ((!old_el || old_el->num_values == 0) &&
2132 el->num_values == 0) {
2136 if (!old_el || old_el->num_values == 0) {
2137 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2140 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2141 if (ret != LDB_SUCCESS) {
2142 talloc_free(tmp_ctx);
2146 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2147 if (ret != LDB_SUCCESS) {
2148 talloc_free(tmp_ctx);
2152 invocation_id = samdb_ntds_invocation_id(ldb);
2153 if (!invocation_id) {
2154 return LDB_ERR_OPERATIONS_ERROR;
2157 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2158 if (ret != LDB_SUCCESS) {
2159 talloc_free(tmp_ctx);
2165 /* see if we are being asked to delete any links that
2166 don't exist or are already deleted */
2167 for (i=0; i<el->num_values; i++) {
2168 struct parsed_dn *p = &dns[i];
2169 struct parsed_dn *p2;
2172 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
2174 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2175 el->name, GUID_string(tmp_ctx, p->guid));
2176 if (ldb_attr_cmp(el->name, "member") == 0) {
2177 return LDB_ERR_UNWILLING_TO_PERFORM;
2179 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2182 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2183 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2184 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2185 el->name, GUID_string(tmp_ctx, p->guid));
2186 if (ldb_attr_cmp(el->name, "member") == 0) {
2187 return LDB_ERR_UNWILLING_TO_PERFORM;
2189 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2194 /* for each new value, see if it exists already with the same GUID
2195 if it is not already deleted and matches the delete list then delete it
2197 for (i=0; i<old_el->num_values; i++) {
2198 struct parsed_dn *p = &old_dns[i];
2201 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
2205 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2206 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2208 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2209 invocation_id, seq_num, seq_num, now, 0, true);
2210 if (ret != LDB_SUCCESS) {
2211 talloc_free(tmp_ctx);
2215 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2216 if (ret != LDB_SUCCESS) {
2217 talloc_free(tmp_ctx);
2222 el->values = talloc_steal(msg->elements, old_el->values);
2223 el->num_values = old_el->num_values;
2225 talloc_free(tmp_ctx);
2227 /* we now tell the backend to replace all existing values
2228 with the one we have constructed */
2229 el->flags = LDB_FLAG_MOD_REPLACE;
2235 handle replacing a linked attribute
2237 static int replmd_modify_la_replace(struct ldb_module *module,
2238 const struct dsdb_schema *schema,
2239 struct ldb_message *msg,
2240 struct ldb_message_element *el,
2241 struct ldb_message_element *old_el,
2242 const struct dsdb_attribute *schema_attr,
2245 struct GUID *msg_guid,
2246 struct ldb_request *parent)
2249 struct parsed_dn *dns, *old_dns;
2250 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2252 const struct GUID *invocation_id;
2253 struct ldb_context *ldb = ldb_module_get_ctx(module);
2254 struct ldb_val *new_values = NULL;
2255 unsigned int num_new_values = 0;
2256 unsigned int old_num_values = old_el?old_el->num_values:0;
2259 unix_to_nt_time(&now, t);
2261 /* check if there is nothing to replace */
2262 if ((!old_el || old_el->num_values == 0) &&
2263 el->num_values == 0) {
2267 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2268 if (ret != LDB_SUCCESS) {
2269 talloc_free(tmp_ctx);
2273 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2274 if (ret != LDB_SUCCESS) {
2275 talloc_free(tmp_ctx);
2279 invocation_id = samdb_ntds_invocation_id(ldb);
2280 if (!invocation_id) {
2281 return LDB_ERR_OPERATIONS_ERROR;
2284 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2285 if (ret != LDB_SUCCESS) {
2286 talloc_free(tmp_ctx);
2290 /* mark all the old ones as deleted */
2291 for (i=0; i<old_num_values; i++) {
2292 struct parsed_dn *old_p = &old_dns[i];
2293 struct parsed_dn *p;
2294 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2296 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2298 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2299 if (ret != LDB_SUCCESS) {
2300 talloc_free(tmp_ctx);
2304 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2306 /* we don't delete it if we are re-adding it */
2310 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2311 invocation_id, seq_num, seq_num, now, 0, true);
2312 if (ret != LDB_SUCCESS) {
2313 talloc_free(tmp_ctx);
2318 /* for each new value, either update its meta-data, or add it
2321 for (i=0; i<el->num_values; i++) {
2322 struct parsed_dn *p = &dns[i], *old_p;
2325 (old_p = parsed_dn_find(old_dns,
2326 old_num_values, p->guid, NULL)) != NULL) {
2327 /* update in place */
2328 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2329 old_p->dsdb_dn, invocation_id,
2330 seq_num, seq_num, now, 0, false);
2331 if (ret != LDB_SUCCESS) {
2332 talloc_free(tmp_ctx);
2337 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2339 if (new_values == NULL) {
2340 ldb_module_oom(module);
2341 talloc_free(tmp_ctx);
2342 return LDB_ERR_OPERATIONS_ERROR;
2344 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2345 invocation_id, seq_num, seq_num, now, 0, false);
2346 if (ret != LDB_SUCCESS) {
2347 talloc_free(tmp_ctx);
2353 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2354 if (ret != LDB_SUCCESS) {
2355 talloc_free(tmp_ctx);
2360 /* add the new values to the end of old_el */
2361 if (num_new_values != 0) {
2362 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2363 struct ldb_val, old_num_values+num_new_values);
2364 if (el->values == NULL) {
2365 ldb_module_oom(module);
2366 return LDB_ERR_OPERATIONS_ERROR;
2368 memcpy(&el->values[old_num_values], &new_values[0],
2369 sizeof(struct ldb_val)*num_new_values);
2370 el->num_values = old_num_values + num_new_values;
2371 talloc_steal(msg->elements, new_values);
2373 el->values = old_el->values;
2374 el->num_values = old_el->num_values;
2375 talloc_steal(msg->elements, el->values);
2378 talloc_free(tmp_ctx);
2380 /* we now tell the backend to replace all existing values
2381 with the one we have constructed */
2382 el->flags = LDB_FLAG_MOD_REPLACE;
2389 handle linked attributes in modify requests
2391 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2392 struct ldb_message *msg,
2393 uint64_t seq_num, time_t t,
2394 struct ldb_request *parent)
2396 struct ldb_result *res;
2399 struct ldb_context *ldb = ldb_module_get_ctx(module);
2400 struct ldb_message *old_msg;
2402 const struct dsdb_schema *schema;
2403 struct GUID old_guid;
2406 /* there the replmd_update_rpmd code has already
2407 * checked and saw that there are no linked
2412 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2413 /* don't do anything special for linked attributes */
2417 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2418 DSDB_FLAG_NEXT_MODULE |
2419 DSDB_SEARCH_SHOW_RECYCLED |
2420 DSDB_SEARCH_REVEAL_INTERNALS |
2421 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2423 if (ret != LDB_SUCCESS) {
2426 schema = dsdb_get_schema(ldb, res);
2428 return LDB_ERR_OPERATIONS_ERROR;
2431 old_msg = res->msgs[0];
2433 old_guid = samdb_result_guid(old_msg, "objectGUID");
2435 for (i=0; i<msg->num_elements; i++) {
2436 struct ldb_message_element *el = &msg->elements[i];
2437 struct ldb_message_element *old_el, *new_el;
2438 const struct dsdb_attribute *schema_attr
2439 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2441 ldb_asprintf_errstring(ldb,
2442 "%s: attribute %s is not a valid attribute in schema",
2443 __FUNCTION__, el->name);
2444 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2446 if (schema_attr->linkID == 0) {
2449 if ((schema_attr->linkID & 1) == 1) {
2450 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2453 /* Odd is for the target. Illegal to modify */
2454 ldb_asprintf_errstring(ldb,
2455 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2456 return LDB_ERR_UNWILLING_TO_PERFORM;
2458 old_el = ldb_msg_find_element(old_msg, el->name);
2459 switch (el->flags & LDB_FLAG_MOD_MASK) {
2460 case LDB_FLAG_MOD_REPLACE:
2461 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2463 case LDB_FLAG_MOD_DELETE:
2464 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2466 case LDB_FLAG_MOD_ADD:
2467 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2470 ldb_asprintf_errstring(ldb,
2471 "invalid flags 0x%x for %s linked attribute",
2472 el->flags, el->name);
2473 return LDB_ERR_UNWILLING_TO_PERFORM;
2475 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2476 ldb_asprintf_errstring(ldb,
2477 "Attribute %s is single valued but more than one value has been supplied",
2479 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2481 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2486 if (ret != LDB_SUCCESS) {
2490 ldb_msg_remove_attr(old_msg, el->name);
2492 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2493 new_el->num_values = el->num_values;
2494 new_el->values = talloc_steal(msg->elements, el->values);
2496 /* TODO: this relises a bit too heavily on the exact
2497 behaviour of ldb_msg_find_element and
2498 ldb_msg_remove_element */
2499 old_el = ldb_msg_find_element(msg, el->name);
2501 ldb_msg_remove_element(msg, old_el);
2512 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2514 struct samldb_msds_intid_persistant *msds_intid_struct;
2515 struct ldb_context *ldb;
2516 struct replmd_replicated_request *ac;
2517 struct ldb_request *down_req;
2518 struct ldb_message *msg;
2519 time_t t = time(NULL);
2521 bool is_urgent = false, rodc = false;
2522 bool is_schema_nc = false;
2523 unsigned int functional_level;
2524 const struct ldb_message_element *guid_el = NULL;
2525 struct ldb_control *sd_propagation_control;
2526 struct replmd_private *replmd_private =
2527 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2529 /* do not manipulate our control entries */
2530 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2531 return ldb_next_request(module, req);
2534 sd_propagation_control = ldb_request_get_control(req,
2535 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2536 if (sd_propagation_control != NULL) {
2537 if (req->op.mod.message->num_elements != 1) {
2538 return ldb_module_operr(module);
2540 ret = strcmp(req->op.mod.message->elements[0].name,
2541 "nTSecurityDescriptor");
2543 return ldb_module_operr(module);
2546 return ldb_next_request(module, req);
2549 ldb = ldb_module_get_ctx(module);
2551 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2553 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2554 if (guid_el != NULL) {
2555 ldb_set_errstring(ldb,
2556 "replmd_modify: it's not allowed to change the objectGUID!");
2557 return LDB_ERR_CONSTRAINT_VIOLATION;
2560 ac = replmd_ctx_init(module, req);
2562 return ldb_module_oom(module);
2565 functional_level = dsdb_functional_level(ldb);
2567 /* we have to copy the message as the caller might have it as a const */
2568 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2572 return LDB_ERR_OPERATIONS_ERROR;
2575 ldb_msg_remove_attr(msg, "whenChanged");
2576 ldb_msg_remove_attr(msg, "uSNChanged");
2578 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2580 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2581 msg, &ac->seq_num, t, is_schema_nc,
2583 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2584 struct loadparm_context *lp_ctx;
2587 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2588 struct loadparm_context);
2590 referral = talloc_asprintf(req,
2592 lpcfg_dnsdomain(lp_ctx),
2593 ldb_dn_get_linearized(msg->dn));
2594 ret = ldb_module_send_referral(req, referral);
2599 if (ret != LDB_SUCCESS) {
2604 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2605 if (ret != LDB_SUCCESS) {
2611 * - replace the old object with the newly constructed one
2614 ac->is_urgent = is_urgent;
2616 ret = ldb_build_mod_req(&down_req, ldb, ac,
2619 ac, replmd_op_callback,
2621 LDB_REQ_SET_LOCATION(down_req);
2622 if (ret != LDB_SUCCESS) {
2627 /* current partition control is needed by "replmd_op_callback" */
2628 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2629 ret = ldb_request_add_control(down_req,
2630 DSDB_CONTROL_CURRENT_PARTITION_OID,
2632 if (ret != LDB_SUCCESS) {
2638 /* If we are in functional level 2000, then
2639 * replmd_modify_handle_linked_attribs will have done
2641 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2642 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2643 if (ret != LDB_SUCCESS) {
2649 talloc_steal(down_req, msg);
2651 /* we only change whenChanged and uSNChanged if the seq_num
2653 if (ac->seq_num != 0) {
2654 ret = add_time_element(msg, "whenChanged", t);
2655 if (ret != LDB_SUCCESS) {
2661 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2662 if (ret != LDB_SUCCESS) {
2669 if (!ldb_dn_compare_base(replmd_private->schema_dn, msg->dn)) {
2670 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2671 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2672 if (msds_intid_struct) {
2673 msds_intid_struct->usn = ac->seq_num;
2677 /* go on with the call chain */
2678 return ldb_next_request(module, down_req);
2681 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2684 handle a rename request
2686 On a rename we need to do an extra ldb_modify which sets the
2687 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2689 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2691 struct ldb_context *ldb;
2692 struct replmd_replicated_request *ac;
2694 struct ldb_request *down_req;
2696 /* do not manipulate our control entries */
2697 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2698 return ldb_next_request(module, req);
2701 ldb = ldb_module_get_ctx(module);
2703 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2705 ac = replmd_ctx_init(module, req);
2707 return ldb_module_oom(module);
2710 ret = ldb_build_rename_req(&down_req, ldb, ac,
2711 ac->req->op.rename.olddn,
2712 ac->req->op.rename.newdn,
2714 ac, replmd_rename_callback,
2716 LDB_REQ_SET_LOCATION(down_req);
2717 if (ret != LDB_SUCCESS) {
2722 /* go on with the call chain */
2723 return ldb_next_request(module, down_req);
2726 /* After the rename is compleated, update the whenchanged etc */
2727 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2729 struct ldb_context *ldb;
2730 struct ldb_request *down_req;
2731 struct ldb_message *msg;
2732 const struct dsdb_attribute *rdn_attr;
2733 const char *rdn_name;
2734 const struct ldb_val *rdn_val;
2735 const char *attrs[5] = { NULL, };
2736 time_t t = time(NULL);
2738 bool is_urgent = false, rodc = false;
2740 struct replmd_replicated_request *ac =
2741 talloc_get_type(req->context, struct replmd_replicated_request);
2742 struct replmd_private *replmd_private =
2743 talloc_get_type(ldb_module_get_private(ac->module),
2744 struct replmd_private);
2746 ldb = ldb_module_get_ctx(ac->module);
2748 if (ares->error != LDB_SUCCESS) {
2749 return ldb_module_done(ac->req, ares->controls,
2750 ares->response, ares->error);
2753 if (ares->type != LDB_REPLY_DONE) {
2754 ldb_set_errstring(ldb,
2755 "invalid ldb_reply_type in callback");
2757 return ldb_module_done(ac->req, NULL, NULL,
2758 LDB_ERR_OPERATIONS_ERROR);
2762 * - replace the old object with the newly constructed one
2765 msg = ldb_msg_new(ac);
2768 return LDB_ERR_OPERATIONS_ERROR;
2771 msg->dn = ac->req->op.rename.newdn;
2773 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2775 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2776 if (rdn_name == NULL) {
2778 return ldb_module_done(ac->req, NULL, NULL,
2782 /* normalize the rdn attribute name */
2783 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2784 if (rdn_attr == NULL) {
2786 return ldb_module_done(ac->req, NULL, NULL,
2789 rdn_name = rdn_attr->lDAPDisplayName;
2791 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2792 if (rdn_val == NULL) {
2794 return ldb_module_done(ac->req, NULL, NULL,
2798 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2800 return ldb_module_done(ac->req, NULL, NULL,
2803 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2805 return ldb_module_done(ac->req, NULL, NULL,
2808 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2810 return ldb_module_done(ac->req, NULL, NULL,
2813 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2815 return ldb_module_done(ac->req, NULL, NULL,
2820 * here we let replmd_update_rpmd() only search for
2821 * the existing "replPropertyMetaData" and rdn_name attributes.
2823 * We do not want the existing "name" attribute as
2824 * the "name" attribute needs to get the version
2825 * updated on rename even if the rdn value hasn't changed.
2827 * This is the diff of the meta data, for a moved user
2828 * on a w2k8r2 server:
2831 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2832 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2833 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2834 * version : 0x00000001 (1)
2835 * reserved : 0x00000000 (0)
2836 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2837 * local_usn : 0x00000000000037a5 (14245)
2838 * array: struct replPropertyMetaData1
2839 * attid : DRSUAPI_ATTID_name (0x90001)
2840 * - version : 0x00000001 (1)
2841 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2842 * + version : 0x00000002 (2)
2843 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2844 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2845 * - originating_usn : 0x00000000000037a5 (14245)
2846 * - local_usn : 0x00000000000037a5 (14245)
2847 * + originating_usn : 0x0000000000003834 (14388)
2848 * + local_usn : 0x0000000000003834 (14388)
2849 * array: struct replPropertyMetaData1
2850 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2851 * version : 0x00000004 (4)
2853 attrs[0] = "replPropertyMetaData";
2854 attrs[1] = "objectClass";
2855 attrs[2] = "instanceType";
2856 attrs[3] = rdn_name;
2859 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2860 msg, &ac->seq_num, t,
2861 is_schema_nc, &is_urgent, &rodc);
2862 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2863 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2864 struct loadparm_context *lp_ctx;
2867 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2868 struct loadparm_context);
2870 referral = talloc_asprintf(req,
2872 lpcfg_dnsdomain(lp_ctx),
2873 ldb_dn_get_linearized(olddn));
2874 ret = ldb_module_send_referral(req, referral);
2876 return ldb_module_done(req, NULL, NULL, ret);
2879 if (ret != LDB_SUCCESS) {
2881 return ldb_module_done(ac->req, NULL, NULL, ret);
2884 if (ac->seq_num == 0) {
2886 return ldb_module_done(ac->req, NULL, NULL,
2888 "internal error seq_num == 0"));
2890 ac->is_urgent = is_urgent;
2892 ret = ldb_build_mod_req(&down_req, ldb, ac,
2895 ac, replmd_op_callback,
2897 LDB_REQ_SET_LOCATION(down_req);
2898 if (ret != LDB_SUCCESS) {
2903 /* current partition control is needed by "replmd_op_callback" */
2904 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2905 ret = ldb_request_add_control(down_req,
2906 DSDB_CONTROL_CURRENT_PARTITION_OID,
2908 if (ret != LDB_SUCCESS) {
2914 talloc_steal(down_req, msg);
2916 ret = add_time_element(msg, "whenChanged", t);
2917 if (ret != LDB_SUCCESS) {
2923 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2924 if (ret != LDB_SUCCESS) {
2930 /* go on with the call chain - do the modify after the rename */
2931 return ldb_next_request(ac->module, down_req);
2935 * remove links from objects that point at this object when an object
2936 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
2937 * RemoveObj which states that link removal due to the object being
2938 * deleted is NOT an originating update - they just go away!
2941 static int replmd_delete_remove_link(struct ldb_module *module,
2942 const struct dsdb_schema *schema,
2944 struct ldb_message_element *el,
2945 const struct dsdb_attribute *sa,
2946 struct ldb_request *parent)
2949 TALLOC_CTX *tmp_ctx = talloc_new(module);
2950 struct ldb_context *ldb = ldb_module_get_ctx(module);
2952 for (i=0; i<el->num_values; i++) {
2953 struct dsdb_dn *dsdb_dn;
2957 struct ldb_message *msg;
2958 const struct dsdb_attribute *target_attr;
2959 struct ldb_message_element *el2;
2960 struct ldb_val dn_val;
2962 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2966 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2968 talloc_free(tmp_ctx);
2969 return LDB_ERR_OPERATIONS_ERROR;
2972 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2973 if (!NT_STATUS_IS_OK(status)) {
2974 talloc_free(tmp_ctx);
2975 return LDB_ERR_OPERATIONS_ERROR;
2978 /* remove the link */
2979 msg = ldb_msg_new(tmp_ctx);
2981 ldb_module_oom(module);
2982 talloc_free(tmp_ctx);
2983 return LDB_ERR_OPERATIONS_ERROR;
2987 msg->dn = dsdb_dn->dn;
2989 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2990 if (target_attr == NULL) {
2994 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2995 if (ret != LDB_SUCCESS) {
2996 ldb_module_oom(module);
2997 talloc_free(tmp_ctx);
2998 return LDB_ERR_OPERATIONS_ERROR;
3000 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
3001 el2->values = &dn_val;
3002 el2->num_values = 1;
3004 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
3005 if (ret != LDB_SUCCESS) {
3006 talloc_free(tmp_ctx);
3010 talloc_free(tmp_ctx);
3016 handle update of replication meta data for deletion of objects
3018 This also handles the mapping of delete to a rename operation
3019 to allow deletes to be replicated.
3021 It also handles the incoming deleted objects, to ensure they are
3022 fully deleted here. In that case re_delete is true, and we do not
3023 use this as a signal to change the deleted state, just reinforce it.
3026 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3028 int ret = LDB_ERR_OTHER;
3029 bool retb, disallow_move_on_delete;
3030 struct ldb_dn *old_dn, *new_dn;
3031 const char *rdn_name;
3032 const struct ldb_val *rdn_value, *new_rdn_value;
3034 struct ldb_context *ldb = ldb_module_get_ctx(module);
3035 const struct dsdb_schema *schema;
3036 struct ldb_message *msg, *old_msg;
3037 struct ldb_message_element *el;
3038 TALLOC_CTX *tmp_ctx;
3039 struct ldb_result *res, *parent_res;
3040 const char *preserved_attrs[] = {
3041 /* yes, this really is a hard coded list. See MS-ADTS
3042 section 3.1.1.5.5.1.1 */
3043 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
3044 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
3045 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
3046 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
3047 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
3048 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
3049 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
3050 "whenChanged", NULL};
3051 unsigned int i, el_count = 0;
3052 enum deletion_state deletion_state, next_deletion_state;
3054 if (ldb_dn_is_special(req->op.del.dn)) {
3055 return ldb_next_request(module, req);
3059 * We have to allow dbcheck to remove an object that
3060 * is beyond repair, and to do so totally. This could
3061 * mean we we can get a partial object from the other
3062 * DC, causing havoc, so dbcheck suggests
3063 * re-replication first. dbcheck sets both DBCHECK
3064 * and RELAX in this situation.
3066 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3067 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3068 /* really, really remove it */
3069 return ldb_next_request(module, req);
3072 tmp_ctx = talloc_new(ldb);
3075 return LDB_ERR_OPERATIONS_ERROR;
3078 schema = dsdb_get_schema(ldb, tmp_ctx);
3080 talloc_free(tmp_ctx);
3081 return LDB_ERR_OPERATIONS_ERROR;
3084 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3086 /* we need the complete msg off disk, so we can work out which
3087 attributes need to be removed */
3088 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
3089 DSDB_FLAG_NEXT_MODULE |
3090 DSDB_SEARCH_SHOW_RECYCLED |
3091 DSDB_SEARCH_REVEAL_INTERNALS |
3092 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3093 if (ret != LDB_SUCCESS) {
3094 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3095 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3096 re_delete ? "re-delete" : "delete",
3097 ldb_dn_get_linearized(old_dn),
3098 ldb_errstring(ldb_module_get_ctx(module)));
3099 talloc_free(tmp_ctx);
3102 old_msg = res->msgs[0];
3104 replmd_deletion_state(module, old_msg,
3106 &next_deletion_state);
3108 /* This supports us noticing an incoming isDeleted and acting on it */
3110 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3111 next_deletion_state = deletion_state;
3114 if (next_deletion_state == OBJECT_REMOVED) {
3116 * We have to prevent objects being deleted, even if
3117 * the administrator really wants them gone, as
3118 * without the tombstone, we can get a partial object
3119 * from the other DC, causing havoc.
3121 * The only other valid case is when the 180 day
3122 * timeout has expired, when relax is specified.
3124 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3125 /* it is already deleted - really remove it this time */
3126 talloc_free(tmp_ctx);
3127 return ldb_next_request(module, req);
3130 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3131 "This check is to prevent corruption of the replicated state.",
3132 ldb_dn_get_linearized(old_msg->dn));
3133 return LDB_ERR_UNWILLING_TO_PERFORM;
3136 rdn_name = ldb_dn_get_rdn_name(old_dn);
3137 rdn_value = ldb_dn_get_rdn_val(old_dn);
3138 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3139 talloc_free(tmp_ctx);
3140 return ldb_operr(ldb);
3143 msg = ldb_msg_new(tmp_ctx);
3145 ldb_module_oom(module);
3146 talloc_free(tmp_ctx);
3147 return LDB_ERR_OPERATIONS_ERROR;
3152 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3153 disallow_move_on_delete =
3154 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3155 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3157 /* work out where we will be renaming this object to */
3158 if (!disallow_move_on_delete) {
3159 struct ldb_dn *deleted_objects_dn;
3160 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3161 &deleted_objects_dn);
3164 * We should not move objects if we can't find the
3165 * deleted objects DN. Not moving (or otherwise
3166 * harming) the Deleted Objects DN itself is handled
3169 if (re_delete && (ret != LDB_SUCCESS)) {
3170 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3171 if (new_dn == NULL) {
3172 ldb_module_oom(module);
3173 talloc_free(tmp_ctx);
3174 return LDB_ERR_OPERATIONS_ERROR;
3176 } else if (ret != LDB_SUCCESS) {
3177 /* this is probably an attempted delete on a partition
3178 * that doesn't allow delete operations, such as the
3179 * schema partition */
3180 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3181 ldb_dn_get_linearized(old_dn));
3182 talloc_free(tmp_ctx);
3183 return LDB_ERR_UNWILLING_TO_PERFORM;
3185 new_dn = deleted_objects_dn;
3188 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3189 if (new_dn == NULL) {
3190 ldb_module_oom(module);
3191 talloc_free(tmp_ctx);
3192 return LDB_ERR_OPERATIONS_ERROR;
3196 if (deletion_state == OBJECT_NOT_DELETED) {
3197 /* get the objects GUID from the search we just did */
3198 guid = samdb_result_guid(old_msg, "objectGUID");
3200 /* Add a formatted child */
3201 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3203 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3204 GUID_string(tmp_ctx, &guid));
3206 ldb_asprintf_errstring(ldb, __location__
3207 ": Unable to add a formatted child to dn: %s",
3208 ldb_dn_get_linearized(new_dn));
3209 talloc_free(tmp_ctx);
3210 return LDB_ERR_OPERATIONS_ERROR;
3213 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3214 if (ret != LDB_SUCCESS) {
3215 ldb_asprintf_errstring(ldb, __location__
3216 ": Failed to add isDeleted string to the msg");
3217 talloc_free(tmp_ctx);
3220 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3223 * No matter what has happened with other renames etc, try again to
3224 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3227 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3228 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3230 ldb_asprintf_errstring(ldb, __location__
3231 ": Unable to add a prepare rdn of %s",
3232 ldb_dn_get_linearized(rdn));
3233 talloc_free(tmp_ctx);
3234 return LDB_ERR_OPERATIONS_ERROR;
3236 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3238 retb = ldb_dn_add_child(new_dn, rdn);
3240 ldb_asprintf_errstring(ldb, __location__
3241 ": Unable to add rdn %s to base dn: %s",
3242 ldb_dn_get_linearized(rdn),
3243 ldb_dn_get_linearized(new_dn));
3244 talloc_free(tmp_ctx);
3245 return LDB_ERR_OPERATIONS_ERROR;
3250 now we need to modify the object in the following ways:
3252 - add isDeleted=TRUE
3253 - update rDN and name, with new rDN
3254 - remove linked attributes
3255 - remove objectCategory and sAMAccountType
3256 - remove attribs not on the preserved list
3257 - preserved if in above list, or is rDN
3258 - remove all linked attribs from this object
3259 - remove all links from other objects to this object
3260 - add lastKnownParent
3261 - update replPropertyMetaData?
3263 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3266 if (deletion_state == OBJECT_NOT_DELETED) {
3267 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3268 char *parent_dn_str = NULL;
3270 /* we need the storage form of the parent GUID */
3271 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3273 DSDB_FLAG_NEXT_MODULE |
3274 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3275 DSDB_SEARCH_REVEAL_INTERNALS|
3276 DSDB_SEARCH_SHOW_RECYCLED, req);
3277 if (ret != LDB_SUCCESS) {
3278 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3279 "repmd_delete: Failed to %s %s, "
3280 "because we failed to find it's parent (%s): %s",
3281 re_delete ? "re-delete" : "delete",
3282 ldb_dn_get_linearized(old_dn),
3283 ldb_dn_get_linearized(parent_dn),
3284 ldb_errstring(ldb_module_get_ctx(module)));
3285 talloc_free(tmp_ctx);
3290 * Now we can use the DB version,
3291 * it will have the extended DN info in it
3293 parent_dn = parent_res->msgs[0]->dn;
3294 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3297 if (parent_dn_str == NULL) {
3298 talloc_free(tmp_ctx);
3299 return ldb_module_oom(module);
3302 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3304 if (ret != LDB_SUCCESS) {
3305 ldb_asprintf_errstring(ldb, __location__
3306 ": Failed to add lastKnownParent "
3307 "string when deleting %s",
3308 ldb_dn_get_linearized(old_dn));
3309 talloc_free(tmp_ctx);
3312 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3314 if (next_deletion_state == OBJECT_DELETED) {
3315 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3316 if (ret != LDB_SUCCESS) {
3317 ldb_asprintf_errstring(ldb, __location__
3318 ": Failed to add msDS-LastKnownRDN "
3319 "string when deleting %s",
3320 ldb_dn_get_linearized(old_dn));
3321 talloc_free(tmp_ctx);
3324 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3328 switch (next_deletion_state) {
3330 case OBJECT_RECYCLED:
3331 case OBJECT_TOMBSTONE:
3334 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3335 * describes what must be removed from a tombstone
3338 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3339 * describes what must be removed from a recycled
3345 * we also mark it as recycled, meaning this object can't be
3346 * recovered (we are stripping its attributes).
3347 * This is done only if we have this schema object of course ...
3348 * This behavior is identical to the one of Windows 2008R2 which
3349 * always set the isRecycled attribute, even if the recycle-bin is
3350 * not activated and what ever the forest level is.
3352 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3353 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3354 if (ret != LDB_SUCCESS) {
3355 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3356 ldb_module_oom(module);
3357 talloc_free(tmp_ctx);
3360 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3363 /* work out which of the old attributes we will be removing */
3364 for (i=0; i<old_msg->num_elements; i++) {
3365 const struct dsdb_attribute *sa;
3366 el = &old_msg->elements[i];
3367 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3369 talloc_free(tmp_ctx);
3370 return LDB_ERR_OPERATIONS_ERROR;
3372 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3373 /* don't remove the rDN */
3376 if (sa->linkID && (sa->linkID & 1)) {
3378 we have a backlink in this object
3379 that needs to be removed. We're not
3380 allowed to remove it directly
3381 however, so we instead setup a
3382 modify to delete the corresponding
3385 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3386 if (ret != LDB_SUCCESS) {
3387 const char *old_dn_str
3388 = ldb_dn_get_linearized(old_dn);
3389 ldb_asprintf_errstring(ldb,
3391 ": Failed to remove backlink of "
3392 "%s when deleting %s",
3395 talloc_free(tmp_ctx);
3396 return LDB_ERR_OPERATIONS_ERROR;
3398 /* now we continue, which means we
3399 won't remove this backlink
3405 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3408 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3412 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3413 if (ret != LDB_SUCCESS) {
3414 talloc_free(tmp_ctx);
3415 ldb_module_oom(module);
3422 case OBJECT_DELETED:
3424 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3425 * describes what must be removed from a deleted
3429 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3430 if (ret != LDB_SUCCESS) {
3431 talloc_free(tmp_ctx);
3432 ldb_module_oom(module);
3436 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3437 if (ret != LDB_SUCCESS) {
3438 talloc_free(tmp_ctx);
3439 ldb_module_oom(module);
3449 if (deletion_state == OBJECT_NOT_DELETED) {
3450 const struct dsdb_attribute *sa;
3452 /* work out what the new rdn value is, for updating the
3453 rDN and name fields */
3454 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3455 if (new_rdn_value == NULL) {
3456 talloc_free(tmp_ctx);
3457 return ldb_operr(ldb);
3460 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3462 talloc_free(tmp_ctx);
3463 return LDB_ERR_OPERATIONS_ERROR;
3466 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3468 if (ret != LDB_SUCCESS) {
3469 talloc_free(tmp_ctx);
3472 el->flags = LDB_FLAG_MOD_REPLACE;
3474 el = ldb_msg_find_element(old_msg, "name");
3476 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3477 if (ret != LDB_SUCCESS) {
3478 talloc_free(tmp_ctx);
3481 el->flags = LDB_FLAG_MOD_REPLACE;
3486 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3491 * No matter what has happned with other renames, try again to
3492 * get this to be under the deleted DN.
3494 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3495 /* now rename onto the new DN */
3496 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3497 if (ret != LDB_SUCCESS){
3498 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3499 ldb_dn_get_linearized(old_dn),
3500 ldb_dn_get_linearized(new_dn),
3501 ldb_errstring(ldb)));
3502 talloc_free(tmp_ctx);
3508 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3509 if (ret != LDB_SUCCESS) {
3510 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3511 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3512 talloc_free(tmp_ctx);
3516 talloc_free(tmp_ctx);
3518 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3521 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3523 return replmd_delete_internals(module, req, false);
3527 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3532 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3534 int ret = LDB_ERR_OTHER;
3535 /* TODO: do some error mapping */
3537 /* Let the caller know the full WERROR */
3538 ar->objs->error = status;
3544 static struct replPropertyMetaData1 *
3545 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3546 enum drsuapi_DsAttributeId attid)
3549 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3551 for (i = 0; i < rpmd_ctr->count; i++) {
3552 if (rpmd_ctr->array[i].attid == attid) {
3553 return &rpmd_ctr->array[i];
3561 return true if an update is newer than an existing entry
3562 see section 5.11 of MS-ADTS
3564 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3565 const struct GUID *update_invocation_id,
3566 uint32_t current_version,
3567 uint32_t update_version,
3568 NTTIME current_change_time,
3569 NTTIME update_change_time)
3571 if (update_version != current_version) {
3572 return update_version > current_version;
3574 if (update_change_time != current_change_time) {
3575 return update_change_time > current_change_time;
3577 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3580 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3581 struct replPropertyMetaData1 *new_m)
3583 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3584 &new_m->originating_invocation_id,
3587 cur_m->originating_change_time,
3588 new_m->originating_change_time);
3591 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
3592 struct replPropertyMetaData1 *cur_m,
3593 struct replPropertyMetaData1 *new_m)
3598 * If the new replPropertyMetaData entry for this attribute is
3599 * not provided (this happens in the case where we look for
3600 * ATTID_name, but the name was not changed), then the local
3601 * state is clearly still current, as the remote
3602 * server didn't send it due to being older the high watermark
3605 if (new_m == NULL) {
3609 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3611 * if we compare equal then do an
3612 * update. This is used when a client
3613 * asks for a FULL_SYNC, and can be
3614 * used to recover a corrupt
3617 * This call is a bit tricky, what we
3618 * are doing it turning the 'is_newer'
3619 * call into a 'not is older' by
3620 * swapping cur_m and new_m, and negating the
3623 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
3626 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
3636 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3638 const struct ldb_val *rdn_val;
3639 const char *rdn_name;
3640 struct ldb_dn *new_dn;
3642 rdn_val = ldb_dn_get_rdn_val(dn);
3643 rdn_name = ldb_dn_get_rdn_name(dn);
3644 if (!rdn_val || !rdn_name) {
3648 new_dn = ldb_dn_copy(mem_ctx, dn);
3653 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3657 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3659 ldb_dn_escape_value(new_dn, *rdn_val),
3660 GUID_string(new_dn, guid))) {
3669 perform a modify operation which sets the rDN and name attributes to
3670 their current values. This has the effect of changing these
3671 attributes to have been last updated by the current DC. This is
3672 needed to ensure that renames performed as part of conflict
3673 resolution are propogated to other DCs
3675 static int replmd_name_modify(struct replmd_replicated_request *ar,
3676 struct ldb_request *req, struct ldb_dn *dn)
3678 struct ldb_message *msg;
3679 const char *rdn_name;
3680 const struct ldb_val *rdn_val;
3681 const struct dsdb_attribute *rdn_attr;
3684 msg = ldb_msg_new(req);
3690 rdn_name = ldb_dn_get_rdn_name(dn);
3691 if (rdn_name == NULL) {
3695 /* normalize the rdn attribute name */
3696 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3697 if (rdn_attr == NULL) {
3700 rdn_name = rdn_attr->lDAPDisplayName;
3702 rdn_val = ldb_dn_get_rdn_val(dn);
3703 if (rdn_val == NULL) {
3707 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3710 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3713 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3716 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3720 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3721 if (ret != LDB_SUCCESS) {
3722 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3723 ldb_dn_get_linearized(dn),
3724 ldb_errstring(ldb_module_get_ctx(ar->module))));
3734 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3735 ldb_dn_get_linearized(dn)));
3736 return LDB_ERR_OPERATIONS_ERROR;
3741 callback for conflict DN handling where we have renamed the incoming
3742 record. After renaming it, we need to ensure the change of name and
3743 rDN for the incoming record is seen as an originating update by this DC.
3745 This also handles updating lastKnownParent for entries sent to lostAndFound
3747 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3749 struct replmd_replicated_request *ar =
3750 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3751 struct ldb_dn *conflict_dn = NULL;
3754 if (ares->error != LDB_SUCCESS) {
3755 /* call the normal callback for everything except success */
3756 return replmd_op_callback(req, ares);
3759 switch (req->operation) {
3761 conflict_dn = req->op.add.message->dn;
3764 conflict_dn = req->op.mod.message->dn;
3767 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3770 /* perform a modify of the rDN and name of the record */
3771 ret = replmd_name_modify(ar, req, conflict_dn);
3772 if (ret != LDB_SUCCESS) {
3774 return replmd_op_callback(req, ares);
3777 if (ar->objs->objects[ar->index_current].last_known_parent) {
3778 struct ldb_message *msg = ldb_msg_new(req);
3780 ldb_module_oom(ar->module);
3781 return LDB_ERR_OPERATIONS_ERROR;
3784 msg->dn = req->op.add.message->dn;
3786 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3787 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3788 if (ret != LDB_SUCCESS) {
3789 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3790 ldb_module_oom(ar->module);
3793 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3795 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3796 if (ret != LDB_SUCCESS) {
3797 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3798 ldb_dn_get_linearized(msg->dn),
3799 ldb_errstring(ldb_module_get_ctx(ar->module))));
3805 return replmd_op_callback(req, ares);
3809 callback for replmd_replicated_apply_add()
3810 This copes with the creation of conflict records in the case where
3811 the DN exists, but with a different objectGUID
3813 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))
3815 struct ldb_dn *conflict_dn;
3816 struct replmd_replicated_request *ar =
3817 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3818 struct ldb_result *res;
3819 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3821 const struct ldb_val *omd_value;
3822 struct replPropertyMetaDataBlob omd, *rmd;
3823 enum ndr_err_code ndr_err;
3824 bool rename_incoming_record, rodc;
3825 struct replPropertyMetaData1 *rmd_name, *omd_name;
3826 struct ldb_message *msg;
3827 struct ldb_request *down_req = NULL;
3829 /* call the normal callback for success */
3830 if (ares->error == LDB_SUCCESS) {
3831 return callback(req, ares);
3835 * we have a conflict, and need to decide if we will keep the
3836 * new record or the old record
3839 msg = ar->objs->objects[ar->index_current].msg;
3840 conflict_dn = msg->dn;
3842 /* For failures other than conflicts, fail the whole operation here */
3843 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3844 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
3845 ldb_dn_get_linearized(conflict_dn),
3846 ldb_errstring(ldb_module_get_ctx(ar->module)));
3848 return ldb_module_done(ar->req, NULL, NULL,
3849 LDB_ERR_OPERATIONS_ERROR);
3852 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3853 if (ret != LDB_SUCCESS) {
3854 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)));
3855 return ldb_module_done(ar->req, NULL, NULL,
3856 LDB_ERR_OPERATIONS_ERROR);
3862 * We are on an RODC, or were a GC for this
3863 * partition, so we have to fail this until
3864 * someone who owns the partition sorts it
3867 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3868 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3869 " - We must fail the operation until a master for this partition resolves the conflict",
3870 ldb_dn_get_linearized(conflict_dn));
3875 * first we need the replPropertyMetaData attribute from the
3876 * local, conflicting record
3878 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3880 DSDB_FLAG_NEXT_MODULE |
3881 DSDB_SEARCH_SHOW_DELETED |
3882 DSDB_SEARCH_SHOW_RECYCLED, req);
3883 if (ret != LDB_SUCCESS) {
3884 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3885 ldb_dn_get_linearized(conflict_dn)));
3889 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3890 if (omd_value == NULL) {
3891 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3892 ldb_dn_get_linearized(conflict_dn)));
3896 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3897 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3898 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3899 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3900 ldb_dn_get_linearized(conflict_dn)));
3904 rmd = ar->objs->objects[ar->index_current].meta_data;
3907 * we decide which is newer based on the RPMD on the name
3908 * attribute. See [MS-DRSR] ResolveNameConflict.
3910 * We expect omd_name to be present, as this is from a local
3911 * search, but while rmd_name should have been given to us by
3912 * the remote server, if it is missing we just prefer the
3914 * replmd_replPropertyMetaData1_new_should_be_taken()
3916 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3917 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3919 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
3920 ldb_dn_get_linearized(conflict_dn)));
3925 * Should we preserve the current record, and so rename the
3926 * incoming record to be a conflict?
3928 rename_incoming_record
3929 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
3930 omd_name, rmd_name);
3932 if (rename_incoming_record) {
3934 struct ldb_dn *new_dn;
3936 guid = samdb_result_guid(msg, "objectGUID");
3937 if (GUID_all_zero(&guid)) {
3938 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3939 ldb_dn_get_linearized(conflict_dn)));
3942 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3943 if (new_dn == NULL) {
3944 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3945 ldb_dn_get_linearized(conflict_dn)));
3949 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3950 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3952 /* re-submit the request, but with the new DN */
3953 callback = replmd_op_name_modify_callback;
3956 /* we are renaming the existing record */
3958 struct ldb_dn *new_dn;
3960 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3961 if (GUID_all_zero(&guid)) {
3962 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3963 ldb_dn_get_linearized(conflict_dn)));
3967 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3968 if (new_dn == NULL) {
3969 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3970 ldb_dn_get_linearized(conflict_dn)));
3974 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
3975 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3977 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3978 DSDB_FLAG_OWN_MODULE, req);
3979 if (ret != LDB_SUCCESS) {
3980 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3981 ldb_dn_get_linearized(conflict_dn),
3982 ldb_dn_get_linearized(new_dn),
3983 ldb_errstring(ldb_module_get_ctx(ar->module))));
3988 * now we need to ensure that the rename is seen as an
3989 * originating update. We do that with a modify.
3991 ret = replmd_name_modify(ar, req, new_dn);
3992 if (ret != LDB_SUCCESS) {
3996 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
3997 ldb_dn_get_linearized(req->op.add.message->dn)));
4000 ret = ldb_build_add_req(&down_req,
4001 ldb_module_get_ctx(ar->module),
4008 if (ret != LDB_SUCCESS) {
4011 LDB_REQ_SET_LOCATION(down_req);
4013 /* current partition control needed by "repmd_op_callback" */
4014 ret = ldb_request_add_control(down_req,
4015 DSDB_CONTROL_CURRENT_PARTITION_OID,
4017 if (ret != LDB_SUCCESS) {
4018 return replmd_replicated_request_error(ar, ret);
4021 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4022 /* this tells the partition module to make it a
4023 partial replica if creating an NC */
4024 ret = ldb_request_add_control(down_req,
4025 DSDB_CONTROL_PARTIAL_REPLICA,
4027 if (ret != LDB_SUCCESS) {
4028 return replmd_replicated_request_error(ar, ret);
4033 * Finally we re-run the add, otherwise the new record won't
4034 * exist, as we are here because of that exact failure!
4036 return ldb_next_request(ar->module, down_req);
4039 /* on failure make the caller get the error. This means
4040 * replication will stop with an error, but there is not much
4043 return ldb_module_done(ar->req, NULL, NULL,
4048 callback for replmd_replicated_apply_add()
4049 This copes with the creation of conflict records in the case where
4050 the DN exists, but with a different objectGUID
4052 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4054 struct replmd_replicated_request *ar =
4055 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4057 if (ar->objs->objects[ar->index_current].last_known_parent) {
4058 /* This is like a conflict DN, where we put the object in LostAndFound
4059 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4060 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4063 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4067 this is called when a new object comes in over DRS
4069 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4071 struct ldb_context *ldb;
4072 struct ldb_request *change_req;
4073 enum ndr_err_code ndr_err;
4074 struct ldb_message *msg;
4075 struct replPropertyMetaDataBlob *md;
4076 struct ldb_val md_value;
4079 bool remote_isDeleted = false;
4082 time_t t = time(NULL);
4083 const struct ldb_val *rdn_val;
4084 struct replmd_private *replmd_private =
4085 talloc_get_type(ldb_module_get_private(ar->module),
4086 struct replmd_private);
4087 unix_to_nt_time(&now, t);
4089 ldb = ldb_module_get_ctx(ar->module);
4090 msg = ar->objs->objects[ar->index_current].msg;
4091 md = ar->objs->objects[ar->index_current].meta_data;
4092 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4094 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4095 if (ret != LDB_SUCCESS) {
4096 return replmd_replicated_request_error(ar, ret);
4099 ret = dsdb_msg_add_guid(msg,
4100 &ar->objs->objects[ar->index_current].object_guid,
4102 if (ret != LDB_SUCCESS) {
4103 return replmd_replicated_request_error(ar, ret);
4106 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4107 if (ret != LDB_SUCCESS) {
4108 return replmd_replicated_request_error(ar, ret);
4111 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4112 if (ret != LDB_SUCCESS) {
4113 return replmd_replicated_request_error(ar, ret);
4116 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4117 if (ret != LDB_SUCCESS) {
4118 return replmd_replicated_request_error(ar, ret);
4121 /* remove any message elements that have zero values */
4122 for (i=0; i<msg->num_elements; i++) {
4123 struct ldb_message_element *el = &msg->elements[i];
4125 if (el->num_values == 0) {
4126 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4127 ldb_asprintf_errstring(ldb, __location__
4128 ": empty objectClass sent on %s, aborting replication\n",
4129 ldb_dn_get_linearized(msg->dn));
4130 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4133 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4135 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4136 msg->num_elements--;
4143 struct GUID_txt_buf guid_txt;
4145 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4146 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4147 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4152 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4153 "isDeleted", false);
4156 * the meta data array is already sorted by the caller, except
4157 * for the RDN, which needs to be added.
4161 rdn_val = ldb_dn_get_rdn_val(msg->dn);
4162 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
4163 md, ar, now, is_schema_nc);
4164 if (ret != LDB_SUCCESS) {
4165 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4166 return replmd_replicated_request_error(ar, ret);
4169 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
4170 if (ret != LDB_SUCCESS) {
4171 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4172 return replmd_replicated_request_error(ar, ret);
4175 for (i=0; i < md->ctr.ctr1.count; i++) {
4176 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4178 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4179 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4180 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4181 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4182 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4184 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4185 if (ret != LDB_SUCCESS) {
4186 return replmd_replicated_request_error(ar, ret);
4189 replmd_ldb_message_sort(msg, ar->schema);
4191 if (!remote_isDeleted) {
4192 ret = dsdb_module_schedule_sd_propagation(ar->module,
4193 ar->objs->partition_dn,
4195 if (ret != LDB_SUCCESS) {
4196 return replmd_replicated_request_error(ar, ret);
4200 ar->isDeleted = remote_isDeleted;
4202 ret = ldb_build_add_req(&change_req,
4208 replmd_op_add_callback,
4210 LDB_REQ_SET_LOCATION(change_req);
4211 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4213 /* current partition control needed by "repmd_op_callback" */
4214 ret = ldb_request_add_control(change_req,
4215 DSDB_CONTROL_CURRENT_PARTITION_OID,
4217 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4219 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4220 /* this tells the partition module to make it a
4221 partial replica if creating an NC */
4222 ret = ldb_request_add_control(change_req,
4223 DSDB_CONTROL_PARTIAL_REPLICA,
4225 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4228 return ldb_next_request(ar->module, change_req);
4231 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4232 struct ldb_reply *ares)
4234 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4235 struct replmd_replicated_request);
4239 return ldb_module_done(ar->req, NULL, NULL,
4240 LDB_ERR_OPERATIONS_ERROR);
4244 * The error NO_SUCH_OBJECT is not expected, unless the search
4245 * base is the partition DN, and that case doesn't happen here
4246 * because then we wouldn't get a parent_guid_value in any
4249 if (ares->error != LDB_SUCCESS) {
4250 return ldb_module_done(ar->req, ares->controls,
4251 ares->response, ares->error);
4254 switch (ares->type) {
4255 case LDB_REPLY_ENTRY:
4257 struct ldb_message *parent_msg = ares->message;
4258 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4259 struct ldb_dn *parent_dn;
4262 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4263 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4264 /* Per MS-DRSR 4.1.10.6.10
4265 * FindBestParentObject we need to move this
4266 * new object under a deleted object to
4268 struct ldb_dn *nc_root;
4270 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4271 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4272 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4273 "No suitable NC root found for %s. "
4274 "We need to move this object because parent object %s "
4275 "is deleted, but this object is not.",
4276 ldb_dn_get_linearized(msg->dn),
4277 ldb_dn_get_linearized(parent_msg->dn));
4278 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4279 } else if (ret != LDB_SUCCESS) {
4280 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4281 "Unable to find NC root for %s: %s. "
4282 "We need to move this object because parent object %s "
4283 "is deleted, but this object is not.",
4284 ldb_dn_get_linearized(msg->dn),
4285 ldb_errstring(ldb_module_get_ctx(ar->module)),
4286 ldb_dn_get_linearized(parent_msg->dn));
4287 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4290 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4292 DS_GUID_LOSTANDFOUND_CONTAINER,
4294 if (ret != LDB_SUCCESS) {
4295 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4296 "Unable to find LostAndFound Container for %s "
4297 "in partition %s: %s. "
4298 "We need to move this object because parent object %s "
4299 "is deleted, but this object is not.",
4300 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4301 ldb_errstring(ldb_module_get_ctx(ar->module)),
4302 ldb_dn_get_linearized(parent_msg->dn));
4303 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4305 ar->objs->objects[ar->index_current].last_known_parent
4306 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4310 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4313 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4315 comp_num = ldb_dn_get_comp_num(msg->dn);
4317 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4319 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4322 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4324 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4328 case LDB_REPLY_REFERRAL:
4329 /* we ignore referrals */
4332 case LDB_REPLY_DONE:
4334 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4335 struct GUID_txt_buf str_buf;
4336 if (ar->search_msg != NULL) {
4337 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4338 "No parent with GUID %s found for object locally known as %s",
4339 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4340 ldb_dn_get_linearized(ar->search_msg->dn));
4342 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4343 "No parent with GUID %s found for object remotely known as %s",
4344 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4345 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4349 * This error code is really important, as it
4350 * is the flag back to the callers to retry
4351 * this with DRSUAPI_DRS_GET_ANC, and so get
4352 * the parent objects before the child
4355 return ldb_module_done(ar->req, NULL, NULL,
4356 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4359 if (ar->search_msg != NULL) {
4360 ret = replmd_replicated_apply_merge(ar);
4362 ret = replmd_replicated_apply_add(ar);
4364 if (ret != LDB_SUCCESS) {
4365 return ldb_module_done(ar->req, NULL, NULL, ret);
4374 * Look for the parent object, so we put the new object in the right
4375 * place This is akin to NameObject in MS-DRSR - this routine and the
4376 * callbacks find the right parent name, and correct name for this
4380 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4382 struct ldb_context *ldb;
4386 struct ldb_request *search_req;
4387 static const char *attrs[] = {"isDeleted", NULL};
4388 struct GUID_txt_buf guid_str_buf;
4390 ldb = ldb_module_get_ctx(ar->module);
4392 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4393 if (ar->search_msg != NULL) {
4394 return replmd_replicated_apply_merge(ar);
4396 return replmd_replicated_apply_add(ar);
4400 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4403 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4404 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4406 ret = ldb_build_search_req(&search_req,
4409 ar->objs->partition_dn,
4415 replmd_replicated_apply_search_for_parent_callback,
4417 LDB_REQ_SET_LOCATION(search_req);
4419 ret = dsdb_request_add_controls(search_req,
4420 DSDB_SEARCH_SHOW_RECYCLED|
4421 DSDB_SEARCH_SHOW_DELETED|
4422 DSDB_SEARCH_SHOW_EXTENDED_DN);
4423 if (ret != LDB_SUCCESS) {
4427 return ldb_next_request(ar->module, search_req);
4431 handle renames that come in over DRS replication
4433 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4434 struct ldb_message *msg,
4435 struct ldb_request *parent,
4439 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4440 struct ldb_result *res;
4441 struct ldb_dn *conflict_dn;
4442 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4443 const struct ldb_val *omd_value;
4444 struct replPropertyMetaDataBlob omd, *rmd;
4445 enum ndr_err_code ndr_err;
4446 bool rename_incoming_record, rodc;
4447 struct replPropertyMetaData1 *rmd_name, *omd_name;
4448 struct ldb_dn *new_dn;
4451 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4452 ldb_dn_get_linearized(ar->search_msg->dn),
4453 ldb_dn_get_linearized(msg->dn)));
4456 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4457 DSDB_FLAG_NEXT_MODULE, ar->req);
4458 if (ret == LDB_SUCCESS) {
4459 talloc_free(tmp_ctx);
4464 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4465 talloc_free(tmp_ctx);
4466 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4467 ldb_dn_get_linearized(ar->search_msg->dn),
4468 ldb_dn_get_linearized(msg->dn),
4469 ldb_errstring(ldb_module_get_ctx(ar->module)));
4473 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4474 if (ret != LDB_SUCCESS) {
4475 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4476 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4477 ldb_errstring(ldb_module_get_ctx(ar->module)));
4478 return LDB_ERR_OPERATIONS_ERROR;
4481 * we have a conflict, and need to decide if we will keep the
4482 * new record or the old record
4485 conflict_dn = msg->dn;
4489 * We are on an RODC, or were a GC for this
4490 * partition, so we have to fail this until
4491 * someone who owns the partition sorts it
4494 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4495 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4496 " - We must fail the operation until a master for this partition resolves the conflict",
4497 ldb_dn_get_linearized(conflict_dn));
4502 * first we need the replPropertyMetaData attribute from the
4505 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4507 DSDB_FLAG_NEXT_MODULE |
4508 DSDB_SEARCH_SHOW_DELETED |
4509 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4510 if (ret != LDB_SUCCESS) {
4511 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4512 ldb_dn_get_linearized(conflict_dn)));
4516 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4517 if (omd_value == NULL) {
4518 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4519 ldb_dn_get_linearized(conflict_dn)));
4523 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4524 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4525 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4526 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4527 ldb_dn_get_linearized(conflict_dn)));
4531 rmd = ar->objs->objects[ar->index_current].meta_data;
4534 * we decide which is newer based on the RPMD on the name
4535 * attribute. See [MS-DRSR] ResolveNameConflict.
4537 * We expect omd_name to be present, as this is from a local
4538 * search, but while rmd_name should have been given to us by
4539 * the remote server, if it is missing we just prefer the
4541 * replmd_replPropertyMetaData1_new_should_be_taken()
4543 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4544 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4546 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4547 ldb_dn_get_linearized(conflict_dn)));
4552 * Should we preserve the current record, and so rename the
4553 * incoming record to be a conflict?
4555 rename_incoming_record =
4556 !replmd_replPropertyMetaData1_new_should_be_taken(
4557 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4558 omd_name, rmd_name);
4560 if (rename_incoming_record) {
4562 new_dn = replmd_conflict_dn(msg, msg->dn,
4563 &ar->objs->objects[ar->index_current].object_guid);
4564 if (new_dn == NULL) {
4565 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4566 "Failed to form conflict DN for %s\n",
4567 ldb_dn_get_linearized(msg->dn));
4569 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4572 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4573 DSDB_FLAG_NEXT_MODULE, ar->req);
4574 if (ret != LDB_SUCCESS) {
4575 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4576 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4577 ldb_dn_get_linearized(conflict_dn),
4578 ldb_dn_get_linearized(ar->search_msg->dn),
4579 ldb_dn_get_linearized(new_dn),
4580 ldb_errstring(ldb_module_get_ctx(ar->module)));
4581 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4589 /* we are renaming the existing record */
4591 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4592 if (GUID_all_zero(&guid)) {
4593 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4594 ldb_dn_get_linearized(conflict_dn)));
4598 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4599 if (new_dn == NULL) {
4600 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4601 ldb_dn_get_linearized(conflict_dn)));
4605 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4606 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4608 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4609 DSDB_FLAG_OWN_MODULE, ar->req);
4610 if (ret != LDB_SUCCESS) {
4611 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4612 ldb_dn_get_linearized(conflict_dn),
4613 ldb_dn_get_linearized(new_dn),
4614 ldb_errstring(ldb_module_get_ctx(ar->module))));
4619 * now we need to ensure that the rename is seen as an
4620 * originating update. We do that with a modify.
4622 ret = replmd_name_modify(ar, ar->req, new_dn);
4623 if (ret != LDB_SUCCESS) {
4627 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4628 ldb_dn_get_linearized(ar->search_msg->dn),
4629 ldb_dn_get_linearized(msg->dn)));
4632 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4633 DSDB_FLAG_NEXT_MODULE, ar->req);
4634 if (ret != LDB_SUCCESS) {
4635 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4636 ldb_dn_get_linearized(ar->search_msg->dn),
4637 ldb_dn_get_linearized(msg->dn),
4638 ldb_errstring(ldb_module_get_ctx(ar->module))));
4644 * On failure make the caller get the error
4645 * This means replication will stop with an error,
4646 * but there is not much else we can do. In the
4647 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4651 talloc_free(tmp_ctx);
4656 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4658 struct ldb_context *ldb;
4659 struct ldb_request *change_req;
4660 enum ndr_err_code ndr_err;
4661 struct ldb_message *msg;
4662 struct replPropertyMetaDataBlob *rmd;
4663 struct replPropertyMetaDataBlob omd;
4664 const struct ldb_val *omd_value;
4665 struct replPropertyMetaDataBlob nmd;
4666 struct ldb_val nmd_value;
4667 struct GUID remote_parent_guid;
4670 unsigned int removed_attrs = 0;
4672 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4673 bool isDeleted = false;
4674 bool local_isDeleted = false;
4675 bool remote_isDeleted = false;
4676 bool take_remote_isDeleted = false;
4677 bool sd_updated = false;
4678 bool renamed = false;
4679 bool is_schema_nc = false;
4681 const struct ldb_val *old_rdn, *new_rdn;
4682 struct replmd_private *replmd_private =
4683 talloc_get_type(ldb_module_get_private(ar->module),
4684 struct replmd_private);
4686 time_t t = time(NULL);
4687 unix_to_nt_time(&now, t);
4689 ldb = ldb_module_get_ctx(ar->module);
4690 msg = ar->objs->objects[ar->index_current].msg;
4692 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4694 rmd = ar->objs->objects[ar->index_current].meta_data;
4698 /* find existing meta data */
4699 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4701 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4702 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4703 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4704 nt_status = ndr_map_error2ntstatus(ndr_err);
4705 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4708 if (omd.version != 1) {
4709 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4714 struct GUID_txt_buf guid_txt;
4716 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4717 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
4720 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4722 ndr_print_struct_string(s,
4723 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4724 "existing replPropertyMetaData",
4726 ndr_print_struct_string(s,
4727 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4728 "incoming replPropertyMetaData",
4733 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4734 "isDeleted", false);
4735 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4736 "isDeleted", false);
4739 * Fill in the remote_parent_guid with the GUID or an all-zero
4742 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4743 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4745 remote_parent_guid = GUID_zero();
4749 * To ensure we follow a complex rename chain around, we have
4750 * to confirm that the DN is the same (mostly to confirm the
4751 * RDN) and the parentGUID is the same.
4753 * This ensures we keep things under the correct parent, which
4754 * replmd_replicated_handle_rename() will do.
4757 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
4758 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
4762 * handle renames, even just by case that come in over
4763 * DRS. Changes in the parent DN don't hit us here,
4764 * because the search for a parent will clean up those
4767 * We also have already filtered out the case where
4768 * the peer has an older name to what we have (see
4769 * replmd_replicated_apply_search_callback())
4771 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
4774 if (ret != LDB_SUCCESS) {
4775 ldb_debug(ldb, LDB_DEBUG_FATAL,
4776 "replmd_replicated_request rename %s => %s failed - %s\n",
4777 ldb_dn_get_linearized(ar->search_msg->dn),
4778 ldb_dn_get_linearized(msg->dn),
4779 ldb_errstring(ldb));
4780 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4783 if (renamed == true) {
4785 * Set the callback to one that will fix up the name
4786 * metadata on the new conflict DN
4788 callback = replmd_op_name_modify_callback;
4793 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4794 nmd.ctr.ctr1.array = talloc_array(ar,
4795 struct replPropertyMetaData1,
4796 nmd.ctr.ctr1.count);
4797 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4799 /* first copy the old meta data */
4800 for (i=0; i < omd.ctr.ctr1.count; i++) {
4801 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4806 /* now merge in the new meta data */
4807 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4810 for (j=0; j < ni; j++) {
4813 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4817 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
4818 ar->objs->dsdb_repl_flags,
4819 &nmd.ctr.ctr1.array[j],
4820 &rmd->ctr.ctr1.array[i]);
4822 /* replace the entry */
4823 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4824 if (ar->seq_num == 0) {
4825 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4826 if (ret != LDB_SUCCESS) {
4827 return replmd_replicated_request_error(ar, ret);
4830 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4831 switch (nmd.ctr.ctr1.array[j].attid) {
4832 case DRSUAPI_ATTID_ntSecurityDescriptor:
4835 case DRSUAPI_ATTID_isDeleted:
4836 take_remote_isDeleted = true;
4845 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4846 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4847 msg->elements[i-removed_attrs].name,
4848 ldb_dn_get_linearized(msg->dn),
4849 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4852 /* we don't want to apply this change so remove the attribute */
4853 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4860 if (found) continue;
4862 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4863 if (ar->seq_num == 0) {
4864 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4865 if (ret != LDB_SUCCESS) {
4866 return replmd_replicated_request_error(ar, ret);
4869 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4870 switch (nmd.ctr.ctr1.array[ni].attid) {
4871 case DRSUAPI_ATTID_ntSecurityDescriptor:
4874 case DRSUAPI_ATTID_isDeleted:
4875 take_remote_isDeleted = true;
4884 * finally correct the size of the meta_data array
4886 nmd.ctr.ctr1.count = ni;
4888 new_rdn = ldb_dn_get_rdn_val(msg->dn);
4889 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
4892 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
4893 &nmd, ar, now, is_schema_nc);
4894 if (ret != LDB_SUCCESS) {
4895 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4896 return replmd_replicated_request_error(ar, ret);
4900 * sort the new meta data array
4902 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
4903 if (ret != LDB_SUCCESS) {
4904 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4909 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4912 * This also controls SD propagation below
4914 if (take_remote_isDeleted) {
4915 isDeleted = remote_isDeleted;
4917 isDeleted = local_isDeleted;
4920 ar->isDeleted = isDeleted;
4923 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4925 if (msg->num_elements == 0) {
4926 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4929 return replmd_replicated_apply_isDeleted(ar);
4932 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4933 ar->index_current, msg->num_elements);
4939 if (sd_updated && !isDeleted) {
4940 ret = dsdb_module_schedule_sd_propagation(ar->module,
4941 ar->objs->partition_dn,
4943 if (ret != LDB_SUCCESS) {
4944 return ldb_operr(ldb);
4948 /* create the meta data value */
4949 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4950 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4951 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4952 nt_status = ndr_map_error2ntstatus(ndr_err);
4953 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4957 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4958 * and replPopertyMetaData attributes
4960 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4961 if (ret != LDB_SUCCESS) {
4962 return replmd_replicated_request_error(ar, ret);
4964 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4965 if (ret != LDB_SUCCESS) {
4966 return replmd_replicated_request_error(ar, ret);
4968 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4969 if (ret != LDB_SUCCESS) {
4970 return replmd_replicated_request_error(ar, ret);
4973 replmd_ldb_message_sort(msg, ar->schema);
4975 /* we want to replace the old values */
4976 for (i=0; i < msg->num_elements; i++) {
4977 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4978 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4979 if (msg->elements[i].num_values == 0) {
4980 ldb_asprintf_errstring(ldb, __location__
4981 ": objectClass removed on %s, aborting replication\n",
4982 ldb_dn_get_linearized(msg->dn));
4983 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4989 struct GUID_txt_buf guid_txt;
4991 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4992 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
4993 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4998 ret = ldb_build_mod_req(&change_req,
5006 LDB_REQ_SET_LOCATION(change_req);
5007 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5009 /* current partition control needed by "repmd_op_callback" */
5010 ret = ldb_request_add_control(change_req,
5011 DSDB_CONTROL_CURRENT_PARTITION_OID,
5013 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5015 return ldb_next_request(ar->module, change_req);
5018 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5019 struct ldb_reply *ares)
5021 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5022 struct replmd_replicated_request);
5026 return ldb_module_done(ar->req, NULL, NULL,
5027 LDB_ERR_OPERATIONS_ERROR);
5029 if (ares->error != LDB_SUCCESS &&
5030 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5031 return ldb_module_done(ar->req, ares->controls,
5032 ares->response, ares->error);
5035 switch (ares->type) {
5036 case LDB_REPLY_ENTRY:
5037 ar->search_msg = talloc_steal(ar, ares->message);
5040 case LDB_REPLY_REFERRAL:
5041 /* we ignore referrals */
5044 case LDB_REPLY_DONE:
5046 struct replPropertyMetaData1 *md_remote;
5047 struct replPropertyMetaData1 *md_local;
5049 struct replPropertyMetaDataBlob omd;
5050 const struct ldb_val *omd_value;
5051 struct replPropertyMetaDataBlob *rmd;
5052 struct ldb_message *msg;
5054 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5055 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5058 * This is the ADD case, find the appropriate parent,
5059 * as this object doesn't exist locally:
5061 if (ar->search_msg == NULL) {
5062 ret = replmd_replicated_apply_search_for_parent(ar);
5063 if (ret != LDB_SUCCESS) {
5064 return ldb_module_done(ar->req, NULL, NULL, ret);
5071 * Otherwise, in the MERGE case, work out if we are
5072 * attempting a rename, and if so find the parent the
5073 * newly renamed object wants to belong under (which
5074 * may not be the parent in it's attached string DN
5076 rmd = ar->objs->objects[ar->index_current].meta_data;
5080 /* find existing meta data */
5081 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5083 enum ndr_err_code ndr_err;
5084 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5085 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5086 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5087 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5088 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5091 if (omd.version != 1) {
5092 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5096 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5098 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5099 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5100 && GUID_all_zero(&ar->local_parent_guid)) {
5101 DEBUG(0, ("Refusing to replicate new version of %s "
5102 "as local object has an all-zero parentGUID attribute, "
5103 "despite not being an NC root\n",
5104 ldb_dn_get_linearized(ar->search_msg->dn)));
5105 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5109 * now we need to check for double renames. We could have a
5110 * local rename pending which our replication partner hasn't
5111 * received yet. We choose which one wins by looking at the
5112 * attribute stamps on the two objects, the newer one wins.
5114 * This also simply applies the correct algorithms for
5115 * determining if a change was made to name at all, or
5116 * if the object has just been renamed under the same
5119 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5120 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5122 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5123 ldb_dn_get_linearized(ar->search_msg->dn)));
5124 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5128 * if there is no name attribute given then we have to assume the
5129 * object we've received has the older name
5131 if (replmd_replPropertyMetaData1_new_should_be_taken(
5132 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5133 md_local, md_remote)) {
5134 struct GUID_txt_buf p_guid_local;
5135 struct GUID_txt_buf p_guid_remote;
5136 msg = ar->objs->objects[ar->index_current].msg;
5138 /* Merge on the existing object, with rename */
5140 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5141 "as incoming object changing to %s under %s\n",
5142 ldb_dn_get_linearized(ar->search_msg->dn),
5143 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5144 ldb_dn_get_linearized(msg->dn),
5145 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5147 ret = replmd_replicated_apply_search_for_parent(ar);
5149 struct GUID_txt_buf p_guid_local;
5150 struct GUID_txt_buf p_guid_remote;
5151 msg = ar->objs->objects[ar->index_current].msg;
5154 * Merge on the existing object, force no
5155 * rename (code below just to explain why in
5159 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5160 ldb_dn_get_linearized(msg->dn)) == 0) {
5161 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5162 GUID_equal(&ar->local_parent_guid,
5163 ar->objs->objects[ar->index_current].parent_guid)
5165 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5166 "despite incoming object changing parent to %s\n",
5167 ldb_dn_get_linearized(ar->search_msg->dn),
5168 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5169 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5173 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5174 " and rejecting older rename to %s under %s\n",
5175 ldb_dn_get_linearized(ar->search_msg->dn),
5176 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5177 ldb_dn_get_linearized(msg->dn),
5178 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5182 * This assignment ensures that the strcmp()
5183 * and GUID_equal() calls in
5184 * replmd_replicated_apply_merge() avoids the
5187 ar->objs->objects[ar->index_current].parent_guid =
5188 &ar->local_parent_guid;
5190 msg->dn = ar->search_msg->dn;
5191 ret = replmd_replicated_apply_merge(ar);
5193 if (ret != LDB_SUCCESS) {
5194 return ldb_module_done(ar->req, NULL, NULL, ret);
5203 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5205 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5207 struct ldb_context *ldb;
5211 struct ldb_request *search_req;
5212 static const char *attrs[] = { "*", "parentGUID", "instanceType",
5213 "replPropertyMetaData", "nTSecurityDescriptor",
5215 struct GUID_txt_buf guid_str_buf;
5217 if (ar->index_current >= ar->objs->num_objects) {
5218 /* done with it, go to next stage */
5219 return replmd_replicated_uptodate_vector(ar);
5222 ldb = ldb_module_get_ctx(ar->module);
5223 ar->search_msg = NULL;
5224 ar->isDeleted = false;
5226 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5229 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5230 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5232 ret = ldb_build_search_req(&search_req,
5235 ar->objs->partition_dn,
5241 replmd_replicated_apply_search_callback,
5243 LDB_REQ_SET_LOCATION(search_req);
5245 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5247 if (ret != LDB_SUCCESS) {
5251 return ldb_next_request(ar->module, search_req);
5255 * This is essentially a wrapper for replmd_replicated_apply_next()
5257 * This is needed to ensure that both codepaths call this handler.
5259 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5261 struct ldb_dn *deleted_objects_dn;
5262 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5263 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5264 &deleted_objects_dn);
5265 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5267 * Do a delete here again, so that if there is
5268 * anything local that conflicts with this
5269 * object being deleted, it is removed. This
5270 * includes links. See MS-DRSR 4.1.10.6.9
5273 * If the object is already deleted, and there
5274 * is no more work required, it doesn't do
5278 /* This has been updated to point to the DN we eventually did the modify on */
5280 struct ldb_request *del_req;
5281 struct ldb_result *res;
5283 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5285 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5289 res = talloc_zero(tmp_ctx, struct ldb_result);
5291 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5292 talloc_free(tmp_ctx);
5296 /* Build a delete request, which hopefully will artually turn into nothing */
5297 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5301 ldb_modify_default_callback,
5303 LDB_REQ_SET_LOCATION(del_req);
5304 if (ret != LDB_SUCCESS) {
5305 talloc_free(tmp_ctx);
5310 * This is the guts of the call, call back
5311 * into our delete code, but setting the
5312 * re_delete flag so we delete anything that
5313 * shouldn't be there on a deleted or recycled
5316 ret = replmd_delete_internals(ar->module, del_req, true);
5317 if (ret == LDB_SUCCESS) {
5318 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5321 talloc_free(tmp_ctx);
5322 if (ret != LDB_SUCCESS) {
5327 ar->index_current++;
5328 return replmd_replicated_apply_next(ar);
5331 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5332 struct ldb_reply *ares)
5334 struct ldb_context *ldb;
5335 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5336 struct replmd_replicated_request);
5337 ldb = ldb_module_get_ctx(ar->module);
5340 return ldb_module_done(ar->req, NULL, NULL,
5341 LDB_ERR_OPERATIONS_ERROR);
5343 if (ares->error != LDB_SUCCESS) {
5344 return ldb_module_done(ar->req, ares->controls,
5345 ares->response, ares->error);
5348 if (ares->type != LDB_REPLY_DONE) {
5349 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5350 return ldb_module_done(ar->req, NULL, NULL,
5351 LDB_ERR_OPERATIONS_ERROR);
5356 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5359 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5361 struct ldb_context *ldb;
5362 struct ldb_request *change_req;
5363 enum ndr_err_code ndr_err;
5364 struct ldb_message *msg;
5365 struct replUpToDateVectorBlob ouv;
5366 const struct ldb_val *ouv_value;
5367 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5368 struct replUpToDateVectorBlob nuv;
5369 struct ldb_val nuv_value;
5370 struct ldb_message_element *nuv_el = NULL;
5371 struct ldb_message_element *orf_el = NULL;
5372 struct repsFromToBlob nrf;
5373 struct ldb_val *nrf_value = NULL;
5374 struct ldb_message_element *nrf_el = NULL;
5378 time_t t = time(NULL);
5381 uint32_t instanceType;
5383 ldb = ldb_module_get_ctx(ar->module);
5384 ruv = ar->objs->uptodateness_vector;
5390 unix_to_nt_time(&now, t);
5392 if (ar->search_msg == NULL) {
5393 /* this happens for a REPL_OBJ call where we are
5394 creating the target object by replicating it. The
5395 subdomain join code does this for the partition DN
5397 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5398 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5401 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5402 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5403 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5404 ldb_dn_get_linearized(ar->search_msg->dn)));
5405 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5409 * first create the new replUpToDateVector
5411 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5413 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5414 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5415 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5416 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5417 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5420 if (ouv.version != 2) {
5421 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5426 * the new uptodateness vector will at least
5427 * contain 1 entry, one for the source_dsa
5429 * plus optional values from our old vector and the one from the source_dsa
5431 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5432 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5433 nuv.ctr.ctr2.cursors = talloc_array(ar,
5434 struct drsuapi_DsReplicaCursor2,
5435 nuv.ctr.ctr2.count);
5436 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5438 /* first copy the old vector */
5439 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5440 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5444 /* merge in the source_dsa vector is available */
5445 for (i=0; (ruv && i < ruv->count); i++) {
5448 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5449 &ar->our_invocation_id)) {
5453 for (j=0; j < ni; j++) {
5454 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5455 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5461 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5462 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5467 if (found) continue;
5469 /* if it's not there yet, add it */
5470 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5475 * finally correct the size of the cursors array
5477 nuv.ctr.ctr2.count = ni;
5482 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5485 * create the change ldb_message
5487 msg = ldb_msg_new(ar);
5488 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5489 msg->dn = ar->search_msg->dn;
5491 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5492 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5493 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5494 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5495 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5497 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5498 if (ret != LDB_SUCCESS) {
5499 return replmd_replicated_request_error(ar, ret);
5501 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5504 * now create the new repsFrom value from the given repsFromTo1 structure
5508 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5509 nrf.ctr.ctr1.last_attempt = now;
5510 nrf.ctr.ctr1.last_success = now;
5511 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5514 * first see if we already have a repsFrom value for the current source dsa
5515 * if so we'll later replace this value
5517 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5519 for (i=0; i < orf_el->num_values; i++) {
5520 struct repsFromToBlob *trf;
5522 trf = talloc(ar, struct repsFromToBlob);
5523 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5525 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5526 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5527 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5528 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5529 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5532 if (trf->version != 1) {
5533 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5537 * we compare the source dsa objectGUID not the invocation_id
5538 * because we want only one repsFrom value per source dsa
5539 * and when the invocation_id of the source dsa has changed we don't need
5540 * the old repsFrom with the old invocation_id
5542 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5543 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5549 nrf_value = &orf_el->values[i];
5554 * copy over all old values to the new ldb_message
5556 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5557 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5562 * if we haven't found an old repsFrom value for the current source dsa
5563 * we'll add a new value
5566 struct ldb_val zero_value;
5567 ZERO_STRUCT(zero_value);
5568 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5569 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5571 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5574 /* we now fill the value which is already attached to ldb_message */
5575 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5577 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5578 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5579 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5580 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5584 * the ldb_message_element for the attribute, has all the old values and the new one
5585 * so we'll replace the whole attribute with all values
5587 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5589 if (CHECK_DEBUGLVL(4)) {
5590 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5591 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5595 /* prepare the ldb_modify() request */
5596 ret = ldb_build_mod_req(&change_req,
5602 replmd_replicated_uptodate_modify_callback,
5604 LDB_REQ_SET_LOCATION(change_req);
5605 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5607 return ldb_next_request(ar->module, change_req);
5610 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5611 struct ldb_reply *ares)
5613 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5614 struct replmd_replicated_request);
5618 return ldb_module_done(ar->req, NULL, NULL,
5619 LDB_ERR_OPERATIONS_ERROR);
5621 if (ares->error != LDB_SUCCESS &&
5622 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5623 return ldb_module_done(ar->req, ares->controls,
5624 ares->response, ares->error);
5627 switch (ares->type) {
5628 case LDB_REPLY_ENTRY:
5629 ar->search_msg = talloc_steal(ar, ares->message);
5632 case LDB_REPLY_REFERRAL:
5633 /* we ignore referrals */
5636 case LDB_REPLY_DONE:
5637 ret = replmd_replicated_uptodate_modify(ar);
5638 if (ret != LDB_SUCCESS) {
5639 return ldb_module_done(ar->req, NULL, NULL, ret);
5648 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5650 struct ldb_context *ldb;
5652 static const char *attrs[] = {
5653 "replUpToDateVector",
5658 struct ldb_request *search_req;
5660 ldb = ldb_module_get_ctx(ar->module);
5661 ar->search_msg = NULL;
5663 ret = ldb_build_search_req(&search_req,
5666 ar->objs->partition_dn,
5672 replmd_replicated_uptodate_search_callback,
5674 LDB_REQ_SET_LOCATION(search_req);
5675 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5677 return ldb_next_request(ar->module, search_req);
5682 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5684 struct ldb_context *ldb;
5685 struct dsdb_extended_replicated_objects *objs;
5686 struct replmd_replicated_request *ar;
5687 struct ldb_control **ctrls;
5690 struct replmd_private *replmd_private =
5691 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5692 struct dsdb_control_replicated_update *rep_update;
5694 ldb = ldb_module_get_ctx(module);
5696 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5698 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5700 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5701 return LDB_ERR_PROTOCOL_ERROR;
5704 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5705 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5706 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5707 return LDB_ERR_PROTOCOL_ERROR;
5710 ar = replmd_ctx_init(module, req);
5712 return LDB_ERR_OPERATIONS_ERROR;
5714 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5715 ar->apply_mode = true;
5717 ar->schema = dsdb_get_schema(ldb, ar);
5719 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5721 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5722 return LDB_ERR_CONSTRAINT_VIOLATION;
5725 ctrls = req->controls;
5727 if (req->controls) {
5728 req->controls = talloc_memdup(ar, req->controls,
5729 talloc_get_size(req->controls));
5730 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5733 /* This allows layers further down to know if a change came in
5734 over replication and what the replication flags were */
5735 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5736 if (rep_update == NULL) {
5737 return ldb_module_oom(module);
5739 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5741 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5742 if (ret != LDB_SUCCESS) {
5746 /* If this change contained linked attributes in the body
5747 * (rather than in the links section) we need to update
5748 * backlinks in linked_attributes */
5749 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5750 if (ret != LDB_SUCCESS) {
5754 ar->controls = req->controls;
5755 req->controls = ctrls;
5757 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5759 /* save away the linked attributes for the end of the
5761 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5762 struct la_entry *la_entry;
5764 if (replmd_private->la_ctx == NULL) {
5765 replmd_private->la_ctx = talloc_new(replmd_private);
5767 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5768 if (la_entry == NULL) {
5770 return LDB_ERR_OPERATIONS_ERROR;
5772 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5773 if (la_entry->la == NULL) {
5774 talloc_free(la_entry);
5776 return LDB_ERR_OPERATIONS_ERROR;
5778 *la_entry->la = ar->objs->linked_attributes[i];
5780 /* we need to steal the non-scalars so they stay
5781 around until the end of the transaction */
5782 talloc_steal(la_entry->la, la_entry->la->identifier);
5783 talloc_steal(la_entry->la, la_entry->la->value.blob);
5785 DLIST_ADD(replmd_private->la_list, la_entry);
5788 return replmd_replicated_apply_next(ar);
5792 process one linked attribute structure
5794 static int replmd_process_linked_attribute(struct ldb_module *module,
5795 struct la_entry *la_entry,
5796 struct ldb_request *parent)
5798 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5799 struct ldb_context *ldb = ldb_module_get_ctx(module);
5800 struct ldb_message *msg;
5801 struct ldb_message *target_msg = NULL;
5802 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5803 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5805 const struct dsdb_attribute *attr;
5806 struct dsdb_dn *dsdb_dn;
5807 uint64_t seq_num = 0;
5808 struct ldb_message_element *old_el;
5810 time_t t = time(NULL);
5811 struct ldb_result *res;
5812 struct ldb_result *target_res;
5813 const char *attrs[4];
5814 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5815 struct parsed_dn *pdn_list, *pdn;
5816 struct GUID guid = GUID_zero();
5818 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5819 const struct GUID *our_invocation_id;
5821 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5822 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5825 linked_attributes[0]:
5826 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5828 identifier: struct drsuapi_DsReplicaObjectIdentifier
5829 __ndr_size : 0x0000003a (58)
5830 __ndr_size_sid : 0x00000000 (0)
5831 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5833 __ndr_size_dn : 0x00000000 (0)
5835 attid : DRSUAPI_ATTID_member (0x1F)
5836 value: struct drsuapi_DsAttributeValue
5837 __ndr_size : 0x0000007e (126)
5839 blob : DATA_BLOB length=126
5840 flags : 0x00000001 (1)
5841 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5842 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5843 meta_data: struct drsuapi_DsReplicaMetaData
5844 version : 0x00000015 (21)
5845 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5846 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5847 originating_usn : 0x000000000001e19c (123292)
5849 (for cases where the link is to a normal DN)
5850 &target: struct drsuapi_DsReplicaObjectIdentifier3
5851 __ndr_size : 0x0000007e (126)
5852 __ndr_size_sid : 0x0000001c (28)
5853 guid : 7639e594-db75-4086-b0d4-67890ae46031
5854 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5855 __ndr_size_dn : 0x00000022 (34)
5856 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5859 /* find the attribute being modified */
5860 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5862 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5863 talloc_free(tmp_ctx);
5864 return LDB_ERR_OPERATIONS_ERROR;
5867 attrs[0] = attr->lDAPDisplayName;
5868 attrs[1] = "isDeleted";
5869 attrs[2] = "isRecycled";
5872 /* get the existing message from the db for the object with
5873 this GUID, returning attribute being modified. We will then
5874 use this msg as the basis for a modify call */
5875 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5876 DSDB_FLAG_NEXT_MODULE |
5877 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5878 DSDB_SEARCH_SHOW_RECYCLED |
5879 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5880 DSDB_SEARCH_REVEAL_INTERNALS,
5882 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5883 if (ret != LDB_SUCCESS) {
5884 talloc_free(tmp_ctx);
5887 if (res->count != 1) {
5888 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5889 GUID_string(tmp_ctx, &la->identifier->guid));
5890 talloc_free(tmp_ctx);
5891 return LDB_ERR_NO_SUCH_OBJECT;
5896 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5897 * ProcessLinkValue, because link updates are not applied to
5898 * recycled and tombstone objects. We don't have to delete
5899 * any existing link, that should have happened when the
5900 * object deletion was replicated or initiated.
5903 replmd_deletion_state(module, msg, &deletion_state, NULL);
5905 if (deletion_state >= OBJECT_RECYCLED) {
5906 talloc_free(tmp_ctx);
5910 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5911 if (old_el == NULL) {
5912 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5913 if (ret != LDB_SUCCESS) {
5914 ldb_module_oom(module);
5915 talloc_free(tmp_ctx);
5916 return LDB_ERR_OPERATIONS_ERROR;
5919 old_el->flags = LDB_FLAG_MOD_REPLACE;
5922 /* parse the existing links */
5923 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5924 if (ret != LDB_SUCCESS) {
5925 talloc_free(tmp_ctx);
5929 /* get our invocationId */
5930 our_invocation_id = samdb_ntds_invocation_id(ldb);
5931 if (!our_invocation_id) {
5932 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5933 talloc_free(tmp_ctx);
5934 return LDB_ERR_OPERATIONS_ERROR;
5937 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5938 if (ret != LDB_SUCCESS) {
5939 talloc_free(tmp_ctx);
5943 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5944 if (!W_ERROR_IS_OK(status)) {
5945 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5946 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5947 talloc_free(tmp_ctx);
5948 return LDB_ERR_OPERATIONS_ERROR;
5951 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5952 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5954 * This strange behaviour (allowing a NULL/missing
5955 * GUID) originally comes from:
5957 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5958 * Author: Andrew Tridgell <tridge@samba.org>
5959 * Date: Mon Dec 21 21:21:55 2009 +1100
5961 * s4-drs: cope better with NULL GUIDS from DRS
5963 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5964 * need to match by DN if possible when seeing if we should update an
5967 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5970 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5971 dsdb_dn->dn, attrs2,
5972 DSDB_FLAG_NEXT_MODULE |
5973 DSDB_SEARCH_SHOW_RECYCLED |
5974 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5975 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5977 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5978 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5980 ldb_dn_get_linearized(dsdb_dn->dn),
5981 ldb_dn_get_linearized(msg->dn));
5982 talloc_free(tmp_ctx);
5983 return LDB_ERR_OPERATIONS_ERROR;
5985 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5986 NULL, LDB_SCOPE_SUBTREE,
5988 DSDB_FLAG_NEXT_MODULE |
5989 DSDB_SEARCH_SHOW_RECYCLED |
5990 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5991 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5994 GUID_string(tmp_ctx, &guid));
5997 if (ret != LDB_SUCCESS) {
5998 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5999 GUID_string(tmp_ctx, &guid),
6000 ldb_errstring(ldb_module_get_ctx(module)));
6001 talloc_free(tmp_ctx);
6005 if (target_res->count == 0) {
6006 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6007 GUID_string(tmp_ctx, &guid),
6008 ldb_dn_get_linearized(dsdb_dn->dn)));
6009 } else if (target_res->count != 1) {
6010 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6011 GUID_string(tmp_ctx, &guid));
6012 talloc_free(tmp_ctx);
6013 return LDB_ERR_OPERATIONS_ERROR;
6015 target_msg = target_res->msgs[0];
6016 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6020 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6021 * ProcessLinkValue, because link updates are not applied to
6022 * recycled and tombstone objects. We don't have to delete
6023 * any existing link, that should have happened when the
6024 * object deletion was replicated or initiated.
6026 replmd_deletion_state(module, target_msg,
6027 &target_deletion_state, NULL);
6029 if (target_deletion_state >= OBJECT_RECYCLED) {
6030 talloc_free(tmp_ctx);
6034 /* see if this link already exists */
6035 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
6037 /* see if this update is newer than what we have already */
6038 struct GUID invocation_id = GUID_zero();
6039 uint32_t version = 0;
6040 uint32_t originating_usn = 0;
6041 NTTIME change_time = 0;
6042 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6044 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6045 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6046 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6047 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6049 if (!replmd_update_is_newer(&invocation_id,
6050 &la->meta_data.originating_invocation_id,
6052 la->meta_data.version,
6054 la->meta_data.originating_change_time)) {
6055 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6056 old_el->name, ldb_dn_get_linearized(msg->dn),
6057 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6058 talloc_free(tmp_ctx);
6062 /* get a seq_num for this change */
6063 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6064 if (ret != LDB_SUCCESS) {
6065 talloc_free(tmp_ctx);
6069 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6070 /* remove the existing backlink */
6071 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
6072 if (ret != LDB_SUCCESS) {
6073 talloc_free(tmp_ctx);
6078 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6079 &la->meta_data.originating_invocation_id,
6080 la->meta_data.originating_usn, seq_num,
6081 la->meta_data.originating_change_time,
6082 la->meta_data.version,
6084 if (ret != LDB_SUCCESS) {
6085 talloc_free(tmp_ctx);
6090 /* add the new backlink */
6091 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
6092 if (ret != LDB_SUCCESS) {
6093 talloc_free(tmp_ctx);
6098 /* get a seq_num for this change */
6099 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6100 if (ret != LDB_SUCCESS) {
6101 talloc_free(tmp_ctx);
6105 old_el->values = talloc_realloc(msg->elements, old_el->values,
6106 struct ldb_val, old_el->num_values+1);
6107 if (!old_el->values) {
6108 ldb_module_oom(module);
6109 return LDB_ERR_OPERATIONS_ERROR;
6111 old_el->num_values++;
6113 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
6114 &la->meta_data.originating_invocation_id,
6115 la->meta_data.originating_usn, seq_num,
6116 la->meta_data.originating_change_time,
6117 la->meta_data.version,
6118 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
6119 if (ret != LDB_SUCCESS) {
6120 talloc_free(tmp_ctx);
6125 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6127 if (ret != LDB_SUCCESS) {
6128 talloc_free(tmp_ctx);
6134 /* we only change whenChanged and uSNChanged if the seq_num
6136 ret = add_time_element(msg, "whenChanged", t);
6137 if (ret != LDB_SUCCESS) {
6138 talloc_free(tmp_ctx);
6143 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6144 if (ret != LDB_SUCCESS) {
6145 talloc_free(tmp_ctx);
6150 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6151 if (old_el == NULL) {
6152 talloc_free(tmp_ctx);
6153 return ldb_operr(ldb);
6156 ret = dsdb_check_single_valued_link(attr, old_el);
6157 if (ret != LDB_SUCCESS) {
6158 talloc_free(tmp_ctx);
6162 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6164 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
6165 if (ret != LDB_SUCCESS) {
6166 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6168 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6169 talloc_free(tmp_ctx);
6173 talloc_free(tmp_ctx);
6178 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6180 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6181 return replmd_extended_replicated_objects(module, req);
6184 return ldb_next_request(module, req);
6189 we hook into the transaction operations to allow us to
6190 perform the linked attribute updates at the end of the whole
6191 transaction. This allows a forward linked attribute to be created
6192 before the object is created. During a vampire, w2k8 sends us linked
6193 attributes before the objects they are part of.
6195 static int replmd_start_transaction(struct ldb_module *module)
6197 /* create our private structure for this transaction */
6198 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6199 struct replmd_private);
6200 replmd_txn_cleanup(replmd_private);
6202 /* free any leftover mod_usn records from cancelled
6204 while (replmd_private->ncs) {
6205 struct nc_entry *e = replmd_private->ncs;
6206 DLIST_REMOVE(replmd_private->ncs, e);
6210 return ldb_next_start_trans(module);
6214 on prepare commit we loop over our queued la_context structures and
6217 static int replmd_prepare_commit(struct ldb_module *module)
6219 struct replmd_private *replmd_private =
6220 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6221 struct la_entry *la, *prev;
6222 struct la_backlink *bl;
6225 /* walk the list backwards, to do the first entry first, as we
6226 * added the entries with DLIST_ADD() which puts them at the
6227 * start of the list */
6228 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6229 prev = DLIST_PREV(la);
6230 DLIST_REMOVE(replmd_private->la_list, la);
6231 ret = replmd_process_linked_attribute(module, la, NULL);
6232 if (ret != LDB_SUCCESS) {
6233 replmd_txn_cleanup(replmd_private);
6238 /* process our backlink list, creating and deleting backlinks
6240 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6241 ret = replmd_process_backlink(module, bl, NULL);
6242 if (ret != LDB_SUCCESS) {
6243 replmd_txn_cleanup(replmd_private);
6248 replmd_txn_cleanup(replmd_private);
6250 /* possibly change @REPLCHANGED */
6251 ret = replmd_notify_store(module, NULL);
6252 if (ret != LDB_SUCCESS) {
6256 return ldb_next_prepare_commit(module);
6259 static int replmd_del_transaction(struct ldb_module *module)
6261 struct replmd_private *replmd_private =
6262 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6263 replmd_txn_cleanup(replmd_private);
6265 return ldb_next_del_trans(module);
6269 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6270 .name = "repl_meta_data",
6271 .init_context = replmd_init,
6273 .modify = replmd_modify,
6274 .rename = replmd_rename,
6275 .del = replmd_delete,
6276 .extended = replmd_extended,
6277 .start_transaction = replmd_start_transaction,
6278 .prepare_commit = replmd_prepare_commit,
6279 .del_transaction = replmd_del_transaction,
6282 int ldb_repl_meta_data_module_init(const char *version)
6284 LDB_MODULE_CHECK_VERSION(version);
6285 return ldb_register_module(&ldb_repl_meta_data_module_ops);