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;
71 bool originating_updates;
75 struct la_entry *next, *prev;
76 struct drsuapi_DsReplicaLinkedAttribute *la;
79 struct replmd_replicated_request {
80 struct ldb_module *module;
81 struct ldb_request *req;
83 const struct dsdb_schema *schema;
84 struct GUID our_invocation_id;
86 /* the controls we pass down */
87 struct ldb_control **controls;
89 /* details for the mode where we apply a bunch of inbound replication meessages */
91 uint32_t index_current;
92 struct dsdb_extended_replicated_objects *objs;
94 struct ldb_message *search_msg;
95 struct GUID local_parent_guid;
103 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
104 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
106 enum urgent_situation {
107 REPL_URGENT_ON_CREATE = 1,
108 REPL_URGENT_ON_UPDATE = 2,
109 REPL_URGENT_ON_DELETE = 4
112 enum deletion_state {
113 OBJECT_NOT_DELETED=1,
120 static void replmd_deletion_state(struct ldb_module *module,
121 const struct ldb_message *msg,
122 enum deletion_state *current_state,
123 enum deletion_state *next_state)
126 bool enabled = false;
129 *current_state = OBJECT_REMOVED;
130 if (next_state != NULL) {
131 *next_state = OBJECT_REMOVED;
136 ret = dsdb_recyclebin_enabled(module, &enabled);
137 if (ret != LDB_SUCCESS) {
141 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
143 *current_state = OBJECT_TOMBSTONE;
144 if (next_state != NULL) {
145 *next_state = OBJECT_REMOVED;
150 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
151 *current_state = OBJECT_RECYCLED;
152 if (next_state != NULL) {
153 *next_state = OBJECT_REMOVED;
158 *current_state = OBJECT_DELETED;
159 if (next_state != NULL) {
160 *next_state = OBJECT_RECYCLED;
165 *current_state = OBJECT_NOT_DELETED;
166 if (next_state == NULL) {
171 *next_state = OBJECT_DELETED;
173 *next_state = OBJECT_TOMBSTONE;
177 static const struct {
178 const char *update_name;
179 enum urgent_situation repl_situation;
180 } urgent_objects[] = {
181 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
182 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
183 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
184 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
185 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
186 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
190 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
191 static const char *urgent_attrs[] = {
194 "userAccountControl",
199 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
200 enum urgent_situation situation)
203 for (i=0; urgent_objects[i].update_name; i++) {
205 if ((situation & urgent_objects[i].repl_situation) == 0) {
209 for (j=0; j<objectclass_el->num_values; j++) {
210 const struct ldb_val *v = &objectclass_el->values[j];
211 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
219 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
221 if (ldb_attr_in_list(urgent_attrs, el->name)) {
228 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
231 initialise the module
232 allocate the private structure and build the list
233 of partition DNs for use by replmd_notify()
235 static int replmd_init(struct ldb_module *module)
237 struct replmd_private *replmd_private;
238 struct ldb_context *ldb = ldb_module_get_ctx(module);
240 replmd_private = talloc_zero(module, struct replmd_private);
241 if (replmd_private == NULL) {
243 return LDB_ERR_OPERATIONS_ERROR;
245 ldb_module_set_private(module, replmd_private);
247 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
249 return ldb_next_init(module);
253 cleanup our per-transaction contexts
255 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
257 talloc_free(replmd_private->la_ctx);
258 replmd_private->la_list = NULL;
259 replmd_private->la_ctx = NULL;
261 talloc_free(replmd_private->bl_ctx);
262 replmd_private->la_backlinks = NULL;
263 replmd_private->bl_ctx = NULL;
268 struct la_backlink *next, *prev;
269 const char *attr_name;
270 struct GUID forward_guid, target_guid;
275 a ldb_modify request operating on modules below the
278 static int linked_attr_modify(struct ldb_module *module,
279 const struct ldb_message *message,
280 struct ldb_request *parent)
282 struct ldb_request *mod_req;
284 struct ldb_context *ldb = ldb_module_get_ctx(module);
285 TALLOC_CTX *tmp_ctx = talloc_new(module);
286 struct ldb_result *res;
288 res = talloc_zero(tmp_ctx, struct ldb_result);
290 talloc_free(tmp_ctx);
291 return ldb_oom(ldb_module_get_ctx(module));
294 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
298 ldb_modify_default_callback,
300 LDB_REQ_SET_LOCATION(mod_req);
301 if (ret != LDB_SUCCESS) {
302 talloc_free(tmp_ctx);
306 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
308 if (ret != LDB_SUCCESS) {
312 /* Run the new request */
313 ret = ldb_next_request(module, mod_req);
315 if (ret == LDB_SUCCESS) {
316 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
319 talloc_free(tmp_ctx);
324 process a backlinks we accumulated during a transaction, adding and
325 deleting the backlinks from the target objects
327 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
329 struct ldb_dn *target_dn, *source_dn;
331 struct ldb_context *ldb = ldb_module_get_ctx(module);
332 struct ldb_message *msg;
333 TALLOC_CTX *tmp_ctx = talloc_new(bl);
339 - construct ldb_message
340 - either an add or a delete
342 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
343 if (ret != LDB_SUCCESS) {
344 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
345 GUID_string(bl, &bl->target_guid)));
349 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
350 if (ret != LDB_SUCCESS) {
351 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
352 GUID_string(bl, &bl->forward_guid));
353 talloc_free(tmp_ctx);
357 msg = ldb_msg_new(tmp_ctx);
359 ldb_module_oom(module);
360 talloc_free(tmp_ctx);
361 return LDB_ERR_OPERATIONS_ERROR;
364 /* construct a ldb_message for adding/deleting the backlink */
366 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
368 ldb_module_oom(module);
369 talloc_free(tmp_ctx);
370 return LDB_ERR_OPERATIONS_ERROR;
372 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
373 if (ret != LDB_SUCCESS) {
374 talloc_free(tmp_ctx);
377 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
379 /* a backlink should never be single valued. Unfortunately the
380 exchange schema has a attribute
381 msExchBridgeheadedLocalConnectorsDNBL which is single
382 valued and a backlink. We need to cope with that by
383 ignoring the single value flag */
384 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
386 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
387 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
388 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
389 cope with possible corruption where the backlink has
390 already been removed */
391 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
392 ldb_dn_get_linearized(target_dn),
393 ldb_dn_get_linearized(source_dn),
394 ldb_errstring(ldb)));
396 } else if (ret != LDB_SUCCESS) {
397 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
398 bl->active?"add":"remove",
399 ldb_dn_get_linearized(source_dn),
400 ldb_dn_get_linearized(target_dn),
402 talloc_free(tmp_ctx);
405 talloc_free(tmp_ctx);
410 add a backlink to the list of backlinks to add/delete in the prepare
413 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
414 struct GUID *forward_guid, struct GUID *target_guid,
415 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
417 const struct dsdb_attribute *target_attr;
418 struct la_backlink *bl;
419 struct replmd_private *replmd_private =
420 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
422 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
425 * windows 2003 has a broken schema where the
426 * definition of msDS-IsDomainFor is missing (which is
427 * supposed to be the backlink of the
428 * msDS-HasDomainNCs attribute
433 /* see if its already in the list */
434 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
435 if (GUID_equal(forward_guid, &bl->forward_guid) &&
436 GUID_equal(target_guid, &bl->target_guid) &&
437 (target_attr->lDAPDisplayName == bl->attr_name ||
438 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
444 /* we found an existing one */
445 if (bl->active == active) {
448 DLIST_REMOVE(replmd_private->la_backlinks, bl);
453 if (replmd_private->bl_ctx == NULL) {
454 replmd_private->bl_ctx = talloc_new(replmd_private);
455 if (replmd_private->bl_ctx == NULL) {
456 ldb_module_oom(module);
457 return LDB_ERR_OPERATIONS_ERROR;
462 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
464 ldb_module_oom(module);
465 return LDB_ERR_OPERATIONS_ERROR;
468 /* Ensure the schema does not go away before the bl->attr_name is used */
469 if (!talloc_reference(bl, schema)) {
471 ldb_module_oom(module);
472 return LDB_ERR_OPERATIONS_ERROR;
475 bl->attr_name = target_attr->lDAPDisplayName;
476 bl->forward_guid = *forward_guid;
477 bl->target_guid = *target_guid;
480 /* the caller may ask for this backlink to be processed
483 int ret = replmd_process_backlink(module, bl, NULL);
488 DLIST_ADD(replmd_private->la_backlinks, bl);
495 * Callback for most write operations in this module:
497 * notify the repl task that a object has changed. The notifies are
498 * gathered up in the replmd_private structure then written to the
499 * @REPLCHANGED object in each partition during the prepare_commit
501 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
504 struct replmd_replicated_request *ac =
505 talloc_get_type_abort(req->context, struct replmd_replicated_request);
506 struct replmd_private *replmd_private =
507 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
508 struct nc_entry *modified_partition;
509 struct ldb_control *partition_ctrl;
510 const struct dsdb_control_current_partition *partition;
512 struct ldb_control **controls;
514 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
516 controls = ares->controls;
517 if (ldb_request_get_control(ac->req,
518 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
520 * Remove the current partition control from what we pass up
521 * the chain if it hasn't been requested manually.
523 controls = ldb_controls_except_specified(ares->controls, ares,
527 if (ares->error != LDB_SUCCESS) {
528 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
529 return ldb_module_done(ac->req, controls,
530 ares->response, ares->error);
533 if (ares->type != LDB_REPLY_DONE) {
534 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
535 return ldb_module_done(ac->req, NULL,
536 NULL, LDB_ERR_OPERATIONS_ERROR);
539 if (!partition_ctrl) {
540 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
541 return ldb_module_done(ac->req, NULL,
542 NULL, LDB_ERR_OPERATIONS_ERROR);
545 partition = talloc_get_type_abort(partition_ctrl->data,
546 struct dsdb_control_current_partition);
548 if (ac->seq_num > 0) {
549 for (modified_partition = replmd_private->ncs; modified_partition;
550 modified_partition = modified_partition->next) {
551 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
556 if (modified_partition == NULL) {
557 modified_partition = talloc_zero(replmd_private, struct nc_entry);
558 if (!modified_partition) {
559 ldb_oom(ldb_module_get_ctx(ac->module));
560 return ldb_module_done(ac->req, NULL,
561 NULL, LDB_ERR_OPERATIONS_ERROR);
563 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
564 if (!modified_partition->dn) {
565 ldb_oom(ldb_module_get_ctx(ac->module));
566 return ldb_module_done(ac->req, NULL,
567 NULL, LDB_ERR_OPERATIONS_ERROR);
569 DLIST_ADD(replmd_private->ncs, modified_partition);
572 if (ac->seq_num > modified_partition->mod_usn) {
573 modified_partition->mod_usn = ac->seq_num;
575 modified_partition->mod_usn_urgent = ac->seq_num;
578 if (!ac->apply_mode) {
579 replmd_private->originating_updates = true;
583 if (ac->apply_mode) {
584 ret = replmd_replicated_apply_isDeleted(ac);
585 if (ret != LDB_SUCCESS) {
586 return ldb_module_done(ac->req, NULL, NULL, ret);
590 /* free the partition control container here, for the
591 * common path. Other cases will have it cleaned up
592 * eventually with the ares */
593 talloc_free(partition_ctrl);
594 return ldb_module_done(ac->req, controls,
595 ares->response, LDB_SUCCESS);
601 * update a @REPLCHANGED record in each partition if there have been
602 * any writes of replicated data in the partition
604 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
606 struct replmd_private *replmd_private =
607 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
609 while (replmd_private->ncs) {
611 struct nc_entry *modified_partition = replmd_private->ncs;
613 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
614 modified_partition->mod_usn,
615 modified_partition->mod_usn_urgent, parent);
616 if (ret != LDB_SUCCESS) {
617 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
618 ldb_dn_get_linearized(modified_partition->dn)));
622 if (ldb_dn_compare(modified_partition->dn,
623 replmd_private->schema_dn) == 0) {
624 struct ldb_result *ext_res;
625 ret = dsdb_module_extended(module,
626 replmd_private->schema_dn,
628 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
630 DSDB_FLAG_NEXT_MODULE,
632 if (ret != LDB_SUCCESS) {
635 talloc_free(ext_res);
638 DLIST_REMOVE(replmd_private->ncs, modified_partition);
639 talloc_free(modified_partition);
647 created a replmd_replicated_request context
649 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
650 struct ldb_request *req)
652 struct ldb_context *ldb;
653 struct replmd_replicated_request *ac;
654 const struct GUID *our_invocation_id;
656 ldb = ldb_module_get_ctx(module);
658 ac = talloc_zero(req, struct replmd_replicated_request);
667 ac->schema = dsdb_get_schema(ldb, ac);
669 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
670 "replmd_modify: no dsdb_schema loaded");
671 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
676 /* get our invocationId */
677 our_invocation_id = samdb_ntds_invocation_id(ldb);
678 if (!our_invocation_id) {
679 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
680 "replmd_add: unable to find invocationId\n");
684 ac->our_invocation_id = *our_invocation_id;
690 add a time element to a record
692 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
694 struct ldb_message_element *el;
698 if (ldb_msg_find_element(msg, attr) != NULL) {
702 s = ldb_timestring(msg, t);
704 return LDB_ERR_OPERATIONS_ERROR;
707 ret = ldb_msg_add_string(msg, attr, s);
708 if (ret != LDB_SUCCESS) {
712 el = ldb_msg_find_element(msg, attr);
713 /* always set as replace. This works because on add ops, the flag
715 el->flags = LDB_FLAG_MOD_REPLACE;
721 add a uint64_t element to a record
723 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
724 const char *attr, uint64_t v)
726 struct ldb_message_element *el;
729 if (ldb_msg_find_element(msg, attr) != NULL) {
733 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
734 if (ret != LDB_SUCCESS) {
738 el = ldb_msg_find_element(msg, attr);
739 /* always set as replace. This works because on add ops, the flag
741 el->flags = LDB_FLAG_MOD_REPLACE;
746 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
747 const struct replPropertyMetaData1 *m2,
748 const uint32_t *rdn_attid)
751 * This assignment seems inoccous, but it is critical for the
752 * system, as we need to do the comparisons as a unsigned
753 * quantity, not signed (enums are signed integers)
755 uint32_t attid_1 = m1->attid;
756 uint32_t attid_2 = m2->attid;
758 if (attid_1 == attid_2) {
763 * See above regarding this being an unsigned comparison.
764 * Otherwise when the high bit is set on non-standard
765 * attributes, they would end up first, before objectClass
768 return attid_1 > attid_2 ? 1 : -1;
771 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
772 struct replPropertyMetaDataCtr1 *ctr1,
775 if (ctr1->count == 0) {
776 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
777 "No elements found in replPropertyMetaData for %s!\n",
778 ldb_dn_get_linearized(dn));
779 return LDB_ERR_CONSTRAINT_VIOLATION;
782 /* the objectClass attribute is value 0x00000000, so must be first */
783 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
784 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
785 "No objectClass found in replPropertyMetaData for %s!\n",
786 ldb_dn_get_linearized(dn));
787 return LDB_ERR_OBJECT_CLASS_VIOLATION;
793 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
794 struct replPropertyMetaDataCtr1 *ctr1,
797 /* Note this is O(n^2) for the almost-sorted case, which this is */
798 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
799 replmd_replPropertyMetaData1_attid_sort);
800 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
803 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
804 const struct ldb_message_element *e2,
805 const struct dsdb_schema *schema)
807 const struct dsdb_attribute *a1;
808 const struct dsdb_attribute *a2;
811 * TODO: make this faster by caching the dsdb_attribute pointer
812 * on the ldb_messag_element
815 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
816 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
819 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
823 return strcasecmp(e1->name, e2->name);
825 if (a1->attributeID_id == a2->attributeID_id) {
828 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
831 static void replmd_ldb_message_sort(struct ldb_message *msg,
832 const struct dsdb_schema *schema)
834 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
837 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
838 const struct GUID *invocation_id, uint64_t seq_num,
839 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
843 fix up linked attributes in replmd_add.
844 This involves setting up the right meta-data in extended DN
845 components, and creating backlinks to the object
847 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
848 uint64_t seq_num, const struct GUID *invocationId, NTTIME now,
849 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
852 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
853 struct ldb_context *ldb = ldb_module_get_ctx(module);
855 /* We will take a reference to the schema in replmd_add_backlink */
856 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
858 for (i=0; i<el->num_values; i++) {
859 struct ldb_val *v = &el->values[i];
860 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
861 struct GUID target_guid;
865 if (dsdb_dn == NULL) {
866 talloc_free(tmp_ctx);
867 return LDB_ERR_INVALID_DN_SYNTAX;
870 /* note that the DN already has the extended
871 components from the extended_dn_store module */
872 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
873 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
874 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
875 if (ret != LDB_SUCCESS) {
876 talloc_free(tmp_ctx);
879 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
880 if (ret != LDB_SUCCESS) {
881 talloc_free(tmp_ctx);
886 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
887 seq_num, seq_num, now, 0, false);
888 if (ret != LDB_SUCCESS) {
889 talloc_free(tmp_ctx);
893 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
894 if (ret != LDB_SUCCESS) {
895 talloc_free(tmp_ctx);
900 talloc_free(tmp_ctx);
906 intercept add requests
908 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
910 struct ldb_context *ldb;
911 struct ldb_control *control;
912 struct replmd_replicated_request *ac;
913 enum ndr_err_code ndr_err;
914 struct ldb_request *down_req;
915 struct ldb_message *msg;
916 const DATA_BLOB *guid_blob;
918 struct replPropertyMetaDataBlob nmd;
919 struct ldb_val nmd_value;
922 * The use of a time_t here seems odd, but as the NTTIME
923 * elements are actually declared as NTTIME_1sec in the IDL,
924 * getting a higher resolution timestamp is not required.
926 time_t t = time(NULL);
931 unsigned int functional_level;
933 bool allow_add_guid = false;
934 bool remove_current_guid = false;
935 bool is_urgent = false;
936 bool is_schema_nc = false;
937 struct ldb_message_element *objectclass_el;
938 struct replmd_private *replmd_private =
939 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
941 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
942 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
944 allow_add_guid = true;
947 /* do not manipulate our control entries */
948 if (ldb_dn_is_special(req->op.add.message->dn)) {
949 return ldb_next_request(module, req);
952 ldb = ldb_module_get_ctx(module);
954 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
956 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
957 if (guid_blob != NULL) {
958 if (!allow_add_guid) {
959 ldb_set_errstring(ldb,
960 "replmd_add: it's not allowed to add an object with objectGUID!");
961 return LDB_ERR_UNWILLING_TO_PERFORM;
963 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
964 if (!NT_STATUS_IS_OK(status)) {
965 ldb_set_errstring(ldb,
966 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
967 return LDB_ERR_UNWILLING_TO_PERFORM;
969 /* we remove this attribute as it can be a string and
970 * will not be treated correctly and then we will re-add
971 * it later on in the good format */
972 remove_current_guid = true;
976 guid = GUID_random();
979 ac = replmd_ctx_init(module, req);
981 return ldb_module_oom(module);
984 functional_level = dsdb_functional_level(ldb);
986 /* Get a sequence number from the backend */
987 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
988 if (ret != LDB_SUCCESS) {
993 /* we have to copy the message as the caller might have it as a const */
994 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
998 return LDB_ERR_OPERATIONS_ERROR;
1001 /* generated times */
1002 unix_to_nt_time(&now, t);
1003 time_str = ldb_timestring(msg, t);
1007 return LDB_ERR_OPERATIONS_ERROR;
1009 if (remove_current_guid) {
1010 ldb_msg_remove_attr(msg,"objectGUID");
1014 * remove autogenerated attributes
1016 ldb_msg_remove_attr(msg, "whenCreated");
1017 ldb_msg_remove_attr(msg, "whenChanged");
1018 ldb_msg_remove_attr(msg, "uSNCreated");
1019 ldb_msg_remove_attr(msg, "uSNChanged");
1020 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1023 * readd replicated attributes
1025 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1026 if (ret != LDB_SUCCESS) {
1032 /* build the replication meta_data */
1035 nmd.ctr.ctr1.count = msg->num_elements;
1036 nmd.ctr.ctr1.array = talloc_array(msg,
1037 struct replPropertyMetaData1,
1038 nmd.ctr.ctr1.count);
1039 if (!nmd.ctr.ctr1.array) {
1042 return LDB_ERR_OPERATIONS_ERROR;
1045 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1047 for (i=0; i < msg->num_elements;) {
1048 struct ldb_message_element *e = &msg->elements[i];
1049 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1050 const struct dsdb_attribute *sa;
1052 if (e->name[0] == '@') {
1057 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1059 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1060 "replmd_add: attribute '%s' not defined in schema\n",
1063 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1066 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1067 /* if the attribute is not replicated (0x00000001)
1068 * or constructed (0x00000004) it has no metadata
1074 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1075 ret = replmd_add_fix_la(module, e, ac->seq_num,
1076 &ac->our_invocation_id, now,
1078 if (ret != LDB_SUCCESS) {
1082 /* linked attributes are not stored in
1083 replPropertyMetaData in FL above w2k */
1088 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1090 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1091 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1094 if (rdn_val == NULL) {
1097 return LDB_ERR_OPERATIONS_ERROR;
1100 rdn = (const char*)rdn_val->data;
1101 if (strcmp(rdn, "Deleted Objects") == 0) {
1103 * Set the originating_change_time to 29/12/9999 at 23:59:59
1104 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1106 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1108 m->originating_change_time = now;
1111 m->originating_change_time = now;
1113 m->originating_invocation_id = ac->our_invocation_id;
1114 m->originating_usn = ac->seq_num;
1115 m->local_usn = ac->seq_num;
1118 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1123 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1125 if (e->num_values != 0) {
1130 ldb_msg_remove_element(msg, e);
1133 /* fix meta data count */
1134 nmd.ctr.ctr1.count = ni;
1137 * sort meta data array
1139 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1140 if (ret != LDB_SUCCESS) {
1141 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1146 /* generated NDR encoded values */
1147 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1149 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1153 return LDB_ERR_OPERATIONS_ERROR;
1157 * add the autogenerated values
1159 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1160 if (ret != LDB_SUCCESS) {
1165 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1166 if (ret != LDB_SUCCESS) {
1171 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1172 if (ret != LDB_SUCCESS) {
1177 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1178 if (ret != LDB_SUCCESS) {
1183 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1184 if (ret != LDB_SUCCESS) {
1191 * sort the attributes by attid before storing the object
1193 replmd_ldb_message_sort(msg, ac->schema);
1196 * Assert that we do have an objectClass
1198 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1199 if (objectclass_el == NULL) {
1200 ldb_asprintf_errstring(ldb, __location__
1201 ": objectClass missing on %s\n",
1202 ldb_dn_get_linearized(msg->dn));
1204 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1206 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1207 REPL_URGENT_ON_CREATE);
1209 ac->is_urgent = is_urgent;
1210 ret = ldb_build_add_req(&down_req, ldb, ac,
1213 ac, replmd_op_callback,
1216 LDB_REQ_SET_LOCATION(down_req);
1217 if (ret != LDB_SUCCESS) {
1222 /* current partition control is needed by "replmd_op_callback" */
1223 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1224 ret = ldb_request_add_control(down_req,
1225 DSDB_CONTROL_CURRENT_PARTITION_OID,
1227 if (ret != LDB_SUCCESS) {
1233 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1234 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1235 if (ret != LDB_SUCCESS) {
1241 /* mark the control done */
1243 control->critical = 0;
1245 /* go on with the call chain */
1246 return ldb_next_request(module, down_req);
1251 * update the replPropertyMetaData for one element
1253 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1254 struct ldb_message *msg,
1255 struct ldb_message_element *el,
1256 struct ldb_message_element *old_el,
1257 struct replPropertyMetaDataBlob *omd,
1258 const struct dsdb_schema *schema,
1260 const struct GUID *our_invocation_id,
1263 struct ldb_request *req)
1266 const struct dsdb_attribute *a;
1267 struct replPropertyMetaData1 *md1;
1268 bool may_skip = false;
1271 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1273 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1274 /* allow this to make it possible for dbcheck
1275 to remove bad attributes */
1279 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1281 return LDB_ERR_OPERATIONS_ERROR;
1284 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1286 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1291 * if the attribute's value haven't changed, and this isn't
1292 * just a delete of everything then return LDB_SUCCESS Unless
1293 * we have the provision control or if the attribute is
1294 * interSiteTopologyGenerator as this page explain:
1295 * http://support.microsoft.com/kb/224815 this attribute is
1296 * periodicaly written by the DC responsible for the intersite
1297 * generation in a given site
1299 * Unchanged could be deleting or replacing an already-gone
1300 * thing with an unconstrained delete/empty replace or a
1301 * replace with the same value, but not an add with the same
1302 * value because that could be about adding a duplicate (which
1303 * is for someone else to error out on).
1305 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1306 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1309 } else if (old_el == NULL && el->num_values == 0) {
1310 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1312 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1315 } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1316 ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1318 * We intentionally skip the version bump when attempting to
1321 * The control is set by dbcheck and expunge-tombstones which
1322 * both attempt to be non-replicating. Otherwise, making an
1323 * alteration to the replication state would trigger a
1324 * broadcast of all expunged objects.
1329 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1331 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1335 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1336 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1338 * allow this to make it possible for dbcheck
1339 * to rebuild broken metadata
1345 for (i=0; i<omd->ctr.ctr1.count; i++) {
1347 * First check if we find it under the msDS-IntID,
1348 * then check if we find it under the OID and
1351 * This allows the administrator to simply re-write
1352 * the attributes and so restore replication, which is
1353 * likely what they will try to do.
1355 if (attid == omd->ctr.ctr1.array[i].attid) {
1359 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1364 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1365 /* linked attributes are not stored in
1366 replPropertyMetaData in FL above w2k, but we do
1367 raise the seqnum for the object */
1368 if (*seq_num == 0 &&
1369 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1370 return LDB_ERR_OPERATIONS_ERROR;
1375 if (i == omd->ctr.ctr1.count) {
1376 /* we need to add a new one */
1377 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1378 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1379 if (omd->ctr.ctr1.array == NULL) {
1381 return LDB_ERR_OPERATIONS_ERROR;
1383 omd->ctr.ctr1.count++;
1384 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1387 /* Get a new sequence number from the backend. We only do this
1388 * if we have a change that requires a new
1389 * replPropertyMetaData element
1391 if (*seq_num == 0) {
1392 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1393 if (ret != LDB_SUCCESS) {
1394 return LDB_ERR_OPERATIONS_ERROR;
1398 md1 = &omd->ctr.ctr1.array[i];
1401 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1402 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1405 if (rdn_val == NULL) {
1407 return LDB_ERR_OPERATIONS_ERROR;
1410 rdn = (const char*)rdn_val->data;
1411 if (strcmp(rdn, "Deleted Objects") == 0) {
1413 * Set the originating_change_time to 29/12/9999 at 23:59:59
1414 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1416 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1418 md1->originating_change_time = now;
1421 md1->originating_change_time = now;
1423 md1->originating_invocation_id = *our_invocation_id;
1424 md1->originating_usn = *seq_num;
1425 md1->local_usn = *seq_num;
1431 * Bump the replPropertyMetaData version on an attribute, and if it
1432 * has changed (or forced by leaving rdn_old NULL), update the value
1435 * This is important, as calling a modify operation may not change the
1436 * version number if the values appear unchanged, but a rename between
1437 * parents bumps this value.
1440 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1441 struct ldb_message *msg,
1442 const struct ldb_val *rdn_new,
1443 const struct ldb_val *rdn_old,
1444 struct replPropertyMetaDataBlob *omd,
1445 struct replmd_replicated_request *ar,
1449 const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1450 const struct dsdb_attribute *rdn_attr =
1451 dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1452 const char *attr_name = rdn_attr != NULL ?
1453 rdn_attr->lDAPDisplayName :
1455 struct ldb_message_element new_el = {
1456 .flags = LDB_FLAG_MOD_REPLACE,
1459 .values = discard_const_p(struct ldb_val, rdn_new)
1461 struct ldb_message_element old_el = {
1462 .flags = LDB_FLAG_MOD_REPLACE,
1464 .num_values = rdn_old ? 1 : 0,
1465 .values = discard_const_p(struct ldb_val, rdn_old)
1468 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1469 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1470 if (ret != LDB_SUCCESS) {
1471 return ldb_oom(ldb);
1475 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1476 omd, ar->schema, &ar->seq_num,
1477 &ar->our_invocation_id,
1478 now, is_schema_nc, ar->req);
1482 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1484 uint32_t count = omd.ctr.ctr1.count;
1487 for (i=0; i < count; i++) {
1488 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1489 if (max < m.local_usn) {
1497 * update the replPropertyMetaData object each time we modify an
1498 * object. This is needed for DRS replication, as the merge on the
1499 * client is based on this object
1501 static int replmd_update_rpmd(struct ldb_module *module,
1502 const struct dsdb_schema *schema,
1503 struct ldb_request *req,
1504 const char * const *rename_attrs,
1505 struct ldb_message *msg, uint64_t *seq_num,
1506 time_t t, bool is_schema_nc,
1507 bool *is_urgent, bool *rodc)
1509 const struct ldb_val *omd_value;
1510 enum ndr_err_code ndr_err;
1511 struct replPropertyMetaDataBlob omd;
1514 const struct GUID *our_invocation_id;
1516 const char * const *attrs = NULL;
1517 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1518 struct ldb_result *res;
1519 struct ldb_context *ldb;
1520 struct ldb_message_element *objectclass_el;
1521 enum urgent_situation situation;
1522 bool rmd_is_provided;
1523 bool rmd_is_just_resorted = false;
1524 const char *not_rename_attrs[4 + msg->num_elements];
1527 attrs = rename_attrs;
1529 for (i = 0; i < msg->num_elements; i++) {
1530 not_rename_attrs[i] = msg->elements[i].name;
1532 not_rename_attrs[i] = "replPropertyMetaData";
1533 not_rename_attrs[i+1] = "objectClass";
1534 not_rename_attrs[i+2] = "instanceType";
1535 not_rename_attrs[i+3] = NULL;
1536 attrs = not_rename_attrs;
1539 ldb = ldb_module_get_ctx(module);
1541 our_invocation_id = samdb_ntds_invocation_id(ldb);
1542 if (!our_invocation_id) {
1543 /* this happens during an initial vampire while
1544 updating the schema */
1545 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1549 unix_to_nt_time(&now, t);
1551 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1552 rmd_is_provided = true;
1553 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1554 rmd_is_just_resorted = true;
1557 rmd_is_provided = false;
1560 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1561 * otherwise we consider we are updating */
1562 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1563 situation = REPL_URGENT_ON_DELETE;
1564 } else if (rename_attrs) {
1565 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1567 situation = REPL_URGENT_ON_UPDATE;
1570 if (rmd_is_provided) {
1571 /* In this case the change_replmetadata control was supplied */
1572 /* We check that it's the only attribute that is provided
1573 * (it's a rare case so it's better to keep the code simplier)
1574 * We also check that the highest local_usn is bigger or the same as
1577 if( msg->num_elements != 1 ||
1578 strncmp(msg->elements[0].name,
1579 "replPropertyMetaData", 20) ) {
1580 DEBUG(0,(__location__ ": changereplmetada control called without "\
1581 "a specified replPropertyMetaData attribute or with others\n"));
1582 return LDB_ERR_OPERATIONS_ERROR;
1584 if (situation != REPL_URGENT_ON_UPDATE) {
1585 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1586 return LDB_ERR_OPERATIONS_ERROR;
1588 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1590 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1591 ldb_dn_get_linearized(msg->dn)));
1592 return LDB_ERR_OPERATIONS_ERROR;
1594 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1595 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1596 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1597 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1598 ldb_dn_get_linearized(msg->dn)));
1599 return LDB_ERR_OPERATIONS_ERROR;
1602 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1603 DSDB_FLAG_NEXT_MODULE |
1604 DSDB_SEARCH_SHOW_RECYCLED |
1605 DSDB_SEARCH_SHOW_EXTENDED_DN |
1606 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1607 DSDB_SEARCH_REVEAL_INTERNALS, req);
1609 if (ret != LDB_SUCCESS) {
1613 if (rmd_is_just_resorted == false) {
1614 *seq_num = find_max_local_usn(omd);
1616 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1619 * The test here now allows for a new
1620 * replPropertyMetaData with no change, if was
1621 * just dbcheck re-sorting the values.
1623 if (*seq_num <= db_seq) {
1624 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1625 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1626 (long long)*seq_num, (long long)db_seq));
1627 return LDB_ERR_OPERATIONS_ERROR;
1632 /* search for the existing replPropertyMetaDataBlob. We need
1633 * to use REVEAL and ask for DNs in storage format to support
1634 * the check for values being the same in
1635 * replmd_update_rpmd_element()
1637 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1638 DSDB_FLAG_NEXT_MODULE |
1639 DSDB_SEARCH_SHOW_RECYCLED |
1640 DSDB_SEARCH_SHOW_EXTENDED_DN |
1641 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1642 DSDB_SEARCH_REVEAL_INTERNALS, req);
1643 if (ret != LDB_SUCCESS) {
1647 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1649 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1650 ldb_dn_get_linearized(msg->dn)));
1651 return LDB_ERR_OPERATIONS_ERROR;
1654 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1655 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1656 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1657 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1658 ldb_dn_get_linearized(msg->dn)));
1659 return LDB_ERR_OPERATIONS_ERROR;
1662 if (omd.version != 1) {
1663 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1664 omd.version, ldb_dn_get_linearized(msg->dn)));
1665 return LDB_ERR_OPERATIONS_ERROR;
1668 for (i=0; i<msg->num_elements;) {
1669 struct ldb_message_element *el = &msg->elements[i];
1670 struct ldb_message_element *old_el;
1672 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1673 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1674 &omd, schema, seq_num,
1678 if (ret != LDB_SUCCESS) {
1682 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1683 *is_urgent = replmd_check_urgent_attribute(el);
1686 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1691 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1693 if (el->num_values != 0) {
1698 ldb_msg_remove_element(msg, el);
1703 * Assert that we have an objectClass attribute - this is major
1704 * corruption if we don't have this!
1706 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1707 if (objectclass_el != NULL) {
1709 * Now check if this objectClass means we need to do urgent replication
1711 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1715 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1716 ldb_asprintf_errstring(ldb, __location__
1717 ": objectClass missing on %s\n",
1718 ldb_dn_get_linearized(msg->dn));
1719 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1723 * replmd_update_rpmd_element has done an update if the
1726 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1727 struct ldb_val *md_value;
1728 struct ldb_message_element *el;
1730 /*if we are RODC and this is a DRSR update then its ok*/
1731 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1732 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1733 unsigned instanceType;
1735 ret = samdb_rodc(ldb, rodc);
1736 if (ret != LDB_SUCCESS) {
1737 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1739 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1740 return LDB_ERR_REFERRAL;
1743 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1744 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1745 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1746 "cannot change replicated attribute on partial replica");
1750 md_value = talloc(msg, struct ldb_val);
1751 if (md_value == NULL) {
1753 return LDB_ERR_OPERATIONS_ERROR;
1756 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1757 if (ret != LDB_SUCCESS) {
1758 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1762 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1763 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1764 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1765 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1766 ldb_dn_get_linearized(msg->dn)));
1767 return LDB_ERR_OPERATIONS_ERROR;
1770 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1771 if (ret != LDB_SUCCESS) {
1772 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1773 ldb_dn_get_linearized(msg->dn)));
1778 el->values = md_value;
1785 struct dsdb_dn *dsdb_dn;
1790 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1792 return GUID_compare(&pdn1->guid, &pdn2->guid);
1795 static int GUID_compare_struct(struct GUID *g1, struct GUID g2)
1797 return GUID_compare(g1, &g2);
1800 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1801 unsigned int count, struct GUID *guid,
1804 struct parsed_dn *ret;
1806 if (dn && GUID_all_zero(guid)) {
1807 /* when updating a link using DRS, we sometimes get a
1808 NULL GUID. We then need to try and match by DN */
1809 for (i=0; i<count; i++) {
1810 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1811 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1817 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare_struct, ret);
1822 get a series of message element values as an array of DNs and GUIDs
1823 the result is sorted by GUID
1825 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1826 struct ldb_message_element *el, struct parsed_dn **pdn,
1827 const char *ldap_oid, struct ldb_request *parent)
1830 bool values_are_sorted = true;
1831 struct ldb_context *ldb = ldb_module_get_ctx(module);
1838 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1840 ldb_module_oom(module);
1841 return LDB_ERR_OPERATIONS_ERROR;
1844 for (i=0; i<el->num_values; i++) {
1845 struct ldb_val *v = &el->values[i];
1848 struct parsed_dn *p;
1852 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1853 if (p->dsdb_dn == NULL) {
1854 return LDB_ERR_INVALID_DN_SYNTAX;
1857 dn = p->dsdb_dn->dn;
1859 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
1860 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1861 /* we got a DN without a GUID - go find the GUID */
1862 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
1863 if (ret != LDB_SUCCESS) {
1864 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1865 ldb_dn_get_linearized(dn));
1866 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1867 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1868 ldb_attr_cmp(el->name, "member") == 0) {
1869 return LDB_ERR_UNWILLING_TO_PERFORM;
1873 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
1874 if (ret != LDB_SUCCESS) {
1877 } else if (!NT_STATUS_IS_OK(status)) {
1878 return LDB_ERR_OPERATIONS_ERROR;
1880 if (i > 0 && values_are_sorted) {
1881 int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
1883 values_are_sorted = false;
1886 /* keep a pointer to the original ldb_val */
1889 if (! values_are_sorted) {
1890 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1896 build a new extended DN, including all meta data fields
1898 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1899 RMD_ADDTIME = originating_add_time
1900 RMD_INVOCID = originating_invocation_id
1901 RMD_CHANGETIME = originating_change_time
1902 RMD_ORIGINATING_USN = originating_usn
1903 RMD_LOCAL_USN = local_usn
1904 RMD_VERSION = version
1906 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1907 const struct GUID *invocation_id, uint64_t seq_num,
1908 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1910 struct ldb_dn *dn = dsdb_dn->dn;
1911 const char *tstring, *usn_string, *flags_string;
1912 struct ldb_val tval;
1914 struct ldb_val usnv, local_usnv;
1915 struct ldb_val vers, flagsv;
1918 const char *dnstring;
1920 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1922 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1924 return LDB_ERR_OPERATIONS_ERROR;
1926 tval = data_blob_string_const(tstring);
1928 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1930 return LDB_ERR_OPERATIONS_ERROR;
1932 usnv = data_blob_string_const(usn_string);
1934 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1936 return LDB_ERR_OPERATIONS_ERROR;
1938 local_usnv = data_blob_string_const(usn_string);
1940 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1942 return LDB_ERR_OPERATIONS_ERROR;
1944 vers = data_blob_string_const(vstring);
1946 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1947 if (!NT_STATUS_IS_OK(status)) {
1948 return LDB_ERR_OPERATIONS_ERROR;
1951 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1952 if (!flags_string) {
1953 return LDB_ERR_OPERATIONS_ERROR;
1955 flagsv = data_blob_string_const(flags_string);
1957 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1958 if (ret != LDB_SUCCESS) return ret;
1959 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1960 if (ret != LDB_SUCCESS) return ret;
1961 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1962 if (ret != LDB_SUCCESS) return ret;
1963 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1964 if (ret != LDB_SUCCESS) return ret;
1965 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1966 if (ret != LDB_SUCCESS) return ret;
1967 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1968 if (ret != LDB_SUCCESS) return ret;
1969 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1970 if (ret != LDB_SUCCESS) return ret;
1972 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1973 if (dnstring == NULL) {
1974 return LDB_ERR_OPERATIONS_ERROR;
1976 *v = data_blob_string_const(dnstring);
1981 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1982 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1983 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1984 uint32_t version, bool deleted);
1987 check if any links need upgrading from w2k format
1989 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.
1991 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1994 for (i=0; i<count; i++) {
1999 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
2000 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2004 /* it's an old one that needs upgrading */
2005 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
2007 if (ret != LDB_SUCCESS) {
2015 update an extended DN, including all meta data fields
2017 see replmd_build_la_val for value names
2019 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2020 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2021 uint64_t usn, uint64_t local_usn, NTTIME nttime,
2022 uint32_t version, bool deleted)
2024 struct ldb_dn *dn = dsdb_dn->dn;
2025 const char *tstring, *usn_string, *flags_string;
2026 struct ldb_val tval;
2028 struct ldb_val usnv, local_usnv;
2029 struct ldb_val vers, flagsv;
2030 const struct ldb_val *old_addtime;
2031 uint32_t old_version;
2034 const char *dnstring;
2036 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2038 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2040 return LDB_ERR_OPERATIONS_ERROR;
2042 tval = data_blob_string_const(tstring);
2044 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2046 return LDB_ERR_OPERATIONS_ERROR;
2048 usnv = data_blob_string_const(usn_string);
2050 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2052 return LDB_ERR_OPERATIONS_ERROR;
2054 local_usnv = data_blob_string_const(usn_string);
2056 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2057 if (!NT_STATUS_IS_OK(status)) {
2058 return LDB_ERR_OPERATIONS_ERROR;
2061 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2062 if (!flags_string) {
2063 return LDB_ERR_OPERATIONS_ERROR;
2065 flagsv = data_blob_string_const(flags_string);
2067 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2068 if (ret != LDB_SUCCESS) return ret;
2070 /* get the ADDTIME from the original */
2071 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2072 if (old_addtime == NULL) {
2073 old_addtime = &tval;
2075 if (dsdb_dn != old_dsdb_dn ||
2076 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2077 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2078 if (ret != LDB_SUCCESS) return ret;
2081 /* use our invocation id */
2082 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2083 if (ret != LDB_SUCCESS) return ret;
2085 /* changetime is the current time */
2086 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2087 if (ret != LDB_SUCCESS) return ret;
2089 /* update the USN */
2090 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2091 if (ret != LDB_SUCCESS) return ret;
2093 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2094 if (ret != LDB_SUCCESS) return ret;
2096 /* increase the version by 1 */
2097 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2098 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2099 version = old_version+1;
2101 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2102 vers = data_blob_string_const(vstring);
2103 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2104 if (ret != LDB_SUCCESS) return ret;
2106 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2107 if (dnstring == NULL) {
2108 return LDB_ERR_OPERATIONS_ERROR;
2110 *v = data_blob_string_const(dnstring);
2116 handle adding a linked attribute
2118 static int replmd_modify_la_add(struct ldb_module *module,
2119 const struct dsdb_schema *schema,
2120 struct ldb_message *msg,
2121 struct ldb_message_element *el,
2122 struct ldb_message_element *old_el,
2123 const struct dsdb_attribute *schema_attr,
2126 struct GUID *msg_guid,
2127 struct ldb_request *parent)
2130 struct parsed_dn *dns, *old_dns;
2131 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2133 struct ldb_val *new_values = NULL;
2134 unsigned int num_new_values = 0;
2135 unsigned old_num_values = old_el?old_el->num_values:0;
2136 const struct GUID *invocation_id;
2137 struct ldb_context *ldb = ldb_module_get_ctx(module);
2140 unix_to_nt_time(&now, t);
2142 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2143 if (ret != LDB_SUCCESS) {
2144 talloc_free(tmp_ctx);
2148 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2149 if (ret != LDB_SUCCESS) {
2150 talloc_free(tmp_ctx);
2154 invocation_id = samdb_ntds_invocation_id(ldb);
2155 if (!invocation_id) {
2156 talloc_free(tmp_ctx);
2157 return LDB_ERR_OPERATIONS_ERROR;
2160 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2161 if (ret != LDB_SUCCESS) {
2162 talloc_free(tmp_ctx);
2166 /* for each new value, see if it exists already with the same GUID */
2167 for (i=0; i<el->num_values; i++) {
2168 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, &dns[i].guid, NULL);
2170 /* this is a new linked attribute value */
2171 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
2172 if (new_values == NULL) {
2173 ldb_module_oom(module);
2174 talloc_free(tmp_ctx);
2175 return LDB_ERR_OPERATIONS_ERROR;
2177 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2178 invocation_id, seq_num, seq_num, now, 0, false);
2179 if (ret != LDB_SUCCESS) {
2180 talloc_free(tmp_ctx);
2185 /* this is only allowed if the GUID was
2186 previously deleted. */
2187 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2189 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2190 struct GUID_txt_buf guid_str;
2191 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2192 el->name, GUID_buf_string(&p->guid, &guid_str));
2193 talloc_free(tmp_ctx);
2194 /* error codes for 'member' need to be
2196 if (ldb_attr_cmp(el->name, "member") == 0) {
2197 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2199 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2202 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2203 invocation_id, seq_num, seq_num, now, 0, false);
2204 if (ret != LDB_SUCCESS) {
2205 talloc_free(tmp_ctx);
2210 ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, true);
2211 if (ret != LDB_SUCCESS) {
2212 talloc_free(tmp_ctx);
2217 /* add the new ones on to the end of the old values, constructing a new el->values */
2218 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2220 old_num_values+num_new_values);
2221 if (el->values == NULL) {
2222 ldb_module_oom(module);
2223 return LDB_ERR_OPERATIONS_ERROR;
2226 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2227 el->num_values = old_num_values + num_new_values;
2229 talloc_steal(msg->elements, el->values);
2230 talloc_steal(el->values, new_values);
2232 talloc_free(tmp_ctx);
2234 /* we now tell the backend to replace all existing values
2235 with the one we have constructed */
2236 el->flags = LDB_FLAG_MOD_REPLACE;
2243 handle deleting all active linked attributes
2245 static int replmd_modify_la_delete(struct ldb_module *module,
2246 const struct dsdb_schema *schema,
2247 struct ldb_message *msg,
2248 struct ldb_message_element *el,
2249 struct ldb_message_element *old_el,
2250 const struct dsdb_attribute *schema_attr,
2253 struct GUID *msg_guid,
2254 struct ldb_request *parent)
2257 struct parsed_dn *dns, *old_dns;
2258 TALLOC_CTX *tmp_ctx = NULL;
2260 const struct GUID *invocation_id;
2261 struct ldb_context *ldb = ldb_module_get_ctx(module);
2262 struct ldb_control *vanish_links_ctrl = NULL;
2263 bool vanish_links = false;
2264 unsigned int num_to_delete = el->num_values;
2267 unix_to_nt_time(&now, t);
2269 /* check if there is nothing to delete */
2270 if ((!old_el || old_el->num_values == 0) &&
2271 el->num_values == 0) {
2275 if (!old_el || old_el->num_values == 0) {
2276 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2279 tmp_ctx = talloc_new(msg);
2280 if (tmp_ctx == NULL) {
2281 return LDB_ERR_OPERATIONS_ERROR;
2284 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2285 if (ret != LDB_SUCCESS) {
2286 talloc_free(tmp_ctx);
2290 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2291 if (ret != LDB_SUCCESS) {
2292 talloc_free(tmp_ctx);
2296 invocation_id = samdb_ntds_invocation_id(ldb);
2297 if (!invocation_id) {
2298 talloc_free(tmp_ctx);
2299 return LDB_ERR_OPERATIONS_ERROR;
2302 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2303 if (ret != LDB_SUCCESS) {
2304 talloc_free(tmp_ctx);
2309 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2310 if (vanish_links_ctrl) {
2311 vanish_links = true;
2312 vanish_links_ctrl->critical = false;
2319 /* see if we are being asked to delete any links that
2320 don't exist or are already deleted */
2321 for (i=0; i < num_to_delete; i++) {
2322 struct parsed_dn *p = &dns[i];
2323 struct parsed_dn *p2;
2326 p2 = parsed_dn_find(old_dns, old_el->num_values, &p->guid, NULL);
2328 struct GUID_txt_buf buf;
2329 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2330 el->name, GUID_buf_string(&p->guid, &buf));
2331 if (ldb_attr_cmp(el->name, "member") == 0) {
2332 talloc_free(tmp_ctx);
2333 return LDB_ERR_UNWILLING_TO_PERFORM;
2335 talloc_free(tmp_ctx);
2336 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2339 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2340 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2341 struct GUID_txt_buf buf;
2342 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2344 DEBUG(0, ("Deleting deleted linked attribute %s to %s, "
2345 "because vanish_links control is set\n",
2346 el->name, guid_str));
2349 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2350 el->name, guid_str);
2351 if (ldb_attr_cmp(el->name, "member") == 0) {
2352 talloc_free(tmp_ctx);
2353 return LDB_ERR_UNWILLING_TO_PERFORM;
2355 talloc_free(tmp_ctx);
2356 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2362 if (num_to_delete == old_el->num_values || num_to_delete == 0) {
2363 el->flags = LDB_FLAG_MOD_REPLACE;
2365 for (i = 0; i < old_el->num_values; i++) {
2366 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2367 if (ret != LDB_SUCCESS) {
2368 talloc_free(tmp_ctx);
2372 talloc_free(tmp_ctx);
2375 unsigned int num_values = 0;
2377 for (i = 0; i < old_el->num_values; i++) {
2378 if (parsed_dn_find(dns, num_to_delete, &old_dns[i].guid, NULL) != NULL) {
2379 /* The element is in the delete list. mark it dead. */
2380 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2381 if (ret != LDB_SUCCESS) {
2382 talloc_free(tmp_ctx);
2385 old_dns[i].v->length = 0;
2390 for (i = 0; i < old_el->num_values; i++) {
2391 if (old_el->values[i].length != 0) {
2392 old_el->values[j] = old_el->values[i];
2394 if (j == num_values) {
2399 old_el->num_values = num_values;
2403 /* for each new value, see if it exists already with the same GUID
2404 if it is not already deleted and matches the delete list then delete it
2406 for (i=0; i<old_el->num_values; i++) {
2407 struct parsed_dn *p = &old_dns[i];
2410 if (num_to_delete && parsed_dn_find(dns, num_to_delete, &p->guid, NULL) == NULL) {
2414 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2415 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2417 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2418 invocation_id, seq_num, seq_num, now, 0, true);
2419 if (ret != LDB_SUCCESS) {
2420 talloc_free(tmp_ctx);
2423 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2424 if (ret != LDB_SUCCESS) {
2425 talloc_free(tmp_ctx);
2430 el->values = talloc_steal(msg->elements, old_el->values);
2431 el->num_values = old_el->num_values;
2433 talloc_free(tmp_ctx);
2435 /* we now tell the backend to replace all existing values
2436 with the one we have constructed */
2437 el->flags = LDB_FLAG_MOD_REPLACE;
2443 handle replacing a linked attribute
2445 static int replmd_modify_la_replace(struct ldb_module *module,
2446 const struct dsdb_schema *schema,
2447 struct ldb_message *msg,
2448 struct ldb_message_element *el,
2449 struct ldb_message_element *old_el,
2450 const struct dsdb_attribute *schema_attr,
2453 struct GUID *msg_guid,
2454 struct ldb_request *parent)
2457 struct parsed_dn *dns, *old_dns;
2458 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2460 const struct GUID *invocation_id;
2461 struct ldb_context *ldb = ldb_module_get_ctx(module);
2462 struct ldb_val *new_values = NULL;
2463 unsigned int num_new_values = 0;
2464 unsigned int old_num_values = old_el?old_el->num_values:0;
2467 unix_to_nt_time(&now, t);
2469 /* check if there is nothing to replace */
2470 if ((!old_el || old_el->num_values == 0) &&
2471 el->num_values == 0) {
2475 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2476 if (ret != LDB_SUCCESS) {
2477 talloc_free(tmp_ctx);
2481 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2482 if (ret != LDB_SUCCESS) {
2483 talloc_free(tmp_ctx);
2487 invocation_id = samdb_ntds_invocation_id(ldb);
2488 if (!invocation_id) {
2489 return LDB_ERR_OPERATIONS_ERROR;
2492 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2493 if (ret != LDB_SUCCESS) {
2494 talloc_free(tmp_ctx);
2498 /* mark all the old ones as deleted */
2499 for (i=0; i<old_num_values; i++) {
2500 struct parsed_dn *old_p = &old_dns[i];
2501 struct parsed_dn *p;
2502 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2504 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2506 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, false);
2507 if (ret != LDB_SUCCESS) {
2508 talloc_free(tmp_ctx);
2512 p = parsed_dn_find(dns, el->num_values, &old_p->guid, NULL);
2514 /* we don't delete it if we are re-adding it */
2518 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2519 invocation_id, seq_num, seq_num, now, 0, true);
2520 if (ret != LDB_SUCCESS) {
2521 talloc_free(tmp_ctx);
2526 /* for each new value, either update its meta-data, or add it
2529 for (i=0; i<el->num_values; i++) {
2530 struct parsed_dn *p = &dns[i], *old_p;
2533 (old_p = parsed_dn_find(old_dns,
2534 old_num_values, &p->guid, NULL)) != NULL) {
2535 /* update in place */
2536 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2537 old_p->dsdb_dn, invocation_id,
2538 seq_num, seq_num, now, 0, false);
2539 if (ret != LDB_SUCCESS) {
2540 talloc_free(tmp_ctx);
2545 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2547 if (new_values == NULL) {
2548 ldb_module_oom(module);
2549 talloc_free(tmp_ctx);
2550 return LDB_ERR_OPERATIONS_ERROR;
2552 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2553 invocation_id, seq_num, seq_num, now, 0, false);
2554 if (ret != LDB_SUCCESS) {
2555 talloc_free(tmp_ctx);
2561 ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, false);
2562 if (ret != LDB_SUCCESS) {
2563 talloc_free(tmp_ctx);
2568 /* add the new values to the end of old_el */
2569 if (num_new_values != 0) {
2570 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2571 struct ldb_val, old_num_values+num_new_values);
2572 if (el->values == NULL) {
2573 ldb_module_oom(module);
2574 return LDB_ERR_OPERATIONS_ERROR;
2576 memcpy(&el->values[old_num_values], &new_values[0],
2577 sizeof(struct ldb_val)*num_new_values);
2578 el->num_values = old_num_values + num_new_values;
2579 talloc_steal(msg->elements, new_values);
2581 el->values = old_el->values;
2582 el->num_values = old_el->num_values;
2583 talloc_steal(msg->elements, el->values);
2586 talloc_free(tmp_ctx);
2588 /* we now tell the backend to replace all existing values
2589 with the one we have constructed */
2590 el->flags = LDB_FLAG_MOD_REPLACE;
2597 handle linked attributes in modify requests
2599 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2600 struct ldb_message *msg,
2601 uint64_t seq_num, time_t t,
2602 struct ldb_request *parent)
2604 struct ldb_result *res;
2607 struct ldb_context *ldb = ldb_module_get_ctx(module);
2608 struct ldb_message *old_msg;
2610 const struct dsdb_schema *schema;
2611 struct GUID old_guid;
2613 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2615 * Nothing special is required for modifying or vanishing links
2616 * in fl2000 since they are just strings in a multi-valued
2619 struct ldb_control *ctrl = ldb_request_get_control(parent,
2620 DSDB_CONTROL_REPLMD_VANISH_LINKS);
2622 ctrl->critical = false;
2630 * We should restrict this to the intersection of the list of
2631 * linked attributes in the schema and the list of attributes
2634 * This will help performance a little, as otherwise we have
2635 * to allocate the entire object value-by-value.
2637 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2638 DSDB_FLAG_NEXT_MODULE |
2639 DSDB_SEARCH_SHOW_RECYCLED |
2640 DSDB_SEARCH_REVEAL_INTERNALS |
2641 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2643 if (ret != LDB_SUCCESS) {
2646 schema = dsdb_get_schema(ldb, res);
2648 return LDB_ERR_OPERATIONS_ERROR;
2651 old_msg = res->msgs[0];
2653 old_guid = samdb_result_guid(old_msg, "objectGUID");
2655 for (i=0; i<msg->num_elements; i++) {
2656 struct ldb_message_element *el = &msg->elements[i];
2657 struct ldb_message_element *old_el, *new_el;
2658 const struct dsdb_attribute *schema_attr
2659 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2661 ldb_asprintf_errstring(ldb,
2662 "%s: attribute %s is not a valid attribute in schema",
2663 __FUNCTION__, el->name);
2664 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2666 if (schema_attr->linkID == 0) {
2669 if ((schema_attr->linkID & 1) == 1) {
2670 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2673 /* Odd is for the target. Illegal to modify */
2674 ldb_asprintf_errstring(ldb,
2675 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2676 return LDB_ERR_UNWILLING_TO_PERFORM;
2678 old_el = ldb_msg_find_element(old_msg, el->name);
2679 switch (el->flags & LDB_FLAG_MOD_MASK) {
2680 case LDB_FLAG_MOD_REPLACE:
2681 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2683 case LDB_FLAG_MOD_DELETE:
2684 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2686 case LDB_FLAG_MOD_ADD:
2687 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2690 ldb_asprintf_errstring(ldb,
2691 "invalid flags 0x%x for %s linked attribute",
2692 el->flags, el->name);
2693 return LDB_ERR_UNWILLING_TO_PERFORM;
2695 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2696 ldb_asprintf_errstring(ldb,
2697 "Attribute %s is single valued but more than one value has been supplied",
2699 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2701 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2706 if (ret != LDB_SUCCESS) {
2710 ldb_msg_remove_attr(old_msg, el->name);
2712 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2713 new_el->num_values = el->num_values;
2714 new_el->values = talloc_steal(msg->elements, el->values);
2716 /* TODO: this relises a bit too heavily on the exact
2717 behaviour of ldb_msg_find_element and
2718 ldb_msg_remove_element */
2719 old_el = ldb_msg_find_element(msg, el->name);
2721 ldb_msg_remove_element(msg, old_el);
2732 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2734 struct ldb_context *ldb;
2735 struct replmd_replicated_request *ac;
2736 struct ldb_request *down_req;
2737 struct ldb_message *msg;
2738 time_t t = time(NULL);
2740 bool is_urgent = false, rodc = false;
2741 bool is_schema_nc = false;
2742 unsigned int functional_level;
2743 const struct ldb_message_element *guid_el = NULL;
2744 struct ldb_control *sd_propagation_control;
2745 struct replmd_private *replmd_private =
2746 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2748 /* do not manipulate our control entries */
2749 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2750 return ldb_next_request(module, req);
2753 sd_propagation_control = ldb_request_get_control(req,
2754 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2755 if (sd_propagation_control != NULL) {
2756 if (req->op.mod.message->num_elements != 1) {
2757 return ldb_module_operr(module);
2759 ret = strcmp(req->op.mod.message->elements[0].name,
2760 "nTSecurityDescriptor");
2762 return ldb_module_operr(module);
2765 return ldb_next_request(module, req);
2768 ldb = ldb_module_get_ctx(module);
2770 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2772 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2773 if (guid_el != NULL) {
2774 ldb_set_errstring(ldb,
2775 "replmd_modify: it's not allowed to change the objectGUID!");
2776 return LDB_ERR_CONSTRAINT_VIOLATION;
2779 ac = replmd_ctx_init(module, req);
2781 return ldb_module_oom(module);
2784 functional_level = dsdb_functional_level(ldb);
2786 /* we have to copy the message as the caller might have it as a const */
2787 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2791 return LDB_ERR_OPERATIONS_ERROR;
2794 ldb_msg_remove_attr(msg, "whenChanged");
2795 ldb_msg_remove_attr(msg, "uSNChanged");
2797 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2799 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2800 msg, &ac->seq_num, t, is_schema_nc,
2802 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2803 struct loadparm_context *lp_ctx;
2806 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2807 struct loadparm_context);
2809 referral = talloc_asprintf(req,
2811 lpcfg_dnsdomain(lp_ctx),
2812 ldb_dn_get_linearized(msg->dn));
2813 ret = ldb_module_send_referral(req, referral);
2818 if (ret != LDB_SUCCESS) {
2823 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2824 if (ret != LDB_SUCCESS) {
2830 * - replace the old object with the newly constructed one
2833 ac->is_urgent = is_urgent;
2835 ret = ldb_build_mod_req(&down_req, ldb, ac,
2838 ac, replmd_op_callback,
2840 LDB_REQ_SET_LOCATION(down_req);
2841 if (ret != LDB_SUCCESS) {
2846 /* current partition control is needed by "replmd_op_callback" */
2847 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2848 ret = ldb_request_add_control(down_req,
2849 DSDB_CONTROL_CURRENT_PARTITION_OID,
2851 if (ret != LDB_SUCCESS) {
2857 /* If we are in functional level 2000, then
2858 * replmd_modify_handle_linked_attribs will have done
2860 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2861 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2862 if (ret != LDB_SUCCESS) {
2868 talloc_steal(down_req, msg);
2870 /* we only change whenChanged and uSNChanged if the seq_num
2872 if (ac->seq_num != 0) {
2873 ret = add_time_element(msg, "whenChanged", t);
2874 if (ret != LDB_SUCCESS) {
2880 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2881 if (ret != LDB_SUCCESS) {
2888 /* go on with the call chain */
2889 return ldb_next_request(module, down_req);
2892 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2895 handle a rename request
2897 On a rename we need to do an extra ldb_modify which sets the
2898 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2900 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2902 struct ldb_context *ldb;
2903 struct replmd_replicated_request *ac;
2905 struct ldb_request *down_req;
2907 /* do not manipulate our control entries */
2908 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2909 return ldb_next_request(module, req);
2912 ldb = ldb_module_get_ctx(module);
2914 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2916 ac = replmd_ctx_init(module, req);
2918 return ldb_module_oom(module);
2921 ret = ldb_build_rename_req(&down_req, ldb, ac,
2922 ac->req->op.rename.olddn,
2923 ac->req->op.rename.newdn,
2925 ac, replmd_rename_callback,
2927 LDB_REQ_SET_LOCATION(down_req);
2928 if (ret != LDB_SUCCESS) {
2933 /* go on with the call chain */
2934 return ldb_next_request(module, down_req);
2937 /* After the rename is compleated, update the whenchanged etc */
2938 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2940 struct ldb_context *ldb;
2941 struct ldb_request *down_req;
2942 struct ldb_message *msg;
2943 const struct dsdb_attribute *rdn_attr;
2944 const char *rdn_name;
2945 const struct ldb_val *rdn_val;
2946 const char *attrs[5] = { NULL, };
2947 time_t t = time(NULL);
2949 bool is_urgent = false, rodc = false;
2951 struct replmd_replicated_request *ac =
2952 talloc_get_type(req->context, struct replmd_replicated_request);
2953 struct replmd_private *replmd_private =
2954 talloc_get_type(ldb_module_get_private(ac->module),
2955 struct replmd_private);
2957 ldb = ldb_module_get_ctx(ac->module);
2959 if (ares->error != LDB_SUCCESS) {
2960 return ldb_module_done(ac->req, ares->controls,
2961 ares->response, ares->error);
2964 if (ares->type != LDB_REPLY_DONE) {
2965 ldb_set_errstring(ldb,
2966 "invalid ldb_reply_type in callback");
2968 return ldb_module_done(ac->req, NULL, NULL,
2969 LDB_ERR_OPERATIONS_ERROR);
2973 * - replace the old object with the newly constructed one
2976 msg = ldb_msg_new(ac);
2979 return LDB_ERR_OPERATIONS_ERROR;
2982 msg->dn = ac->req->op.rename.newdn;
2984 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2986 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2987 if (rdn_name == NULL) {
2989 return ldb_module_done(ac->req, NULL, NULL,
2993 /* normalize the rdn attribute name */
2994 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2995 if (rdn_attr == NULL) {
2997 return ldb_module_done(ac->req, NULL, NULL,
3000 rdn_name = rdn_attr->lDAPDisplayName;
3002 rdn_val = ldb_dn_get_rdn_val(msg->dn);
3003 if (rdn_val == NULL) {
3005 return ldb_module_done(ac->req, NULL, NULL,
3009 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3011 return ldb_module_done(ac->req, NULL, NULL,
3014 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3016 return ldb_module_done(ac->req, NULL, NULL,
3019 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3021 return ldb_module_done(ac->req, NULL, NULL,
3024 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3026 return ldb_module_done(ac->req, NULL, NULL,
3031 * here we let replmd_update_rpmd() only search for
3032 * the existing "replPropertyMetaData" and rdn_name attributes.
3034 * We do not want the existing "name" attribute as
3035 * the "name" attribute needs to get the version
3036 * updated on rename even if the rdn value hasn't changed.
3038 * This is the diff of the meta data, for a moved user
3039 * on a w2k8r2 server:
3042 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3043 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3044 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3045 * version : 0x00000001 (1)
3046 * reserved : 0x00000000 (0)
3047 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3048 * local_usn : 0x00000000000037a5 (14245)
3049 * array: struct replPropertyMetaData1
3050 * attid : DRSUAPI_ATTID_name (0x90001)
3051 * - version : 0x00000001 (1)
3052 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3053 * + version : 0x00000002 (2)
3054 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3055 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3056 * - originating_usn : 0x00000000000037a5 (14245)
3057 * - local_usn : 0x00000000000037a5 (14245)
3058 * + originating_usn : 0x0000000000003834 (14388)
3059 * + local_usn : 0x0000000000003834 (14388)
3060 * array: struct replPropertyMetaData1
3061 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3062 * version : 0x00000004 (4)
3064 attrs[0] = "replPropertyMetaData";
3065 attrs[1] = "objectClass";
3066 attrs[2] = "instanceType";
3067 attrs[3] = rdn_name;
3070 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3071 msg, &ac->seq_num, t,
3072 is_schema_nc, &is_urgent, &rodc);
3073 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3074 struct ldb_dn *olddn = ac->req->op.rename.olddn;
3075 struct loadparm_context *lp_ctx;
3078 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3079 struct loadparm_context);
3081 referral = talloc_asprintf(req,
3083 lpcfg_dnsdomain(lp_ctx),
3084 ldb_dn_get_linearized(olddn));
3085 ret = ldb_module_send_referral(req, referral);
3087 return ldb_module_done(req, NULL, NULL, ret);
3090 if (ret != LDB_SUCCESS) {
3092 return ldb_module_done(ac->req, NULL, NULL, ret);
3095 if (ac->seq_num == 0) {
3097 return ldb_module_done(ac->req, NULL, NULL,
3099 "internal error seq_num == 0"));
3101 ac->is_urgent = is_urgent;
3103 ret = ldb_build_mod_req(&down_req, ldb, ac,
3106 ac, replmd_op_callback,
3108 LDB_REQ_SET_LOCATION(down_req);
3109 if (ret != LDB_SUCCESS) {
3114 /* current partition control is needed by "replmd_op_callback" */
3115 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3116 ret = ldb_request_add_control(down_req,
3117 DSDB_CONTROL_CURRENT_PARTITION_OID,
3119 if (ret != LDB_SUCCESS) {
3125 talloc_steal(down_req, msg);
3127 ret = add_time_element(msg, "whenChanged", t);
3128 if (ret != LDB_SUCCESS) {
3134 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3135 if (ret != LDB_SUCCESS) {
3141 /* go on with the call chain - do the modify after the rename */
3142 return ldb_next_request(ac->module, down_req);
3146 * remove links from objects that point at this object when an object
3147 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3148 * RemoveObj which states that link removal due to the object being
3149 * deleted is NOT an originating update - they just go away!
3152 static int replmd_delete_remove_link(struct ldb_module *module,
3153 const struct dsdb_schema *schema,
3155 struct ldb_message_element *el,
3156 const struct dsdb_attribute *sa,
3157 struct ldb_request *parent)
3160 TALLOC_CTX *tmp_ctx = talloc_new(module);
3161 struct ldb_context *ldb = ldb_module_get_ctx(module);
3163 for (i=0; i<el->num_values; i++) {
3164 struct dsdb_dn *dsdb_dn;
3168 struct ldb_message *msg;
3169 const struct dsdb_attribute *target_attr;
3170 struct ldb_message_element *el2;
3171 struct ldb_val dn_val;
3172 uint32_t dsdb_flags = 0;
3174 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3178 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3180 talloc_free(tmp_ctx);
3181 return LDB_ERR_OPERATIONS_ERROR;
3184 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
3185 if (!NT_STATUS_IS_OK(status)) {
3186 talloc_free(tmp_ctx);
3187 return LDB_ERR_OPERATIONS_ERROR;
3190 /* remove the link */
3191 msg = ldb_msg_new(tmp_ctx);
3193 ldb_module_oom(module);
3194 talloc_free(tmp_ctx);
3195 return LDB_ERR_OPERATIONS_ERROR;
3199 msg->dn = dsdb_dn->dn;
3201 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3202 if (target_attr == NULL) {
3206 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
3207 if (ret != LDB_SUCCESS) {
3208 ldb_module_oom(module);
3209 talloc_free(tmp_ctx);
3210 return LDB_ERR_OPERATIONS_ERROR;
3212 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
3213 el2->values = &dn_val;
3214 el2->num_values = 1;
3217 * Ensure that we tell the modification to vanish any linked
3218 * attributes (not simply mark them as isDeleted = TRUE)
3220 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3222 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3223 if (ret != LDB_SUCCESS) {
3224 talloc_free(tmp_ctx);
3228 talloc_free(tmp_ctx);
3234 handle update of replication meta data for deletion of objects
3236 This also handles the mapping of delete to a rename operation
3237 to allow deletes to be replicated.
3239 It also handles the incoming deleted objects, to ensure they are
3240 fully deleted here. In that case re_delete is true, and we do not
3241 use this as a signal to change the deleted state, just reinforce it.
3244 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3246 int ret = LDB_ERR_OTHER;
3247 bool retb, disallow_move_on_delete;
3248 struct ldb_dn *old_dn, *new_dn;
3249 const char *rdn_name;
3250 const struct ldb_val *rdn_value, *new_rdn_value;
3252 struct ldb_context *ldb = ldb_module_get_ctx(module);
3253 const struct dsdb_schema *schema;
3254 struct ldb_message *msg, *old_msg;
3255 struct ldb_message_element *el;
3256 TALLOC_CTX *tmp_ctx;
3257 struct ldb_result *res, *parent_res;
3258 static const char * const preserved_attrs[] = {
3259 /* yes, this really is a hard coded list. See MS-ADTS
3260 section 3.1.1.5.5.1.1 */
3263 "dNReferenceUpdate",
3274 "msDS-LastKnownRDN",
3280 "distinguishedName",
3284 "proxiedObjectName",
3286 "nTSecurityDescriptor",
3287 "replPropertyMetaData",
3289 "securityIdentifier",
3297 "userAccountControl",
3304 static const char * const all_attrs[] = {
3305 DSDB_SECRET_ATTRIBUTES,
3309 unsigned int i, el_count = 0;
3310 uint32_t dsdb_flags = 0;
3311 enum deletion_state deletion_state, next_deletion_state;
3313 if (ldb_dn_is_special(req->op.del.dn)) {
3314 return ldb_next_request(module, req);
3318 * We have to allow dbcheck to remove an object that
3319 * is beyond repair, and to do so totally. This could
3320 * mean we we can get a partial object from the other
3321 * DC, causing havoc, so dbcheck suggests
3322 * re-replication first. dbcheck sets both DBCHECK
3323 * and RELAX in this situation.
3325 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3326 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3327 /* really, really remove it */
3328 return ldb_next_request(module, req);
3331 tmp_ctx = talloc_new(ldb);
3334 return LDB_ERR_OPERATIONS_ERROR;
3337 schema = dsdb_get_schema(ldb, tmp_ctx);
3339 talloc_free(tmp_ctx);
3340 return LDB_ERR_OPERATIONS_ERROR;
3343 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3345 /* we need the complete msg off disk, so we can work out which
3346 attributes need to be removed */
3347 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3348 DSDB_FLAG_NEXT_MODULE |
3349 DSDB_SEARCH_SHOW_RECYCLED |
3350 DSDB_SEARCH_REVEAL_INTERNALS |
3351 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3352 if (ret != LDB_SUCCESS) {
3353 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3354 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3355 re_delete ? "re-delete" : "delete",
3356 ldb_dn_get_linearized(old_dn),
3357 ldb_errstring(ldb_module_get_ctx(module)));
3358 talloc_free(tmp_ctx);
3361 old_msg = res->msgs[0];
3363 replmd_deletion_state(module, old_msg,
3365 &next_deletion_state);
3367 /* This supports us noticing an incoming isDeleted and acting on it */
3369 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3370 next_deletion_state = deletion_state;
3373 if (next_deletion_state == OBJECT_REMOVED) {
3375 * We have to prevent objects being deleted, even if
3376 * the administrator really wants them gone, as
3377 * without the tombstone, we can get a partial object
3378 * from the other DC, causing havoc.
3380 * The only other valid case is when the 180 day
3381 * timeout has expired, when relax is specified.
3383 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3384 /* it is already deleted - really remove it this time */
3385 talloc_free(tmp_ctx);
3386 return ldb_next_request(module, req);
3389 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3390 "This check is to prevent corruption of the replicated state.",
3391 ldb_dn_get_linearized(old_msg->dn));
3392 return LDB_ERR_UNWILLING_TO_PERFORM;
3395 rdn_name = ldb_dn_get_rdn_name(old_dn);
3396 rdn_value = ldb_dn_get_rdn_val(old_dn);
3397 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3398 talloc_free(tmp_ctx);
3399 return ldb_operr(ldb);
3402 msg = ldb_msg_new(tmp_ctx);
3404 ldb_module_oom(module);
3405 talloc_free(tmp_ctx);
3406 return LDB_ERR_OPERATIONS_ERROR;
3411 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3412 disallow_move_on_delete =
3413 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3414 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3416 /* work out where we will be renaming this object to */
3417 if (!disallow_move_on_delete) {
3418 struct ldb_dn *deleted_objects_dn;
3419 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3420 &deleted_objects_dn);
3423 * We should not move objects if we can't find the
3424 * deleted objects DN. Not moving (or otherwise
3425 * harming) the Deleted Objects DN itself is handled
3428 if (re_delete && (ret != LDB_SUCCESS)) {
3429 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3430 if (new_dn == NULL) {
3431 ldb_module_oom(module);
3432 talloc_free(tmp_ctx);
3433 return LDB_ERR_OPERATIONS_ERROR;
3435 } else if (ret != LDB_SUCCESS) {
3436 /* this is probably an attempted delete on a partition
3437 * that doesn't allow delete operations, such as the
3438 * schema partition */
3439 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3440 ldb_dn_get_linearized(old_dn));
3441 talloc_free(tmp_ctx);
3442 return LDB_ERR_UNWILLING_TO_PERFORM;
3444 new_dn = deleted_objects_dn;
3447 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3448 if (new_dn == NULL) {
3449 ldb_module_oom(module);
3450 talloc_free(tmp_ctx);
3451 return LDB_ERR_OPERATIONS_ERROR;
3455 if (deletion_state == OBJECT_NOT_DELETED) {
3456 /* get the objects GUID from the search we just did */
3457 guid = samdb_result_guid(old_msg, "objectGUID");
3459 /* Add a formatted child */
3460 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3462 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3463 GUID_string(tmp_ctx, &guid));
3465 ldb_asprintf_errstring(ldb, __location__
3466 ": Unable to add a formatted child to dn: %s",
3467 ldb_dn_get_linearized(new_dn));
3468 talloc_free(tmp_ctx);
3469 return LDB_ERR_OPERATIONS_ERROR;
3472 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3473 if (ret != LDB_SUCCESS) {
3474 ldb_asprintf_errstring(ldb, __location__
3475 ": Failed to add isDeleted string to the msg");
3476 talloc_free(tmp_ctx);
3479 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3482 * No matter what has happened with other renames etc, try again to
3483 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3486 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3487 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3489 ldb_asprintf_errstring(ldb, __location__
3490 ": Unable to add a prepare rdn of %s",
3491 ldb_dn_get_linearized(rdn));
3492 talloc_free(tmp_ctx);
3493 return LDB_ERR_OPERATIONS_ERROR;
3495 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3497 retb = ldb_dn_add_child(new_dn, rdn);
3499 ldb_asprintf_errstring(ldb, __location__
3500 ": Unable to add rdn %s to base dn: %s",
3501 ldb_dn_get_linearized(rdn),
3502 ldb_dn_get_linearized(new_dn));
3503 talloc_free(tmp_ctx);
3504 return LDB_ERR_OPERATIONS_ERROR;
3509 now we need to modify the object in the following ways:
3511 - add isDeleted=TRUE
3512 - update rDN and name, with new rDN
3513 - remove linked attributes
3514 - remove objectCategory and sAMAccountType
3515 - remove attribs not on the preserved list
3516 - preserved if in above list, or is rDN
3517 - remove all linked attribs from this object
3518 - remove all links from other objects to this object
3519 - add lastKnownParent
3520 - update replPropertyMetaData?
3522 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3525 if (deletion_state == OBJECT_NOT_DELETED) {
3526 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3527 char *parent_dn_str = NULL;
3529 /* we need the storage form of the parent GUID */
3530 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3532 DSDB_FLAG_NEXT_MODULE |
3533 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3534 DSDB_SEARCH_REVEAL_INTERNALS|
3535 DSDB_SEARCH_SHOW_RECYCLED, req);
3536 if (ret != LDB_SUCCESS) {
3537 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3538 "repmd_delete: Failed to %s %s, "
3539 "because we failed to find it's parent (%s): %s",
3540 re_delete ? "re-delete" : "delete",
3541 ldb_dn_get_linearized(old_dn),
3542 ldb_dn_get_linearized(parent_dn),
3543 ldb_errstring(ldb_module_get_ctx(module)));
3544 talloc_free(tmp_ctx);
3549 * Now we can use the DB version,
3550 * it will have the extended DN info in it
3552 parent_dn = parent_res->msgs[0]->dn;
3553 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3556 if (parent_dn_str == NULL) {
3557 talloc_free(tmp_ctx);
3558 return ldb_module_oom(module);
3561 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3563 if (ret != LDB_SUCCESS) {
3564 ldb_asprintf_errstring(ldb, __location__
3565 ": Failed to add lastKnownParent "
3566 "string when deleting %s",
3567 ldb_dn_get_linearized(old_dn));
3568 talloc_free(tmp_ctx);
3571 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3573 if (next_deletion_state == OBJECT_DELETED) {
3574 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3575 if (ret != LDB_SUCCESS) {
3576 ldb_asprintf_errstring(ldb, __location__
3577 ": Failed to add msDS-LastKnownRDN "
3578 "string when deleting %s",
3579 ldb_dn_get_linearized(old_dn));
3580 talloc_free(tmp_ctx);
3583 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3587 switch (next_deletion_state) {
3589 case OBJECT_RECYCLED:
3590 case OBJECT_TOMBSTONE:
3593 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3594 * describes what must be removed from a tombstone
3597 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3598 * describes what must be removed from a recycled
3604 * we also mark it as recycled, meaning this object can't be
3605 * recovered (we are stripping its attributes).
3606 * This is done only if we have this schema object of course ...
3607 * This behavior is identical to the one of Windows 2008R2 which
3608 * always set the isRecycled attribute, even if the recycle-bin is
3609 * not activated and what ever the forest level is.
3611 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3612 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3613 if (ret != LDB_SUCCESS) {
3614 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3615 ldb_module_oom(module);
3616 talloc_free(tmp_ctx);
3619 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3622 /* work out which of the old attributes we will be removing */
3623 for (i=0; i<old_msg->num_elements; i++) {
3624 const struct dsdb_attribute *sa;
3625 el = &old_msg->elements[i];
3626 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3628 talloc_free(tmp_ctx);
3629 return LDB_ERR_OPERATIONS_ERROR;
3631 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3632 /* don't remove the rDN */
3635 if (sa->linkID && (sa->linkID & 1)) {
3637 we have a backlink in this object
3638 that needs to be removed. We're not
3639 allowed to remove it directly
3640 however, so we instead setup a
3641 modify to delete the corresponding
3644 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3645 if (ret != LDB_SUCCESS) {
3646 const char *old_dn_str
3647 = ldb_dn_get_linearized(old_dn);
3648 ldb_asprintf_errstring(ldb,
3650 ": Failed to remove backlink of "
3651 "%s when deleting %s: %s",
3654 ldb_errstring(ldb));
3655 talloc_free(tmp_ctx);
3656 return LDB_ERR_OPERATIONS_ERROR;
3658 /* now we continue, which means we
3659 won't remove this backlink
3665 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3668 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3673 * Ensure that we tell the modification to vanish any linked
3674 * attributes (not simply mark them as isDeleted = TRUE)
3676 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3678 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3679 if (ret != LDB_SUCCESS) {
3680 talloc_free(tmp_ctx);
3681 ldb_module_oom(module);
3688 case OBJECT_DELETED:
3690 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3691 * describes what must be removed from a deleted
3695 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3696 if (ret != LDB_SUCCESS) {
3697 talloc_free(tmp_ctx);
3698 ldb_module_oom(module);
3702 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3703 if (ret != LDB_SUCCESS) {
3704 talloc_free(tmp_ctx);
3705 ldb_module_oom(module);
3715 if (deletion_state == OBJECT_NOT_DELETED) {
3716 const struct dsdb_attribute *sa;
3718 /* work out what the new rdn value is, for updating the
3719 rDN and name fields */
3720 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3721 if (new_rdn_value == NULL) {
3722 talloc_free(tmp_ctx);
3723 return ldb_operr(ldb);
3726 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3728 talloc_free(tmp_ctx);
3729 return LDB_ERR_OPERATIONS_ERROR;
3732 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3734 if (ret != LDB_SUCCESS) {
3735 talloc_free(tmp_ctx);
3738 el->flags = LDB_FLAG_MOD_REPLACE;
3740 el = ldb_msg_find_element(old_msg, "name");
3742 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3743 if (ret != LDB_SUCCESS) {
3744 talloc_free(tmp_ctx);
3747 el->flags = LDB_FLAG_MOD_REPLACE;
3752 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3757 * No matter what has happned with other renames, try again to
3758 * get this to be under the deleted DN.
3760 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3761 /* now rename onto the new DN */
3762 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3763 if (ret != LDB_SUCCESS){
3764 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3765 ldb_dn_get_linearized(old_dn),
3766 ldb_dn_get_linearized(new_dn),
3767 ldb_errstring(ldb)));
3768 talloc_free(tmp_ctx);
3774 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
3775 if (ret != LDB_SUCCESS) {
3776 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3777 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3778 talloc_free(tmp_ctx);
3782 talloc_free(tmp_ctx);
3784 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3787 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3789 return replmd_delete_internals(module, req, false);
3793 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3798 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3800 int ret = LDB_ERR_OTHER;
3801 /* TODO: do some error mapping */
3803 /* Let the caller know the full WERROR */
3804 ar->objs->error = status;
3810 static struct replPropertyMetaData1 *
3811 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3812 enum drsuapi_DsAttributeId attid)
3815 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3817 for (i = 0; i < rpmd_ctr->count; i++) {
3818 if (rpmd_ctr->array[i].attid == attid) {
3819 return &rpmd_ctr->array[i];
3827 return true if an update is newer than an existing entry
3828 see section 5.11 of MS-ADTS
3830 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3831 const struct GUID *update_invocation_id,
3832 uint32_t current_version,
3833 uint32_t update_version,
3834 NTTIME current_change_time,
3835 NTTIME update_change_time)
3837 if (update_version != current_version) {
3838 return update_version > current_version;
3840 if (update_change_time != current_change_time) {
3841 return update_change_time > current_change_time;
3843 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3846 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3847 struct replPropertyMetaData1 *new_m)
3849 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3850 &new_m->originating_invocation_id,
3853 cur_m->originating_change_time,
3854 new_m->originating_change_time);
3857 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
3858 struct replPropertyMetaData1 *cur_m,
3859 struct replPropertyMetaData1 *new_m)
3864 * If the new replPropertyMetaData entry for this attribute is
3865 * not provided (this happens in the case where we look for
3866 * ATTID_name, but the name was not changed), then the local
3867 * state is clearly still current, as the remote
3868 * server didn't send it due to being older the high watermark
3871 if (new_m == NULL) {
3875 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3877 * if we compare equal then do an
3878 * update. This is used when a client
3879 * asks for a FULL_SYNC, and can be
3880 * used to recover a corrupt
3883 * This call is a bit tricky, what we
3884 * are doing it turning the 'is_newer'
3885 * call into a 'not is older' by
3886 * swapping cur_m and new_m, and negating the
3889 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
3892 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
3902 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3904 const struct ldb_val *rdn_val;
3905 const char *rdn_name;
3906 struct ldb_dn *new_dn;
3908 rdn_val = ldb_dn_get_rdn_val(dn);
3909 rdn_name = ldb_dn_get_rdn_name(dn);
3910 if (!rdn_val || !rdn_name) {
3914 new_dn = ldb_dn_copy(mem_ctx, dn);
3919 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3923 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3925 ldb_dn_escape_value(new_dn, *rdn_val),
3926 GUID_string(new_dn, guid))) {
3935 perform a modify operation which sets the rDN and name attributes to
3936 their current values. This has the effect of changing these
3937 attributes to have been last updated by the current DC. This is
3938 needed to ensure that renames performed as part of conflict
3939 resolution are propogated to other DCs
3941 static int replmd_name_modify(struct replmd_replicated_request *ar,
3942 struct ldb_request *req, struct ldb_dn *dn)
3944 struct ldb_message *msg;
3945 const char *rdn_name;
3946 const struct ldb_val *rdn_val;
3947 const struct dsdb_attribute *rdn_attr;
3950 msg = ldb_msg_new(req);
3956 rdn_name = ldb_dn_get_rdn_name(dn);
3957 if (rdn_name == NULL) {
3961 /* normalize the rdn attribute name */
3962 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3963 if (rdn_attr == NULL) {
3966 rdn_name = rdn_attr->lDAPDisplayName;
3968 rdn_val = ldb_dn_get_rdn_val(dn);
3969 if (rdn_val == NULL) {
3973 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3976 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3979 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3982 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3986 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3987 if (ret != LDB_SUCCESS) {
3988 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3989 ldb_dn_get_linearized(dn),
3990 ldb_errstring(ldb_module_get_ctx(ar->module))));
4000 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
4001 ldb_dn_get_linearized(dn)));
4002 return LDB_ERR_OPERATIONS_ERROR;
4007 callback for conflict DN handling where we have renamed the incoming
4008 record. After renaming it, we need to ensure the change of name and
4009 rDN for the incoming record is seen as an originating update by this DC.
4011 This also handles updating lastKnownParent for entries sent to lostAndFound
4013 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
4015 struct replmd_replicated_request *ar =
4016 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4017 struct ldb_dn *conflict_dn = NULL;
4020 if (ares->error != LDB_SUCCESS) {
4021 /* call the normal callback for everything except success */
4022 return replmd_op_callback(req, ares);
4025 switch (req->operation) {
4027 conflict_dn = req->op.add.message->dn;
4030 conflict_dn = req->op.mod.message->dn;
4033 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4036 /* perform a modify of the rDN and name of the record */
4037 ret = replmd_name_modify(ar, req, conflict_dn);
4038 if (ret != LDB_SUCCESS) {
4040 return replmd_op_callback(req, ares);
4043 if (ar->objs->objects[ar->index_current].last_known_parent) {
4044 struct ldb_message *msg = ldb_msg_new(req);
4046 ldb_module_oom(ar->module);
4047 return LDB_ERR_OPERATIONS_ERROR;
4050 msg->dn = req->op.add.message->dn;
4052 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4053 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4054 if (ret != LDB_SUCCESS) {
4055 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4056 ldb_module_oom(ar->module);
4059 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4061 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4062 if (ret != LDB_SUCCESS) {
4063 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4064 ldb_dn_get_linearized(msg->dn),
4065 ldb_errstring(ldb_module_get_ctx(ar->module))));
4071 return replmd_op_callback(req, ares);
4075 callback for replmd_replicated_apply_add()
4076 This copes with the creation of conflict records in the case where
4077 the DN exists, but with a different objectGUID
4079 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))
4081 struct ldb_dn *conflict_dn;
4082 struct replmd_replicated_request *ar =
4083 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4084 struct ldb_result *res;
4085 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4087 const struct ldb_val *omd_value;
4088 struct replPropertyMetaDataBlob omd, *rmd;
4089 enum ndr_err_code ndr_err;
4090 bool rename_incoming_record, rodc;
4091 struct replPropertyMetaData1 *rmd_name, *omd_name;
4092 struct ldb_message *msg;
4093 struct ldb_request *down_req = NULL;
4095 /* call the normal callback for success */
4096 if (ares->error == LDB_SUCCESS) {
4097 return callback(req, ares);
4101 * we have a conflict, and need to decide if we will keep the
4102 * new record or the old record
4105 msg = ar->objs->objects[ar->index_current].msg;
4106 conflict_dn = msg->dn;
4108 /* For failures other than conflicts, fail the whole operation here */
4109 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4110 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4111 ldb_dn_get_linearized(conflict_dn),
4112 ldb_errstring(ldb_module_get_ctx(ar->module)));
4114 return ldb_module_done(ar->req, NULL, NULL,
4115 LDB_ERR_OPERATIONS_ERROR);
4118 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4119 if (ret != LDB_SUCCESS) {
4120 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)));
4121 return ldb_module_done(ar->req, NULL, NULL,
4122 LDB_ERR_OPERATIONS_ERROR);
4128 * We are on an RODC, or were a GC for this
4129 * partition, so we have to fail this until
4130 * someone who owns the partition sorts it
4133 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4134 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4135 " - We must fail the operation until a master for this partition resolves the conflict",
4136 ldb_dn_get_linearized(conflict_dn));
4141 * first we need the replPropertyMetaData attribute from the
4142 * local, conflicting record
4144 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4146 DSDB_FLAG_NEXT_MODULE |
4147 DSDB_SEARCH_SHOW_DELETED |
4148 DSDB_SEARCH_SHOW_RECYCLED, req);
4149 if (ret != LDB_SUCCESS) {
4150 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4151 ldb_dn_get_linearized(conflict_dn)));
4155 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4156 if (omd_value == NULL) {
4157 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4158 ldb_dn_get_linearized(conflict_dn)));
4162 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4163 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4164 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4165 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4166 ldb_dn_get_linearized(conflict_dn)));
4170 rmd = ar->objs->objects[ar->index_current].meta_data;
4173 * we decide which is newer based on the RPMD on the name
4174 * attribute. See [MS-DRSR] ResolveNameConflict.
4176 * We expect omd_name to be present, as this is from a local
4177 * search, but while rmd_name should have been given to us by
4178 * the remote server, if it is missing we just prefer the
4180 * replmd_replPropertyMetaData1_new_should_be_taken()
4182 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4183 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4185 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4186 ldb_dn_get_linearized(conflict_dn)));
4191 * Should we preserve the current record, and so rename the
4192 * incoming record to be a conflict?
4194 rename_incoming_record
4195 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4196 omd_name, rmd_name);
4198 if (rename_incoming_record) {
4200 struct ldb_dn *new_dn;
4202 guid = samdb_result_guid(msg, "objectGUID");
4203 if (GUID_all_zero(&guid)) {
4204 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4205 ldb_dn_get_linearized(conflict_dn)));
4208 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4209 if (new_dn == NULL) {
4210 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4211 ldb_dn_get_linearized(conflict_dn)));
4215 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4216 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4218 /* re-submit the request, but with the new DN */
4219 callback = replmd_op_name_modify_callback;
4222 /* we are renaming the existing record */
4224 struct ldb_dn *new_dn;
4226 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4227 if (GUID_all_zero(&guid)) {
4228 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4229 ldb_dn_get_linearized(conflict_dn)));
4233 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4234 if (new_dn == NULL) {
4235 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4236 ldb_dn_get_linearized(conflict_dn)));
4240 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4241 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4243 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4244 DSDB_FLAG_OWN_MODULE, req);
4245 if (ret != LDB_SUCCESS) {
4246 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4247 ldb_dn_get_linearized(conflict_dn),
4248 ldb_dn_get_linearized(new_dn),
4249 ldb_errstring(ldb_module_get_ctx(ar->module))));
4254 * now we need to ensure that the rename is seen as an
4255 * originating update. We do that with a modify.
4257 ret = replmd_name_modify(ar, req, new_dn);
4258 if (ret != LDB_SUCCESS) {
4262 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4263 ldb_dn_get_linearized(req->op.add.message->dn)));
4266 ret = ldb_build_add_req(&down_req,
4267 ldb_module_get_ctx(ar->module),
4274 if (ret != LDB_SUCCESS) {
4277 LDB_REQ_SET_LOCATION(down_req);
4279 /* current partition control needed by "repmd_op_callback" */
4280 ret = ldb_request_add_control(down_req,
4281 DSDB_CONTROL_CURRENT_PARTITION_OID,
4283 if (ret != LDB_SUCCESS) {
4284 return replmd_replicated_request_error(ar, ret);
4287 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4288 /* this tells the partition module to make it a
4289 partial replica if creating an NC */
4290 ret = ldb_request_add_control(down_req,
4291 DSDB_CONTROL_PARTIAL_REPLICA,
4293 if (ret != LDB_SUCCESS) {
4294 return replmd_replicated_request_error(ar, ret);
4299 * Finally we re-run the add, otherwise the new record won't
4300 * exist, as we are here because of that exact failure!
4302 return ldb_next_request(ar->module, down_req);
4305 /* on failure make the caller get the error. This means
4306 * replication will stop with an error, but there is not much
4309 return ldb_module_done(ar->req, NULL, NULL,
4314 callback for replmd_replicated_apply_add()
4315 This copes with the creation of conflict records in the case where
4316 the DN exists, but with a different objectGUID
4318 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4320 struct replmd_replicated_request *ar =
4321 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4323 if (ar->objs->objects[ar->index_current].last_known_parent) {
4324 /* This is like a conflict DN, where we put the object in LostAndFound
4325 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4326 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4329 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4333 this is called when a new object comes in over DRS
4335 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4337 struct ldb_context *ldb;
4338 struct ldb_request *change_req;
4339 enum ndr_err_code ndr_err;
4340 struct ldb_message *msg;
4341 struct replPropertyMetaDataBlob *md;
4342 struct ldb_val md_value;
4345 bool remote_isDeleted = false;
4348 time_t t = time(NULL);
4349 const struct ldb_val *rdn_val;
4350 struct replmd_private *replmd_private =
4351 talloc_get_type(ldb_module_get_private(ar->module),
4352 struct replmd_private);
4353 unix_to_nt_time(&now, t);
4355 ldb = ldb_module_get_ctx(ar->module);
4356 msg = ar->objs->objects[ar->index_current].msg;
4357 md = ar->objs->objects[ar->index_current].meta_data;
4358 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4360 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4361 if (ret != LDB_SUCCESS) {
4362 return replmd_replicated_request_error(ar, ret);
4365 ret = dsdb_msg_add_guid(msg,
4366 &ar->objs->objects[ar->index_current].object_guid,
4368 if (ret != LDB_SUCCESS) {
4369 return replmd_replicated_request_error(ar, ret);
4372 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4373 if (ret != LDB_SUCCESS) {
4374 return replmd_replicated_request_error(ar, ret);
4377 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4378 if (ret != LDB_SUCCESS) {
4379 return replmd_replicated_request_error(ar, ret);
4382 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4383 if (ret != LDB_SUCCESS) {
4384 return replmd_replicated_request_error(ar, ret);
4387 /* remove any message elements that have zero values */
4388 for (i=0; i<msg->num_elements; i++) {
4389 struct ldb_message_element *el = &msg->elements[i];
4391 if (el->num_values == 0) {
4392 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4393 ldb_asprintf_errstring(ldb, __location__
4394 ": empty objectClass sent on %s, aborting replication\n",
4395 ldb_dn_get_linearized(msg->dn));
4396 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4399 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4401 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4402 msg->num_elements--;
4409 struct GUID_txt_buf guid_txt;
4411 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4412 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4413 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4418 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4419 "isDeleted", false);
4422 * the meta data array is already sorted by the caller, except
4423 * for the RDN, which needs to be added.
4427 rdn_val = ldb_dn_get_rdn_val(msg->dn);
4428 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
4429 md, ar, now, is_schema_nc);
4430 if (ret != LDB_SUCCESS) {
4431 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4432 return replmd_replicated_request_error(ar, ret);
4435 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
4436 if (ret != LDB_SUCCESS) {
4437 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4438 return replmd_replicated_request_error(ar, ret);
4441 for (i=0; i < md->ctr.ctr1.count; i++) {
4442 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4444 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4445 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4446 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4447 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4448 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4450 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4451 if (ret != LDB_SUCCESS) {
4452 return replmd_replicated_request_error(ar, ret);
4455 replmd_ldb_message_sort(msg, ar->schema);
4457 if (!remote_isDeleted) {
4458 ret = dsdb_module_schedule_sd_propagation(ar->module,
4459 ar->objs->partition_dn,
4461 if (ret != LDB_SUCCESS) {
4462 return replmd_replicated_request_error(ar, ret);
4466 ar->isDeleted = remote_isDeleted;
4468 ret = ldb_build_add_req(&change_req,
4474 replmd_op_add_callback,
4476 LDB_REQ_SET_LOCATION(change_req);
4477 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4479 /* current partition control needed by "repmd_op_callback" */
4480 ret = ldb_request_add_control(change_req,
4481 DSDB_CONTROL_CURRENT_PARTITION_OID,
4483 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4485 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4486 /* this tells the partition module to make it a
4487 partial replica if creating an NC */
4488 ret = ldb_request_add_control(change_req,
4489 DSDB_CONTROL_PARTIAL_REPLICA,
4491 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4494 return ldb_next_request(ar->module, change_req);
4497 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4498 struct ldb_reply *ares)
4500 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4501 struct replmd_replicated_request);
4505 return ldb_module_done(ar->req, NULL, NULL,
4506 LDB_ERR_OPERATIONS_ERROR);
4510 * The error NO_SUCH_OBJECT is not expected, unless the search
4511 * base is the partition DN, and that case doesn't happen here
4512 * because then we wouldn't get a parent_guid_value in any
4515 if (ares->error != LDB_SUCCESS) {
4516 return ldb_module_done(ar->req, ares->controls,
4517 ares->response, ares->error);
4520 switch (ares->type) {
4521 case LDB_REPLY_ENTRY:
4523 struct ldb_message *parent_msg = ares->message;
4524 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4525 struct ldb_dn *parent_dn;
4528 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4529 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4530 /* Per MS-DRSR 4.1.10.6.10
4531 * FindBestParentObject we need to move this
4532 * new object under a deleted object to
4534 struct ldb_dn *nc_root;
4536 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4537 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4538 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4539 "No suitable NC root found for %s. "
4540 "We need to move this object because parent object %s "
4541 "is deleted, but this object is not.",
4542 ldb_dn_get_linearized(msg->dn),
4543 ldb_dn_get_linearized(parent_msg->dn));
4544 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4545 } else if (ret != LDB_SUCCESS) {
4546 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4547 "Unable to find NC root for %s: %s. "
4548 "We need to move this object because parent object %s "
4549 "is deleted, but this object is not.",
4550 ldb_dn_get_linearized(msg->dn),
4551 ldb_errstring(ldb_module_get_ctx(ar->module)),
4552 ldb_dn_get_linearized(parent_msg->dn));
4553 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4556 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4558 DS_GUID_LOSTANDFOUND_CONTAINER,
4560 if (ret != LDB_SUCCESS) {
4561 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4562 "Unable to find LostAndFound Container for %s "
4563 "in partition %s: %s. "
4564 "We need to move this object because parent object %s "
4565 "is deleted, but this object is not.",
4566 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4567 ldb_errstring(ldb_module_get_ctx(ar->module)),
4568 ldb_dn_get_linearized(parent_msg->dn));
4569 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4571 ar->objs->objects[ar->index_current].last_known_parent
4572 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4576 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4579 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4581 comp_num = ldb_dn_get_comp_num(msg->dn);
4583 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4585 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4588 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4590 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4594 case LDB_REPLY_REFERRAL:
4595 /* we ignore referrals */
4598 case LDB_REPLY_DONE:
4600 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4601 struct GUID_txt_buf str_buf;
4602 if (ar->search_msg != NULL) {
4603 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4604 "No parent with GUID %s found for object locally known as %s",
4605 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4606 ldb_dn_get_linearized(ar->search_msg->dn));
4608 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4609 "No parent with GUID %s found for object remotely known as %s",
4610 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4611 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4615 * This error code is really important, as it
4616 * is the flag back to the callers to retry
4617 * this with DRSUAPI_DRS_GET_ANC, and so get
4618 * the parent objects before the child
4621 return ldb_module_done(ar->req, NULL, NULL,
4622 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4625 if (ar->search_msg != NULL) {
4626 ret = replmd_replicated_apply_merge(ar);
4628 ret = replmd_replicated_apply_add(ar);
4630 if (ret != LDB_SUCCESS) {
4631 return ldb_module_done(ar->req, NULL, NULL, ret);
4640 * Look for the parent object, so we put the new object in the right
4641 * place This is akin to NameObject in MS-DRSR - this routine and the
4642 * callbacks find the right parent name, and correct name for this
4646 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4648 struct ldb_context *ldb;
4652 struct ldb_request *search_req;
4653 static const char *attrs[] = {"isDeleted", NULL};
4654 struct GUID_txt_buf guid_str_buf;
4656 ldb = ldb_module_get_ctx(ar->module);
4658 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4659 if (ar->search_msg != NULL) {
4660 return replmd_replicated_apply_merge(ar);
4662 return replmd_replicated_apply_add(ar);
4666 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4669 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4670 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
4672 ret = ldb_build_search_req(&search_req,
4675 ar->objs->partition_dn,
4681 replmd_replicated_apply_search_for_parent_callback,
4683 LDB_REQ_SET_LOCATION(search_req);
4685 ret = dsdb_request_add_controls(search_req,
4686 DSDB_SEARCH_SHOW_RECYCLED|
4687 DSDB_SEARCH_SHOW_DELETED|
4688 DSDB_SEARCH_SHOW_EXTENDED_DN);
4689 if (ret != LDB_SUCCESS) {
4693 return ldb_next_request(ar->module, search_req);
4697 handle renames that come in over DRS replication
4699 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4700 struct ldb_message *msg,
4701 struct ldb_request *parent,
4705 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4706 struct ldb_result *res;
4707 struct ldb_dn *conflict_dn;
4708 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4709 const struct ldb_val *omd_value;
4710 struct replPropertyMetaDataBlob omd, *rmd;
4711 enum ndr_err_code ndr_err;
4712 bool rename_incoming_record, rodc;
4713 struct replPropertyMetaData1 *rmd_name, *omd_name;
4714 struct ldb_dn *new_dn;
4717 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4718 ldb_dn_get_linearized(ar->search_msg->dn),
4719 ldb_dn_get_linearized(msg->dn)));
4722 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4723 DSDB_FLAG_NEXT_MODULE, ar->req);
4724 if (ret == LDB_SUCCESS) {
4725 talloc_free(tmp_ctx);
4730 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4731 talloc_free(tmp_ctx);
4732 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4733 ldb_dn_get_linearized(ar->search_msg->dn),
4734 ldb_dn_get_linearized(msg->dn),
4735 ldb_errstring(ldb_module_get_ctx(ar->module)));
4739 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4740 if (ret != LDB_SUCCESS) {
4741 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4742 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4743 ldb_errstring(ldb_module_get_ctx(ar->module)));
4744 return LDB_ERR_OPERATIONS_ERROR;
4747 * we have a conflict, and need to decide if we will keep the
4748 * new record or the old record
4751 conflict_dn = msg->dn;
4755 * We are on an RODC, or were a GC for this
4756 * partition, so we have to fail this until
4757 * someone who owns the partition sorts it
4760 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4761 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4762 " - We must fail the operation until a master for this partition resolves the conflict",
4763 ldb_dn_get_linearized(conflict_dn));
4768 * first we need the replPropertyMetaData attribute from the
4771 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4773 DSDB_FLAG_NEXT_MODULE |
4774 DSDB_SEARCH_SHOW_DELETED |
4775 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4776 if (ret != LDB_SUCCESS) {
4777 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4778 ldb_dn_get_linearized(conflict_dn)));
4782 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4783 if (omd_value == NULL) {
4784 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4785 ldb_dn_get_linearized(conflict_dn)));
4789 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4790 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4791 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4792 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4793 ldb_dn_get_linearized(conflict_dn)));
4797 rmd = ar->objs->objects[ar->index_current].meta_data;
4800 * we decide which is newer based on the RPMD on the name
4801 * attribute. See [MS-DRSR] ResolveNameConflict.
4803 * We expect omd_name to be present, as this is from a local
4804 * search, but while rmd_name should have been given to us by
4805 * the remote server, if it is missing we just prefer the
4807 * replmd_replPropertyMetaData1_new_should_be_taken()
4809 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4810 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4812 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4813 ldb_dn_get_linearized(conflict_dn)));
4818 * Should we preserve the current record, and so rename the
4819 * incoming record to be a conflict?
4821 rename_incoming_record =
4822 !replmd_replPropertyMetaData1_new_should_be_taken(
4823 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4824 omd_name, rmd_name);
4826 if (rename_incoming_record) {
4828 new_dn = replmd_conflict_dn(msg, msg->dn,
4829 &ar->objs->objects[ar->index_current].object_guid);
4830 if (new_dn == NULL) {
4831 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4832 "Failed to form conflict DN for %s\n",
4833 ldb_dn_get_linearized(msg->dn));
4835 return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
4838 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4839 DSDB_FLAG_NEXT_MODULE, ar->req);
4840 if (ret != LDB_SUCCESS) {
4841 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4842 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4843 ldb_dn_get_linearized(conflict_dn),
4844 ldb_dn_get_linearized(ar->search_msg->dn),
4845 ldb_dn_get_linearized(new_dn),
4846 ldb_errstring(ldb_module_get_ctx(ar->module)));
4847 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4855 /* we are renaming the existing record */
4857 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4858 if (GUID_all_zero(&guid)) {
4859 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4860 ldb_dn_get_linearized(conflict_dn)));
4864 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4865 if (new_dn == NULL) {
4866 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4867 ldb_dn_get_linearized(conflict_dn)));
4871 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4872 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4874 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4875 DSDB_FLAG_OWN_MODULE, ar->req);
4876 if (ret != LDB_SUCCESS) {
4877 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4878 ldb_dn_get_linearized(conflict_dn),
4879 ldb_dn_get_linearized(new_dn),
4880 ldb_errstring(ldb_module_get_ctx(ar->module))));
4885 * now we need to ensure that the rename is seen as an
4886 * originating update. We do that with a modify.
4888 ret = replmd_name_modify(ar, ar->req, new_dn);
4889 if (ret != LDB_SUCCESS) {
4893 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4894 ldb_dn_get_linearized(ar->search_msg->dn),
4895 ldb_dn_get_linearized(msg->dn)));
4898 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4899 DSDB_FLAG_NEXT_MODULE, ar->req);
4900 if (ret != LDB_SUCCESS) {
4901 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4902 ldb_dn_get_linearized(ar->search_msg->dn),
4903 ldb_dn_get_linearized(msg->dn),
4904 ldb_errstring(ldb_module_get_ctx(ar->module))));
4910 * On failure make the caller get the error
4911 * This means replication will stop with an error,
4912 * but there is not much else we can do. In the
4913 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4917 talloc_free(tmp_ctx);
4922 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4924 struct ldb_context *ldb;
4925 struct ldb_request *change_req;
4926 enum ndr_err_code ndr_err;
4927 struct ldb_message *msg;
4928 struct replPropertyMetaDataBlob *rmd;
4929 struct replPropertyMetaDataBlob omd;
4930 const struct ldb_val *omd_value;
4931 struct replPropertyMetaDataBlob nmd;
4932 struct ldb_val nmd_value;
4933 struct GUID remote_parent_guid;
4936 unsigned int removed_attrs = 0;
4938 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4939 bool isDeleted = false;
4940 bool local_isDeleted = false;
4941 bool remote_isDeleted = false;
4942 bool take_remote_isDeleted = false;
4943 bool sd_updated = false;
4944 bool renamed = false;
4945 bool is_schema_nc = false;
4947 const struct ldb_val *old_rdn, *new_rdn;
4948 struct replmd_private *replmd_private =
4949 talloc_get_type(ldb_module_get_private(ar->module),
4950 struct replmd_private);
4952 time_t t = time(NULL);
4953 unix_to_nt_time(&now, t);
4955 ldb = ldb_module_get_ctx(ar->module);
4956 msg = ar->objs->objects[ar->index_current].msg;
4958 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4960 rmd = ar->objs->objects[ar->index_current].meta_data;
4964 /* find existing meta data */
4965 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4967 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4968 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4969 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4970 nt_status = ndr_map_error2ntstatus(ndr_err);
4971 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4974 if (omd.version != 1) {
4975 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4980 struct GUID_txt_buf guid_txt;
4982 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4983 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
4986 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4988 ndr_print_struct_string(s,
4989 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4990 "existing replPropertyMetaData",
4992 ndr_print_struct_string(s,
4993 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4994 "incoming replPropertyMetaData",
4999 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
5000 "isDeleted", false);
5001 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5002 "isDeleted", false);
5005 * Fill in the remote_parent_guid with the GUID or an all-zero
5008 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
5009 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
5011 remote_parent_guid = GUID_zero();
5015 * To ensure we follow a complex rename chain around, we have
5016 * to confirm that the DN is the same (mostly to confirm the
5017 * RDN) and the parentGUID is the same.
5019 * This ensures we keep things under the correct parent, which
5020 * replmd_replicated_handle_rename() will do.
5023 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5024 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5028 * handle renames, even just by case that come in over
5029 * DRS. Changes in the parent DN don't hit us here,
5030 * because the search for a parent will clean up those
5033 * We also have already filtered out the case where
5034 * the peer has an older name to what we have (see
5035 * replmd_replicated_apply_search_callback())
5037 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5040 if (ret != LDB_SUCCESS) {
5041 ldb_debug(ldb, LDB_DEBUG_FATAL,
5042 "replmd_replicated_request rename %s => %s failed - %s\n",
5043 ldb_dn_get_linearized(ar->search_msg->dn),
5044 ldb_dn_get_linearized(msg->dn),
5045 ldb_errstring(ldb));
5046 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5049 if (renamed == true) {
5051 * Set the callback to one that will fix up the name
5052 * metadata on the new conflict DN
5054 callback = replmd_op_name_modify_callback;
5059 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5060 nmd.ctr.ctr1.array = talloc_array(ar,
5061 struct replPropertyMetaData1,
5062 nmd.ctr.ctr1.count);
5063 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5065 /* first copy the old meta data */
5066 for (i=0; i < omd.ctr.ctr1.count; i++) {
5067 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5072 /* now merge in the new meta data */
5073 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5076 for (j=0; j < ni; j++) {
5079 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5083 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5084 ar->objs->dsdb_repl_flags,
5085 &nmd.ctr.ctr1.array[j],
5086 &rmd->ctr.ctr1.array[i]);
5088 /* replace the entry */
5089 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5090 if (ar->seq_num == 0) {
5091 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5092 if (ret != LDB_SUCCESS) {
5093 return replmd_replicated_request_error(ar, ret);
5096 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5097 switch (nmd.ctr.ctr1.array[j].attid) {
5098 case DRSUAPI_ATTID_ntSecurityDescriptor:
5101 case DRSUAPI_ATTID_isDeleted:
5102 take_remote_isDeleted = true;
5111 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5112 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5113 msg->elements[i-removed_attrs].name,
5114 ldb_dn_get_linearized(msg->dn),
5115 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5118 /* we don't want to apply this change so remove the attribute */
5119 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5126 if (found) continue;
5128 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5129 if (ar->seq_num == 0) {
5130 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5131 if (ret != LDB_SUCCESS) {
5132 return replmd_replicated_request_error(ar, ret);
5135 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5136 switch (nmd.ctr.ctr1.array[ni].attid) {
5137 case DRSUAPI_ATTID_ntSecurityDescriptor:
5140 case DRSUAPI_ATTID_isDeleted:
5141 take_remote_isDeleted = true;
5150 * finally correct the size of the meta_data array
5152 nmd.ctr.ctr1.count = ni;
5154 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5155 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5158 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5159 &nmd, ar, now, is_schema_nc);
5160 if (ret != LDB_SUCCESS) {
5161 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5162 return replmd_replicated_request_error(ar, ret);
5166 * sort the new meta data array
5168 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5169 if (ret != LDB_SUCCESS) {
5170 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5175 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5178 * This also controls SD propagation below
5180 if (take_remote_isDeleted) {
5181 isDeleted = remote_isDeleted;
5183 isDeleted = local_isDeleted;
5186 ar->isDeleted = isDeleted;
5189 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5191 if (msg->num_elements == 0) {
5192 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5195 return replmd_replicated_apply_isDeleted(ar);
5198 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5199 ar->index_current, msg->num_elements);
5205 if (sd_updated && !isDeleted) {
5206 ret = dsdb_module_schedule_sd_propagation(ar->module,
5207 ar->objs->partition_dn,
5209 if (ret != LDB_SUCCESS) {
5210 return ldb_operr(ldb);
5214 /* create the meta data value */
5215 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5216 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5217 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5218 nt_status = ndr_map_error2ntstatus(ndr_err);
5219 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5223 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5224 * and replPopertyMetaData attributes
5226 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5227 if (ret != LDB_SUCCESS) {
5228 return replmd_replicated_request_error(ar, ret);
5230 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5231 if (ret != LDB_SUCCESS) {
5232 return replmd_replicated_request_error(ar, ret);
5234 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5235 if (ret != LDB_SUCCESS) {
5236 return replmd_replicated_request_error(ar, ret);
5239 replmd_ldb_message_sort(msg, ar->schema);
5241 /* we want to replace the old values */
5242 for (i=0; i < msg->num_elements; i++) {
5243 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5244 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5245 if (msg->elements[i].num_values == 0) {
5246 ldb_asprintf_errstring(ldb, __location__
5247 ": objectClass removed on %s, aborting replication\n",
5248 ldb_dn_get_linearized(msg->dn));
5249 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5255 struct GUID_txt_buf guid_txt;
5257 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5258 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5259 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5264 ret = ldb_build_mod_req(&change_req,
5272 LDB_REQ_SET_LOCATION(change_req);
5273 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5275 /* current partition control needed by "repmd_op_callback" */
5276 ret = ldb_request_add_control(change_req,
5277 DSDB_CONTROL_CURRENT_PARTITION_OID,
5279 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5281 return ldb_next_request(ar->module, change_req);
5284 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5285 struct ldb_reply *ares)
5287 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5288 struct replmd_replicated_request);
5292 return ldb_module_done(ar->req, NULL, NULL,
5293 LDB_ERR_OPERATIONS_ERROR);
5295 if (ares->error != LDB_SUCCESS &&
5296 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5297 return ldb_module_done(ar->req, ares->controls,
5298 ares->response, ares->error);
5301 switch (ares->type) {
5302 case LDB_REPLY_ENTRY:
5303 ar->search_msg = talloc_steal(ar, ares->message);
5306 case LDB_REPLY_REFERRAL:
5307 /* we ignore referrals */
5310 case LDB_REPLY_DONE:
5312 struct replPropertyMetaData1 *md_remote;
5313 struct replPropertyMetaData1 *md_local;
5315 struct replPropertyMetaDataBlob omd;
5316 const struct ldb_val *omd_value;
5317 struct replPropertyMetaDataBlob *rmd;
5318 struct ldb_message *msg;
5320 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5321 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5324 * This is the ADD case, find the appropriate parent,
5325 * as this object doesn't exist locally:
5327 if (ar->search_msg == NULL) {
5328 ret = replmd_replicated_apply_search_for_parent(ar);
5329 if (ret != LDB_SUCCESS) {
5330 return ldb_module_done(ar->req, NULL, NULL, ret);
5337 * Otherwise, in the MERGE case, work out if we are
5338 * attempting a rename, and if so find the parent the
5339 * newly renamed object wants to belong under (which
5340 * may not be the parent in it's attached string DN
5342 rmd = ar->objs->objects[ar->index_current].meta_data;
5346 /* find existing meta data */
5347 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5349 enum ndr_err_code ndr_err;
5350 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5351 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5352 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5353 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5354 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5357 if (omd.version != 1) {
5358 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5362 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5364 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5365 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5366 && GUID_all_zero(&ar->local_parent_guid)) {
5367 DEBUG(0, ("Refusing to replicate new version of %s "
5368 "as local object has an all-zero parentGUID attribute, "
5369 "despite not being an NC root\n",
5370 ldb_dn_get_linearized(ar->search_msg->dn)));
5371 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5375 * now we need to check for double renames. We could have a
5376 * local rename pending which our replication partner hasn't
5377 * received yet. We choose which one wins by looking at the
5378 * attribute stamps on the two objects, the newer one wins.
5380 * This also simply applies the correct algorithms for
5381 * determining if a change was made to name at all, or
5382 * if the object has just been renamed under the same
5385 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5386 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5388 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5389 ldb_dn_get_linearized(ar->search_msg->dn)));
5390 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5394 * if there is no name attribute given then we have to assume the
5395 * object we've received has the older name
5397 if (replmd_replPropertyMetaData1_new_should_be_taken(
5398 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5399 md_local, md_remote)) {
5400 struct GUID_txt_buf p_guid_local;
5401 struct GUID_txt_buf p_guid_remote;
5402 msg = ar->objs->objects[ar->index_current].msg;
5404 /* Merge on the existing object, with rename */
5406 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5407 "as incoming object changing to %s under %s\n",
5408 ldb_dn_get_linearized(ar->search_msg->dn),
5409 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5410 ldb_dn_get_linearized(msg->dn),
5411 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5413 ret = replmd_replicated_apply_search_for_parent(ar);
5415 struct GUID_txt_buf p_guid_local;
5416 struct GUID_txt_buf p_guid_remote;
5417 msg = ar->objs->objects[ar->index_current].msg;
5420 * Merge on the existing object, force no
5421 * rename (code below just to explain why in
5425 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5426 ldb_dn_get_linearized(msg->dn)) == 0) {
5427 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5428 GUID_equal(&ar->local_parent_guid,
5429 ar->objs->objects[ar->index_current].parent_guid)
5431 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5432 "despite incoming object changing parent to %s\n",
5433 ldb_dn_get_linearized(ar->search_msg->dn),
5434 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5435 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5439 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5440 " and rejecting older rename to %s under %s\n",
5441 ldb_dn_get_linearized(ar->search_msg->dn),
5442 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5443 ldb_dn_get_linearized(msg->dn),
5444 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5448 * This assignment ensures that the strcmp()
5449 * and GUID_equal() calls in
5450 * replmd_replicated_apply_merge() avoids the
5453 ar->objs->objects[ar->index_current].parent_guid =
5454 &ar->local_parent_guid;
5456 msg->dn = ar->search_msg->dn;
5457 ret = replmd_replicated_apply_merge(ar);
5459 if (ret != LDB_SUCCESS) {
5460 return ldb_module_done(ar->req, NULL, NULL, ret);
5469 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5471 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5473 struct ldb_context *ldb;
5477 struct ldb_request *search_req;
5478 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
5479 "parentGUID", "instanceType",
5480 "replPropertyMetaData", "nTSecurityDescriptor",
5481 "isDeleted", NULL };
5482 struct GUID_txt_buf guid_str_buf;
5484 if (ar->index_current >= ar->objs->num_objects) {
5485 /* done with it, go to next stage */
5486 return replmd_replicated_uptodate_vector(ar);
5489 ldb = ldb_module_get_ctx(ar->module);
5490 ar->search_msg = NULL;
5491 ar->isDeleted = false;
5493 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5496 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5497 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5499 ret = ldb_build_search_req(&search_req,
5502 ar->objs->partition_dn,
5508 replmd_replicated_apply_search_callback,
5510 LDB_REQ_SET_LOCATION(search_req);
5512 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5514 if (ret != LDB_SUCCESS) {
5518 return ldb_next_request(ar->module, search_req);
5522 * This is essentially a wrapper for replmd_replicated_apply_next()
5524 * This is needed to ensure that both codepaths call this handler.
5526 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5528 struct ldb_dn *deleted_objects_dn;
5529 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5530 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5531 &deleted_objects_dn);
5532 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5534 * Do a delete here again, so that if there is
5535 * anything local that conflicts with this
5536 * object being deleted, it is removed. This
5537 * includes links. See MS-DRSR 4.1.10.6.9
5540 * If the object is already deleted, and there
5541 * is no more work required, it doesn't do
5545 /* This has been updated to point to the DN we eventually did the modify on */
5547 struct ldb_request *del_req;
5548 struct ldb_result *res;
5550 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5552 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5556 res = talloc_zero(tmp_ctx, struct ldb_result);
5558 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5559 talloc_free(tmp_ctx);
5563 /* Build a delete request, which hopefully will artually turn into nothing */
5564 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5568 ldb_modify_default_callback,
5570 LDB_REQ_SET_LOCATION(del_req);
5571 if (ret != LDB_SUCCESS) {
5572 talloc_free(tmp_ctx);
5577 * This is the guts of the call, call back
5578 * into our delete code, but setting the
5579 * re_delete flag so we delete anything that
5580 * shouldn't be there on a deleted or recycled
5583 ret = replmd_delete_internals(ar->module, del_req, true);
5584 if (ret == LDB_SUCCESS) {
5585 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5588 talloc_free(tmp_ctx);
5589 if (ret != LDB_SUCCESS) {
5594 ar->index_current++;
5595 return replmd_replicated_apply_next(ar);
5598 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5599 struct ldb_reply *ares)
5601 struct ldb_context *ldb;
5602 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5603 struct replmd_replicated_request);
5604 ldb = ldb_module_get_ctx(ar->module);
5607 return ldb_module_done(ar->req, NULL, NULL,
5608 LDB_ERR_OPERATIONS_ERROR);
5610 if (ares->error != LDB_SUCCESS) {
5611 return ldb_module_done(ar->req, ares->controls,
5612 ares->response, ares->error);
5615 if (ares->type != LDB_REPLY_DONE) {
5616 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5617 return ldb_module_done(ar->req, NULL, NULL,
5618 LDB_ERR_OPERATIONS_ERROR);
5623 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5626 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5628 struct ldb_context *ldb;
5629 struct ldb_request *change_req;
5630 enum ndr_err_code ndr_err;
5631 struct ldb_message *msg;
5632 struct replUpToDateVectorBlob ouv;
5633 const struct ldb_val *ouv_value;
5634 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5635 struct replUpToDateVectorBlob nuv;
5636 struct ldb_val nuv_value;
5637 struct ldb_message_element *nuv_el = NULL;
5638 struct ldb_message_element *orf_el = NULL;
5639 struct repsFromToBlob nrf;
5640 struct ldb_val *nrf_value = NULL;
5641 struct ldb_message_element *nrf_el = NULL;
5645 time_t t = time(NULL);
5648 uint32_t instanceType;
5650 ldb = ldb_module_get_ctx(ar->module);
5651 ruv = ar->objs->uptodateness_vector;
5657 unix_to_nt_time(&now, t);
5659 if (ar->search_msg == NULL) {
5660 /* this happens for a REPL_OBJ call where we are
5661 creating the target object by replicating it. The
5662 subdomain join code does this for the partition DN
5664 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5665 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5668 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5669 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5670 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5671 ldb_dn_get_linearized(ar->search_msg->dn)));
5672 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5676 * first create the new replUpToDateVector
5678 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5680 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5681 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5682 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5683 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5684 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5687 if (ouv.version != 2) {
5688 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5693 * the new uptodateness vector will at least
5694 * contain 1 entry, one for the source_dsa
5696 * plus optional values from our old vector and the one from the source_dsa
5698 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5699 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5700 nuv.ctr.ctr2.cursors = talloc_array(ar,
5701 struct drsuapi_DsReplicaCursor2,
5702 nuv.ctr.ctr2.count);
5703 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5705 /* first copy the old vector */
5706 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5707 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5711 /* merge in the source_dsa vector is available */
5712 for (i=0; (ruv && i < ruv->count); i++) {
5715 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5716 &ar->our_invocation_id)) {
5720 for (j=0; j < ni; j++) {
5721 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5722 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5728 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5729 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5734 if (found) continue;
5736 /* if it's not there yet, add it */
5737 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5742 * finally correct the size of the cursors array
5744 nuv.ctr.ctr2.count = ni;
5749 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5752 * create the change ldb_message
5754 msg = ldb_msg_new(ar);
5755 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5756 msg->dn = ar->search_msg->dn;
5758 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5759 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5760 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5761 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5762 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5764 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5765 if (ret != LDB_SUCCESS) {
5766 return replmd_replicated_request_error(ar, ret);
5768 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5771 * now create the new repsFrom value from the given repsFromTo1 structure
5775 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5776 nrf.ctr.ctr1.last_attempt = now;
5777 nrf.ctr.ctr1.last_success = now;
5778 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5781 * first see if we already have a repsFrom value for the current source dsa
5782 * if so we'll later replace this value
5784 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5786 for (i=0; i < orf_el->num_values; i++) {
5787 struct repsFromToBlob *trf;
5789 trf = talloc(ar, struct repsFromToBlob);
5790 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5792 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5793 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5794 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5795 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5796 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5799 if (trf->version != 1) {
5800 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5804 * we compare the source dsa objectGUID not the invocation_id
5805 * because we want only one repsFrom value per source dsa
5806 * and when the invocation_id of the source dsa has changed we don't need
5807 * the old repsFrom with the old invocation_id
5809 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5810 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5816 nrf_value = &orf_el->values[i];
5821 * copy over all old values to the new ldb_message
5823 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5824 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5829 * if we haven't found an old repsFrom value for the current source dsa
5830 * we'll add a new value
5833 struct ldb_val zero_value;
5834 ZERO_STRUCT(zero_value);
5835 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5836 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5838 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5841 /* we now fill the value which is already attached to ldb_message */
5842 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5844 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5845 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5846 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5847 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5851 * the ldb_message_element for the attribute, has all the old values and the new one
5852 * so we'll replace the whole attribute with all values
5854 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5856 if (CHECK_DEBUGLVL(4)) {
5857 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5858 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5862 /* prepare the ldb_modify() request */
5863 ret = ldb_build_mod_req(&change_req,
5869 replmd_replicated_uptodate_modify_callback,
5871 LDB_REQ_SET_LOCATION(change_req);
5872 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5874 return ldb_next_request(ar->module, change_req);
5877 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5878 struct ldb_reply *ares)
5880 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5881 struct replmd_replicated_request);
5885 return ldb_module_done(ar->req, NULL, NULL,
5886 LDB_ERR_OPERATIONS_ERROR);
5888 if (ares->error != LDB_SUCCESS &&
5889 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5890 return ldb_module_done(ar->req, ares->controls,
5891 ares->response, ares->error);
5894 switch (ares->type) {
5895 case LDB_REPLY_ENTRY:
5896 ar->search_msg = talloc_steal(ar, ares->message);
5899 case LDB_REPLY_REFERRAL:
5900 /* we ignore referrals */
5903 case LDB_REPLY_DONE:
5904 ret = replmd_replicated_uptodate_modify(ar);
5905 if (ret != LDB_SUCCESS) {
5906 return ldb_module_done(ar->req, NULL, NULL, ret);
5915 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5917 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5918 struct replmd_private *replmd_private =
5919 talloc_get_type_abort(ldb_module_get_private(ar->module),
5920 struct replmd_private);
5922 static const char *attrs[] = {
5923 "replUpToDateVector",
5928 struct ldb_request *search_req;
5930 ar->search_msg = NULL;
5933 * Let the caller know that we did an originating updates
5935 ar->objs->originating_updates = replmd_private->originating_updates;
5937 ret = ldb_build_search_req(&search_req,
5940 ar->objs->partition_dn,
5946 replmd_replicated_uptodate_search_callback,
5948 LDB_REQ_SET_LOCATION(search_req);
5949 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5951 return ldb_next_request(ar->module, search_req);
5956 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5958 struct ldb_context *ldb;
5959 struct dsdb_extended_replicated_objects *objs;
5960 struct replmd_replicated_request *ar;
5961 struct ldb_control **ctrls;
5964 struct replmd_private *replmd_private =
5965 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5967 ldb = ldb_module_get_ctx(module);
5969 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5971 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5973 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5974 return LDB_ERR_PROTOCOL_ERROR;
5977 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5978 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5979 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5980 return LDB_ERR_PROTOCOL_ERROR;
5983 ar = replmd_ctx_init(module, req);
5985 return LDB_ERR_OPERATIONS_ERROR;
5987 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5988 ar->apply_mode = true;
5990 ar->schema = dsdb_get_schema(ldb, ar);
5992 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5994 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5995 return LDB_ERR_CONSTRAINT_VIOLATION;
5998 ctrls = req->controls;
6000 if (req->controls) {
6001 req->controls = talloc_memdup(ar, req->controls,
6002 talloc_get_size(req->controls));
6003 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6006 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
6007 if (ret != LDB_SUCCESS) {
6011 /* If this change contained linked attributes in the body
6012 * (rather than in the links section) we need to update
6013 * backlinks in linked_attributes */
6014 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
6015 if (ret != LDB_SUCCESS) {
6019 ar->controls = req->controls;
6020 req->controls = ctrls;
6022 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
6024 /* save away the linked attributes for the end of the
6026 for (i=0; i<ar->objs->linked_attributes_count; i++) {
6027 struct la_entry *la_entry;
6029 if (replmd_private->la_ctx == NULL) {
6030 replmd_private->la_ctx = talloc_new(replmd_private);
6032 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6033 if (la_entry == NULL) {
6035 return LDB_ERR_OPERATIONS_ERROR;
6037 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6038 if (la_entry->la == NULL) {
6039 talloc_free(la_entry);
6041 return LDB_ERR_OPERATIONS_ERROR;
6043 *la_entry->la = ar->objs->linked_attributes[i];
6045 /* we need to steal the non-scalars so they stay
6046 around until the end of the transaction */
6047 talloc_steal(la_entry->la, la_entry->la->identifier);
6048 talloc_steal(la_entry->la, la_entry->la->value.blob);
6050 DLIST_ADD(replmd_private->la_list, la_entry);
6053 return replmd_replicated_apply_next(ar);
6057 process one linked attribute structure
6059 static int replmd_process_linked_attribute(struct ldb_module *module,
6060 struct la_entry *la_entry,
6061 struct ldb_request *parent)
6063 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6064 struct ldb_context *ldb = ldb_module_get_ctx(module);
6065 struct ldb_message *msg;
6066 struct ldb_message *target_msg = NULL;
6067 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6068 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6070 const struct dsdb_attribute *attr;
6071 struct dsdb_dn *dsdb_dn;
6072 uint64_t seq_num = 0;
6073 struct ldb_message_element *old_el;
6075 time_t t = time(NULL);
6076 struct ldb_result *res;
6077 struct ldb_result *target_res;
6078 const char *attrs[4];
6079 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6080 struct parsed_dn *pdn_list, *pdn;
6081 struct GUID guid = GUID_zero();
6083 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6084 const struct GUID *our_invocation_id;
6086 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6087 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6090 linked_attributes[0]:
6091 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6093 identifier: struct drsuapi_DsReplicaObjectIdentifier
6094 __ndr_size : 0x0000003a (58)
6095 __ndr_size_sid : 0x00000000 (0)
6096 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6098 __ndr_size_dn : 0x00000000 (0)
6100 attid : DRSUAPI_ATTID_member (0x1F)
6101 value: struct drsuapi_DsAttributeValue
6102 __ndr_size : 0x0000007e (126)
6104 blob : DATA_BLOB length=126
6105 flags : 0x00000001 (1)
6106 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6107 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6108 meta_data: struct drsuapi_DsReplicaMetaData
6109 version : 0x00000015 (21)
6110 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6111 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6112 originating_usn : 0x000000000001e19c (123292)
6114 (for cases where the link is to a normal DN)
6115 &target: struct drsuapi_DsReplicaObjectIdentifier3
6116 __ndr_size : 0x0000007e (126)
6117 __ndr_size_sid : 0x0000001c (28)
6118 guid : 7639e594-db75-4086-b0d4-67890ae46031
6119 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6120 __ndr_size_dn : 0x00000022 (34)
6121 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6124 /* find the attribute being modified */
6125 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6127 struct GUID_txt_buf guid_str;
6128 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6130 GUID_buf_string(&la->identifier->guid,
6132 talloc_free(tmp_ctx);
6133 return LDB_ERR_OPERATIONS_ERROR;
6136 attrs[0] = attr->lDAPDisplayName;
6137 attrs[1] = "isDeleted";
6138 attrs[2] = "isRecycled";
6141 /* get the existing message from the db for the object with
6142 this GUID, returning attribute being modified. We will then
6143 use this msg as the basis for a modify call */
6144 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6145 DSDB_FLAG_NEXT_MODULE |
6146 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6147 DSDB_SEARCH_SHOW_RECYCLED |
6148 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6149 DSDB_SEARCH_REVEAL_INTERNALS,
6151 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6152 if (ret != LDB_SUCCESS) {
6153 talloc_free(tmp_ctx);
6156 if (res->count != 1) {
6157 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6158 GUID_string(tmp_ctx, &la->identifier->guid));
6159 talloc_free(tmp_ctx);
6160 return LDB_ERR_NO_SUCH_OBJECT;
6165 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6166 * ProcessLinkValue, because link updates are not applied to
6167 * recycled and tombstone objects. We don't have to delete
6168 * any existing link, that should have happened when the
6169 * object deletion was replicated or initiated.
6172 replmd_deletion_state(module, msg, &deletion_state, NULL);
6174 if (deletion_state >= OBJECT_RECYCLED) {
6175 talloc_free(tmp_ctx);
6179 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6180 if (old_el == NULL) {
6181 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6182 if (ret != LDB_SUCCESS) {
6183 ldb_module_oom(module);
6184 talloc_free(tmp_ctx);
6185 return LDB_ERR_OPERATIONS_ERROR;
6188 old_el->flags = LDB_FLAG_MOD_REPLACE;
6191 /* parse the existing links */
6192 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
6193 if (ret != LDB_SUCCESS) {
6194 talloc_free(tmp_ctx);
6198 /* get our invocationId */
6199 our_invocation_id = samdb_ntds_invocation_id(ldb);
6200 if (!our_invocation_id) {
6201 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
6202 talloc_free(tmp_ctx);
6203 return LDB_ERR_OPERATIONS_ERROR;
6206 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
6207 if (ret != LDB_SUCCESS) {
6208 talloc_free(tmp_ctx);
6212 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6213 if (!W_ERROR_IS_OK(status)) {
6214 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6215 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6216 talloc_free(tmp_ctx);
6217 return LDB_ERR_OPERATIONS_ERROR;
6220 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6221 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6223 * This strange behaviour (allowing a NULL/missing
6224 * GUID) originally comes from:
6226 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6227 * Author: Andrew Tridgell <tridge@samba.org>
6228 * Date: Mon Dec 21 21:21:55 2009 +1100
6230 * s4-drs: cope better with NULL GUIDS from DRS
6232 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6233 * need to match by DN if possible when seeing if we should update an
6236 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6239 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6240 dsdb_dn->dn, attrs2,
6241 DSDB_FLAG_NEXT_MODULE |
6242 DSDB_SEARCH_SHOW_RECYCLED |
6243 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6244 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6246 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6247 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6249 ldb_dn_get_linearized(dsdb_dn->dn),
6250 ldb_dn_get_linearized(msg->dn));
6251 talloc_free(tmp_ctx);
6252 return LDB_ERR_OPERATIONS_ERROR;
6254 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6255 NULL, LDB_SCOPE_SUBTREE,
6257 DSDB_FLAG_NEXT_MODULE |
6258 DSDB_SEARCH_SHOW_RECYCLED |
6259 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6260 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6263 GUID_string(tmp_ctx, &guid));
6266 if (ret != LDB_SUCCESS) {
6267 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6268 GUID_string(tmp_ctx, &guid),
6269 ldb_errstring(ldb_module_get_ctx(module)));
6270 talloc_free(tmp_ctx);
6274 if (target_res->count == 0) {
6275 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6276 GUID_string(tmp_ctx, &guid),
6277 ldb_dn_get_linearized(dsdb_dn->dn)));
6278 } else if (target_res->count != 1) {
6279 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6280 GUID_string(tmp_ctx, &guid));
6281 talloc_free(tmp_ctx);
6282 return LDB_ERR_OPERATIONS_ERROR;
6284 target_msg = target_res->msgs[0];
6285 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6289 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6290 * ProcessLinkValue, because link updates are not applied to
6291 * recycled and tombstone objects. We don't have to delete
6292 * any existing link, that should have happened when the
6293 * object deletion was replicated or initiated.
6295 replmd_deletion_state(module, target_msg,
6296 &target_deletion_state, NULL);
6298 if (target_deletion_state >= OBJECT_RECYCLED) {
6299 talloc_free(tmp_ctx);
6303 /* see if this link already exists */
6304 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
6306 /* see if this update is newer than what we have already */
6307 struct GUID invocation_id = GUID_zero();
6308 uint32_t version = 0;
6309 uint32_t originating_usn = 0;
6310 NTTIME change_time = 0;
6311 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6313 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6314 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6315 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6316 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6318 if (!replmd_update_is_newer(&invocation_id,
6319 &la->meta_data.originating_invocation_id,
6321 la->meta_data.version,
6323 la->meta_data.originating_change_time)) {
6324 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6325 old_el->name, ldb_dn_get_linearized(msg->dn),
6326 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6327 talloc_free(tmp_ctx);
6331 /* get a seq_num for this change */
6332 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6333 if (ret != LDB_SUCCESS) {
6334 talloc_free(tmp_ctx);
6338 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6339 /* remove the existing backlink */
6340 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, true);
6341 if (ret != LDB_SUCCESS) {
6342 talloc_free(tmp_ctx);
6347 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6348 &la->meta_data.originating_invocation_id,
6349 la->meta_data.originating_usn, seq_num,
6350 la->meta_data.originating_change_time,
6351 la->meta_data.version,
6353 if (ret != LDB_SUCCESS) {
6354 talloc_free(tmp_ctx);
6359 /* add the new backlink */
6360 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, true);
6361 if (ret != LDB_SUCCESS) {
6362 talloc_free(tmp_ctx);
6367 /* get a seq_num for this change */
6368 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6369 if (ret != LDB_SUCCESS) {
6370 talloc_free(tmp_ctx);
6374 old_el->values = talloc_realloc(msg->elements, old_el->values,
6375 struct ldb_val, old_el->num_values+1);
6376 if (!old_el->values) {
6377 ldb_module_oom(module);
6378 return LDB_ERR_OPERATIONS_ERROR;
6380 old_el->num_values++;
6382 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
6383 &la->meta_data.originating_invocation_id,
6384 la->meta_data.originating_usn, seq_num,
6385 la->meta_data.originating_change_time,
6386 la->meta_data.version,
6388 if (ret != LDB_SUCCESS) {
6389 talloc_free(tmp_ctx);
6394 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6396 if (ret != LDB_SUCCESS) {
6397 talloc_free(tmp_ctx);
6403 /* we only change whenChanged and uSNChanged if the seq_num
6405 ret = add_time_element(msg, "whenChanged", t);
6406 if (ret != LDB_SUCCESS) {
6407 talloc_free(tmp_ctx);
6412 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6413 if (ret != LDB_SUCCESS) {
6414 talloc_free(tmp_ctx);
6419 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6420 if (old_el == NULL) {
6421 talloc_free(tmp_ctx);
6422 return ldb_operr(ldb);
6425 ret = dsdb_check_single_valued_link(attr, old_el);
6426 if (ret != LDB_SUCCESS) {
6427 talloc_free(tmp_ctx);
6431 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6433 ret = linked_attr_modify(module, msg, parent);
6434 if (ret != LDB_SUCCESS) {
6435 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6437 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6438 talloc_free(tmp_ctx);
6442 talloc_free(tmp_ctx);
6447 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6449 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6450 return replmd_extended_replicated_objects(module, req);
6453 return ldb_next_request(module, req);
6458 we hook into the transaction operations to allow us to
6459 perform the linked attribute updates at the end of the whole
6460 transaction. This allows a forward linked attribute to be created
6461 before the object is created. During a vampire, w2k8 sends us linked
6462 attributes before the objects they are part of.
6464 static int replmd_start_transaction(struct ldb_module *module)
6466 /* create our private structure for this transaction */
6467 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6468 struct replmd_private);
6469 replmd_txn_cleanup(replmd_private);
6471 /* free any leftover mod_usn records from cancelled
6473 while (replmd_private->ncs) {
6474 struct nc_entry *e = replmd_private->ncs;
6475 DLIST_REMOVE(replmd_private->ncs, e);
6479 replmd_private->originating_updates = false;
6481 return ldb_next_start_trans(module);
6485 on prepare commit we loop over our queued la_context structures and
6488 static int replmd_prepare_commit(struct ldb_module *module)
6490 struct replmd_private *replmd_private =
6491 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6492 struct la_entry *la, *prev;
6493 struct la_backlink *bl;
6496 /* walk the list backwards, to do the first entry first, as we
6497 * added the entries with DLIST_ADD() which puts them at the
6498 * start of the list */
6499 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6500 prev = DLIST_PREV(la);
6501 DLIST_REMOVE(replmd_private->la_list, la);
6502 ret = replmd_process_linked_attribute(module, la, NULL);
6503 if (ret != LDB_SUCCESS) {
6504 replmd_txn_cleanup(replmd_private);
6509 /* process our backlink list, creating and deleting backlinks
6511 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6512 ret = replmd_process_backlink(module, bl, NULL);
6513 if (ret != LDB_SUCCESS) {
6514 replmd_txn_cleanup(replmd_private);
6519 replmd_txn_cleanup(replmd_private);
6521 /* possibly change @REPLCHANGED */
6522 ret = replmd_notify_store(module, NULL);
6523 if (ret != LDB_SUCCESS) {
6527 return ldb_next_prepare_commit(module);
6530 static int replmd_del_transaction(struct ldb_module *module)
6532 struct replmd_private *replmd_private =
6533 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6534 replmd_txn_cleanup(replmd_private);
6536 return ldb_next_del_trans(module);
6540 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6541 .name = "repl_meta_data",
6542 .init_context = replmd_init,
6544 .modify = replmd_modify,
6545 .rename = replmd_rename,
6546 .del = replmd_delete,
6547 .extended = replmd_extended,
6548 .start_transaction = replmd_start_transaction,
6549 .prepare_commit = replmd_prepare_commit,
6550 .del_transaction = replmd_del_transaction,
6553 int ldb_repl_meta_data_module_init(const char *version)
6555 LDB_MODULE_CHECK_VERSION(version);
6556 return ldb_register_module(&ldb_repl_meta_data_module_ops);