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) {
1317 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1319 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1323 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1324 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1326 * allow this to make it possible for dbcheck
1327 * to rebuild broken metadata
1333 for (i=0; i<omd->ctr.ctr1.count; i++) {
1335 * First check if we find it under the msDS-IntID,
1336 * then check if we find it under the OID and
1339 * This allows the administrator to simply re-write
1340 * the attributes and so restore replication, which is
1341 * likely what they will try to do.
1343 if (attid == omd->ctr.ctr1.array[i].attid) {
1347 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1352 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1353 /* linked attributes are not stored in
1354 replPropertyMetaData in FL above w2k, but we do
1355 raise the seqnum for the object */
1356 if (*seq_num == 0 &&
1357 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1358 return LDB_ERR_OPERATIONS_ERROR;
1363 if (i == omd->ctr.ctr1.count) {
1364 /* we need to add a new one */
1365 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1366 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1367 if (omd->ctr.ctr1.array == NULL) {
1369 return LDB_ERR_OPERATIONS_ERROR;
1371 omd->ctr.ctr1.count++;
1372 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1375 /* Get a new sequence number from the backend. We only do this
1376 * if we have a change that requires a new
1377 * replPropertyMetaData element
1379 if (*seq_num == 0) {
1380 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1381 if (ret != LDB_SUCCESS) {
1382 return LDB_ERR_OPERATIONS_ERROR;
1386 md1 = &omd->ctr.ctr1.array[i];
1389 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1390 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1393 if (rdn_val == NULL) {
1395 return LDB_ERR_OPERATIONS_ERROR;
1398 rdn = (const char*)rdn_val->data;
1399 if (strcmp(rdn, "Deleted Objects") == 0) {
1401 * Set the originating_change_time to 29/12/9999 at 23:59:59
1402 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1404 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1406 md1->originating_change_time = now;
1409 md1->originating_change_time = now;
1411 md1->originating_invocation_id = *our_invocation_id;
1412 md1->originating_usn = *seq_num;
1413 md1->local_usn = *seq_num;
1419 * Bump the replPropertyMetaData version on an attribute, and if it
1420 * has changed (or forced by leaving rdn_old NULL), update the value
1423 * This is important, as calling a modify operation may not change the
1424 * version number if the values appear unchanged, but a rename between
1425 * parents bumps this value.
1428 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1429 struct ldb_message *msg,
1430 const struct ldb_val *rdn_new,
1431 const struct ldb_val *rdn_old,
1432 struct replPropertyMetaDataBlob *omd,
1433 struct replmd_replicated_request *ar,
1437 struct ldb_message_element new_el = {
1438 .flags = LDB_FLAG_MOD_REPLACE,
1439 .name = ldb_dn_get_rdn_name(msg->dn),
1441 .values = discard_const_p(struct ldb_val, rdn_new)
1443 struct ldb_message_element old_el = {
1444 .flags = LDB_FLAG_MOD_REPLACE,
1445 .name = ldb_dn_get_rdn_name(msg->dn),
1446 .num_values = rdn_old ? 1 : 0,
1447 .values = discard_const_p(struct ldb_val, rdn_old)
1450 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1451 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1452 if (ret != LDB_SUCCESS) {
1453 return ldb_oom(ldb);
1457 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1458 omd, ar->schema, &ar->seq_num,
1459 &ar->our_invocation_id,
1460 now, is_schema_nc, ar->req);
1464 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1466 uint32_t count = omd.ctr.ctr1.count;
1469 for (i=0; i < count; i++) {
1470 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1471 if (max < m.local_usn) {
1479 * update the replPropertyMetaData object each time we modify an
1480 * object. This is needed for DRS replication, as the merge on the
1481 * client is based on this object
1483 static int replmd_update_rpmd(struct ldb_module *module,
1484 const struct dsdb_schema *schema,
1485 struct ldb_request *req,
1486 const char * const *rename_attrs,
1487 struct ldb_message *msg, uint64_t *seq_num,
1488 time_t t, bool is_schema_nc,
1489 bool *is_urgent, bool *rodc)
1491 const struct ldb_val *omd_value;
1492 enum ndr_err_code ndr_err;
1493 struct replPropertyMetaDataBlob omd;
1496 const struct GUID *our_invocation_id;
1498 const char * const *attrs = NULL;
1499 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1500 struct ldb_result *res;
1501 struct ldb_context *ldb;
1502 struct ldb_message_element *objectclass_el;
1503 enum urgent_situation situation;
1504 bool rmd_is_provided;
1505 bool rmd_is_just_resorted = false;
1506 const char *not_rename_attrs[4 + msg->num_elements];
1509 attrs = rename_attrs;
1511 for (i = 0; i < msg->num_elements; i++) {
1512 not_rename_attrs[i] = msg->elements[i].name;
1514 not_rename_attrs[i] = "replPropertyMetaData";
1515 not_rename_attrs[i+1] = "objectClass";
1516 not_rename_attrs[i+2] = "instanceType";
1517 not_rename_attrs[i+3] = NULL;
1518 attrs = not_rename_attrs;
1521 ldb = ldb_module_get_ctx(module);
1523 our_invocation_id = samdb_ntds_invocation_id(ldb);
1524 if (!our_invocation_id) {
1525 /* this happens during an initial vampire while
1526 updating the schema */
1527 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1531 unix_to_nt_time(&now, t);
1533 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1534 rmd_is_provided = true;
1535 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1536 rmd_is_just_resorted = true;
1539 rmd_is_provided = false;
1542 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1543 * otherwise we consider we are updating */
1544 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1545 situation = REPL_URGENT_ON_DELETE;
1546 } else if (rename_attrs) {
1547 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1549 situation = REPL_URGENT_ON_UPDATE;
1552 if (rmd_is_provided) {
1553 /* In this case the change_replmetadata control was supplied */
1554 /* We check that it's the only attribute that is provided
1555 * (it's a rare case so it's better to keep the code simplier)
1556 * We also check that the highest local_usn is bigger or the same as
1559 if( msg->num_elements != 1 ||
1560 strncmp(msg->elements[0].name,
1561 "replPropertyMetaData", 20) ) {
1562 DEBUG(0,(__location__ ": changereplmetada control called without "\
1563 "a specified replPropertyMetaData attribute or with others\n"));
1564 return LDB_ERR_OPERATIONS_ERROR;
1566 if (situation != REPL_URGENT_ON_UPDATE) {
1567 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1568 return LDB_ERR_OPERATIONS_ERROR;
1570 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1572 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1573 ldb_dn_get_linearized(msg->dn)));
1574 return LDB_ERR_OPERATIONS_ERROR;
1576 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1577 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1578 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1579 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1580 ldb_dn_get_linearized(msg->dn)));
1581 return LDB_ERR_OPERATIONS_ERROR;
1584 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1585 DSDB_FLAG_NEXT_MODULE |
1586 DSDB_SEARCH_SHOW_RECYCLED |
1587 DSDB_SEARCH_SHOW_EXTENDED_DN |
1588 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1589 DSDB_SEARCH_REVEAL_INTERNALS, req);
1591 if (ret != LDB_SUCCESS) {
1595 if (rmd_is_just_resorted == false) {
1596 *seq_num = find_max_local_usn(omd);
1598 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1601 * The test here now allows for a new
1602 * replPropertyMetaData with no change, if was
1603 * just dbcheck re-sorting the values.
1605 if (*seq_num <= db_seq) {
1606 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1607 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1608 (long long)*seq_num, (long long)db_seq));
1609 return LDB_ERR_OPERATIONS_ERROR;
1614 /* search for the existing replPropertyMetaDataBlob. We need
1615 * to use REVEAL and ask for DNs in storage format to support
1616 * the check for values being the same in
1617 * replmd_update_rpmd_element()
1619 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1620 DSDB_FLAG_NEXT_MODULE |
1621 DSDB_SEARCH_SHOW_RECYCLED |
1622 DSDB_SEARCH_SHOW_EXTENDED_DN |
1623 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1624 DSDB_SEARCH_REVEAL_INTERNALS, req);
1625 if (ret != LDB_SUCCESS) {
1629 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1631 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1632 ldb_dn_get_linearized(msg->dn)));
1633 return LDB_ERR_OPERATIONS_ERROR;
1636 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1637 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1638 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1639 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1640 ldb_dn_get_linearized(msg->dn)));
1641 return LDB_ERR_OPERATIONS_ERROR;
1644 if (omd.version != 1) {
1645 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1646 omd.version, ldb_dn_get_linearized(msg->dn)));
1647 return LDB_ERR_OPERATIONS_ERROR;
1650 for (i=0; i<msg->num_elements;) {
1651 struct ldb_message_element *el = &msg->elements[i];
1652 struct ldb_message_element *old_el;
1654 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1655 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1656 &omd, schema, seq_num,
1660 if (ret != LDB_SUCCESS) {
1664 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1665 *is_urgent = replmd_check_urgent_attribute(el);
1668 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1673 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1675 if (el->num_values != 0) {
1680 ldb_msg_remove_element(msg, el);
1685 * Assert that we have an objectClass attribute - this is major
1686 * corruption if we don't have this!
1688 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1689 if (objectclass_el != NULL) {
1691 * Now check if this objectClass means we need to do urgent replication
1693 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1697 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1698 ldb_asprintf_errstring(ldb, __location__
1699 ": objectClass missing on %s\n",
1700 ldb_dn_get_linearized(msg->dn));
1701 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1705 * replmd_update_rpmd_element has done an update if the
1708 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1709 struct ldb_val *md_value;
1710 struct ldb_message_element *el;
1712 /*if we are RODC and this is a DRSR update then its ok*/
1713 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1714 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1715 unsigned instanceType;
1717 ret = samdb_rodc(ldb, rodc);
1718 if (ret != LDB_SUCCESS) {
1719 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1721 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1722 return LDB_ERR_REFERRAL;
1725 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1726 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1727 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1728 "cannot change replicated attribute on partial replica");
1732 md_value = talloc(msg, struct ldb_val);
1733 if (md_value == NULL) {
1735 return LDB_ERR_OPERATIONS_ERROR;
1738 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1739 if (ret != LDB_SUCCESS) {
1740 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1744 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1745 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1746 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1747 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1748 ldb_dn_get_linearized(msg->dn)));
1749 return LDB_ERR_OPERATIONS_ERROR;
1752 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1753 if (ret != LDB_SUCCESS) {
1754 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1755 ldb_dn_get_linearized(msg->dn)));
1760 el->values = md_value;
1767 struct dsdb_dn *dsdb_dn;
1772 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1774 return GUID_compare(&pdn1->guid, &pdn2->guid);
1777 static int GUID_compare_struct(struct GUID *g1, struct GUID g2)
1779 return GUID_compare(g1, &g2);
1782 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1783 unsigned int count, struct GUID *guid,
1786 struct parsed_dn *ret;
1788 if (dn && GUID_all_zero(guid)) {
1789 /* when updating a link using DRS, we sometimes get a
1790 NULL GUID. We then need to try and match by DN */
1791 for (i=0; i<count; i++) {
1792 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1793 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1799 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare_struct, ret);
1804 get a series of message element values as an array of DNs and GUIDs
1805 the result is sorted by GUID
1807 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1808 struct ldb_message_element *el, struct parsed_dn **pdn,
1809 const char *ldap_oid, struct ldb_request *parent)
1812 struct ldb_context *ldb = ldb_module_get_ctx(module);
1819 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1821 ldb_module_oom(module);
1822 return LDB_ERR_OPERATIONS_ERROR;
1825 for (i=0; i<el->num_values; i++) {
1826 struct ldb_val *v = &el->values[i];
1829 struct parsed_dn *p;
1833 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1834 if (p->dsdb_dn == NULL) {
1835 return LDB_ERR_INVALID_DN_SYNTAX;
1838 dn = p->dsdb_dn->dn;
1840 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
1841 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1842 /* we got a DN without a GUID - go find the GUID */
1843 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
1844 if (ret != LDB_SUCCESS) {
1845 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1846 ldb_dn_get_linearized(dn));
1847 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1848 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1849 ldb_attr_cmp(el->name, "member") == 0) {
1850 return LDB_ERR_UNWILLING_TO_PERFORM;
1854 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
1855 if (ret != LDB_SUCCESS) {
1858 } else if (!NT_STATUS_IS_OK(status)) {
1859 return LDB_ERR_OPERATIONS_ERROR;
1862 /* keep a pointer to the original ldb_val */
1866 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1872 build a new extended DN, including all meta data fields
1874 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1875 RMD_ADDTIME = originating_add_time
1876 RMD_INVOCID = originating_invocation_id
1877 RMD_CHANGETIME = originating_change_time
1878 RMD_ORIGINATING_USN = originating_usn
1879 RMD_LOCAL_USN = local_usn
1880 RMD_VERSION = version
1882 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1883 const struct GUID *invocation_id, uint64_t seq_num,
1884 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1886 struct ldb_dn *dn = dsdb_dn->dn;
1887 const char *tstring, *usn_string, *flags_string;
1888 struct ldb_val tval;
1890 struct ldb_val usnv, local_usnv;
1891 struct ldb_val vers, flagsv;
1894 const char *dnstring;
1896 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1898 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1900 return LDB_ERR_OPERATIONS_ERROR;
1902 tval = data_blob_string_const(tstring);
1904 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1906 return LDB_ERR_OPERATIONS_ERROR;
1908 usnv = data_blob_string_const(usn_string);
1910 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1912 return LDB_ERR_OPERATIONS_ERROR;
1914 local_usnv = data_blob_string_const(usn_string);
1916 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1918 return LDB_ERR_OPERATIONS_ERROR;
1920 vers = data_blob_string_const(vstring);
1922 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1923 if (!NT_STATUS_IS_OK(status)) {
1924 return LDB_ERR_OPERATIONS_ERROR;
1927 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1928 if (!flags_string) {
1929 return LDB_ERR_OPERATIONS_ERROR;
1931 flagsv = data_blob_string_const(flags_string);
1933 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1934 if (ret != LDB_SUCCESS) return ret;
1935 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1936 if (ret != LDB_SUCCESS) return ret;
1937 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1938 if (ret != LDB_SUCCESS) return ret;
1939 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1940 if (ret != LDB_SUCCESS) return ret;
1941 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1942 if (ret != LDB_SUCCESS) return ret;
1943 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1944 if (ret != LDB_SUCCESS) return ret;
1945 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1946 if (ret != LDB_SUCCESS) return ret;
1948 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1949 if (dnstring == NULL) {
1950 return LDB_ERR_OPERATIONS_ERROR;
1952 *v = data_blob_string_const(dnstring);
1957 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1958 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1959 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1960 uint32_t version, bool deleted);
1963 check if any links need upgrading from w2k format
1965 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.
1967 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1970 for (i=0; i<count; i++) {
1975 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1976 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1980 /* it's an old one that needs upgrading */
1981 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1983 if (ret != LDB_SUCCESS) {
1991 update an extended DN, including all meta data fields
1993 see replmd_build_la_val for value names
1995 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1996 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1997 uint64_t usn, uint64_t local_usn, NTTIME nttime,
1998 uint32_t version, bool deleted)
2000 struct ldb_dn *dn = dsdb_dn->dn;
2001 const char *tstring, *usn_string, *flags_string;
2002 struct ldb_val tval;
2004 struct ldb_val usnv, local_usnv;
2005 struct ldb_val vers, flagsv;
2006 const struct ldb_val *old_addtime;
2007 uint32_t old_version;
2010 const char *dnstring;
2012 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2014 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2016 return LDB_ERR_OPERATIONS_ERROR;
2018 tval = data_blob_string_const(tstring);
2020 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2022 return LDB_ERR_OPERATIONS_ERROR;
2024 usnv = data_blob_string_const(usn_string);
2026 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2028 return LDB_ERR_OPERATIONS_ERROR;
2030 local_usnv = data_blob_string_const(usn_string);
2032 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2033 if (!NT_STATUS_IS_OK(status)) {
2034 return LDB_ERR_OPERATIONS_ERROR;
2037 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2038 if (!flags_string) {
2039 return LDB_ERR_OPERATIONS_ERROR;
2041 flagsv = data_blob_string_const(flags_string);
2043 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2044 if (ret != LDB_SUCCESS) return ret;
2046 /* get the ADDTIME from the original */
2047 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2048 if (old_addtime == NULL) {
2049 old_addtime = &tval;
2051 if (dsdb_dn != old_dsdb_dn ||
2052 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2053 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2054 if (ret != LDB_SUCCESS) return ret;
2057 /* use our invocation id */
2058 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2059 if (ret != LDB_SUCCESS) return ret;
2061 /* changetime is the current time */
2062 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2063 if (ret != LDB_SUCCESS) return ret;
2065 /* update the USN */
2066 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2067 if (ret != LDB_SUCCESS) return ret;
2069 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2070 if (ret != LDB_SUCCESS) return ret;
2072 /* increase the version by 1 */
2073 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2074 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2075 version = old_version+1;
2077 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2078 vers = data_blob_string_const(vstring);
2079 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2080 if (ret != LDB_SUCCESS) return ret;
2082 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2083 if (dnstring == NULL) {
2084 return LDB_ERR_OPERATIONS_ERROR;
2086 *v = data_blob_string_const(dnstring);
2092 handle adding a linked attribute
2094 static int replmd_modify_la_add(struct ldb_module *module,
2095 const struct dsdb_schema *schema,
2096 struct ldb_message *msg,
2097 struct ldb_message_element *el,
2098 struct ldb_message_element *old_el,
2099 const struct dsdb_attribute *schema_attr,
2102 struct GUID *msg_guid,
2103 struct ldb_request *parent)
2106 struct parsed_dn *dns, *old_dns;
2107 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2109 struct ldb_val *new_values = NULL;
2110 unsigned int num_new_values = 0;
2111 unsigned old_num_values = old_el?old_el->num_values:0;
2112 const struct GUID *invocation_id;
2113 struct ldb_context *ldb = ldb_module_get_ctx(module);
2116 unix_to_nt_time(&now, t);
2118 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2119 if (ret != LDB_SUCCESS) {
2120 talloc_free(tmp_ctx);
2124 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2125 if (ret != LDB_SUCCESS) {
2126 talloc_free(tmp_ctx);
2130 invocation_id = samdb_ntds_invocation_id(ldb);
2131 if (!invocation_id) {
2132 talloc_free(tmp_ctx);
2133 return LDB_ERR_OPERATIONS_ERROR;
2136 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2137 if (ret != LDB_SUCCESS) {
2138 talloc_free(tmp_ctx);
2142 /* for each new value, see if it exists already with the same GUID */
2143 for (i=0; i<el->num_values; i++) {
2144 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, &dns[i].guid, NULL);
2146 /* this is a new linked attribute value */
2147 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
2148 if (new_values == NULL) {
2149 ldb_module_oom(module);
2150 talloc_free(tmp_ctx);
2151 return LDB_ERR_OPERATIONS_ERROR;
2153 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2154 invocation_id, seq_num, seq_num, now, 0, false);
2155 if (ret != LDB_SUCCESS) {
2156 talloc_free(tmp_ctx);
2161 /* this is only allowed if the GUID was
2162 previously deleted. */
2163 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2165 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2166 struct GUID_txt_buf guid_str;
2167 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2168 el->name, GUID_buf_string(&p->guid, &guid_str));
2169 talloc_free(tmp_ctx);
2170 /* error codes for 'member' need to be
2172 if (ldb_attr_cmp(el->name, "member") == 0) {
2173 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2175 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2178 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2179 invocation_id, seq_num, seq_num, now, 0, false);
2180 if (ret != LDB_SUCCESS) {
2181 talloc_free(tmp_ctx);
2186 ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, true);
2187 if (ret != LDB_SUCCESS) {
2188 talloc_free(tmp_ctx);
2193 /* add the new ones on to the end of the old values, constructing a new el->values */
2194 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2196 old_num_values+num_new_values);
2197 if (el->values == NULL) {
2198 ldb_module_oom(module);
2199 return LDB_ERR_OPERATIONS_ERROR;
2202 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2203 el->num_values = old_num_values + num_new_values;
2205 talloc_steal(msg->elements, el->values);
2206 talloc_steal(el->values, new_values);
2208 talloc_free(tmp_ctx);
2210 /* we now tell the backend to replace all existing values
2211 with the one we have constructed */
2212 el->flags = LDB_FLAG_MOD_REPLACE;
2219 handle deleting all active linked attributes
2221 static int replmd_modify_la_delete(struct ldb_module *module,
2222 const struct dsdb_schema *schema,
2223 struct ldb_message *msg,
2224 struct ldb_message_element *el,
2225 struct ldb_message_element *old_el,
2226 const struct dsdb_attribute *schema_attr,
2229 struct GUID *msg_guid,
2230 struct ldb_request *parent)
2233 struct parsed_dn *dns, *old_dns;
2234 TALLOC_CTX *tmp_ctx = NULL;
2236 const struct GUID *invocation_id;
2237 struct ldb_context *ldb = ldb_module_get_ctx(module);
2238 struct ldb_control *vanish_links_ctrl = NULL;
2239 bool vanish_links = false;
2240 unsigned int num_to_delete = el->num_values;
2243 unix_to_nt_time(&now, t);
2245 /* check if there is nothing to delete */
2246 if ((!old_el || old_el->num_values == 0) &&
2247 el->num_values == 0) {
2251 if (!old_el || old_el->num_values == 0) {
2252 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2255 tmp_ctx = talloc_new(msg);
2256 if (tmp_ctx == NULL) {
2257 return LDB_ERR_OPERATIONS_ERROR;
2260 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2261 if (ret != LDB_SUCCESS) {
2262 talloc_free(tmp_ctx);
2266 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2267 if (ret != LDB_SUCCESS) {
2268 talloc_free(tmp_ctx);
2272 invocation_id = samdb_ntds_invocation_id(ldb);
2273 if (!invocation_id) {
2274 talloc_free(tmp_ctx);
2275 return LDB_ERR_OPERATIONS_ERROR;
2278 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2279 if (ret != LDB_SUCCESS) {
2280 talloc_free(tmp_ctx);
2285 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2286 if (vanish_links_ctrl) {
2287 vanish_links = true;
2288 vanish_links_ctrl->critical = false;
2295 /* see if we are being asked to delete any links that
2296 don't exist or are already deleted */
2297 for (i=0; i < num_to_delete; i++) {
2298 struct parsed_dn *p = &dns[i];
2299 struct parsed_dn *p2;
2302 p2 = parsed_dn_find(old_dns, old_el->num_values, &p->guid, NULL);
2304 struct GUID_txt_buf buf;
2305 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2306 el->name, GUID_buf_string(&p->guid, &buf));
2307 if (ldb_attr_cmp(el->name, "member") == 0) {
2308 talloc_free(tmp_ctx);
2309 return LDB_ERR_UNWILLING_TO_PERFORM;
2311 talloc_free(tmp_ctx);
2312 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2315 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2316 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2317 struct GUID_txt_buf buf;
2318 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2320 DEBUG(0, ("Deleting deleted linked attribute %s to %s, "
2321 "because vanish_links control is set\n",
2322 el->name, guid_str));
2325 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2326 el->name, guid_str);
2327 if (ldb_attr_cmp(el->name, "member") == 0) {
2328 talloc_free(tmp_ctx);
2329 return LDB_ERR_UNWILLING_TO_PERFORM;
2331 talloc_free(tmp_ctx);
2332 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2338 if (num_to_delete == old_el->num_values || num_to_delete == 0) {
2339 el->flags = LDB_FLAG_MOD_REPLACE;
2341 for (i = 0; i < old_el->num_values; i++) {
2342 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2343 if (ret != LDB_SUCCESS) {
2344 talloc_free(tmp_ctx);
2348 talloc_free(tmp_ctx);
2351 unsigned int num_values = 0;
2353 for (i = 0; i < old_el->num_values; i++) {
2354 if (parsed_dn_find(dns, num_to_delete, &old_dns[i].guid, NULL) != NULL) {
2355 /* The element is in the delete list. mark it dead. */
2356 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2357 if (ret != LDB_SUCCESS) {
2358 talloc_free(tmp_ctx);
2361 old_dns[i].v->length = 0;
2366 for (i = 0; i < old_el->num_values; i++) {
2367 if (old_el->values[i].length != 0) {
2368 old_el->values[j] = old_el->values[i];
2370 if (j == num_values) {
2375 old_el->num_values = num_values;
2379 /* for each new value, see if it exists already with the same GUID
2380 if it is not already deleted and matches the delete list then delete it
2382 for (i=0; i<old_el->num_values; i++) {
2383 struct parsed_dn *p = &old_dns[i];
2386 if (num_to_delete && parsed_dn_find(dns, num_to_delete, &p->guid, NULL) == NULL) {
2390 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2391 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2393 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2394 invocation_id, seq_num, seq_num, now, 0, true);
2395 if (ret != LDB_SUCCESS) {
2396 talloc_free(tmp_ctx);
2399 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2400 if (ret != LDB_SUCCESS) {
2401 talloc_free(tmp_ctx);
2406 el->values = talloc_steal(msg->elements, old_el->values);
2407 el->num_values = old_el->num_values;
2409 talloc_free(tmp_ctx);
2411 /* we now tell the backend to replace all existing values
2412 with the one we have constructed */
2413 el->flags = LDB_FLAG_MOD_REPLACE;
2419 handle replacing a linked attribute
2421 static int replmd_modify_la_replace(struct ldb_module *module,
2422 const struct dsdb_schema *schema,
2423 struct ldb_message *msg,
2424 struct ldb_message_element *el,
2425 struct ldb_message_element *old_el,
2426 const struct dsdb_attribute *schema_attr,
2429 struct GUID *msg_guid,
2430 struct ldb_request *parent)
2433 struct parsed_dn *dns, *old_dns;
2434 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2436 const struct GUID *invocation_id;
2437 struct ldb_context *ldb = ldb_module_get_ctx(module);
2438 struct ldb_val *new_values = NULL;
2439 unsigned int num_new_values = 0;
2440 unsigned int old_num_values = old_el?old_el->num_values:0;
2443 unix_to_nt_time(&now, t);
2445 /* check if there is nothing to replace */
2446 if ((!old_el || old_el->num_values == 0) &&
2447 el->num_values == 0) {
2451 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2452 if (ret != LDB_SUCCESS) {
2453 talloc_free(tmp_ctx);
2457 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2458 if (ret != LDB_SUCCESS) {
2459 talloc_free(tmp_ctx);
2463 invocation_id = samdb_ntds_invocation_id(ldb);
2464 if (!invocation_id) {
2465 return LDB_ERR_OPERATIONS_ERROR;
2468 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2469 if (ret != LDB_SUCCESS) {
2470 talloc_free(tmp_ctx);
2474 /* mark all the old ones as deleted */
2475 for (i=0; i<old_num_values; i++) {
2476 struct parsed_dn *old_p = &old_dns[i];
2477 struct parsed_dn *p;
2478 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2480 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2482 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, false);
2483 if (ret != LDB_SUCCESS) {
2484 talloc_free(tmp_ctx);
2488 p = parsed_dn_find(dns, el->num_values, &old_p->guid, NULL);
2490 /* we don't delete it if we are re-adding it */
2494 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2495 invocation_id, seq_num, seq_num, now, 0, true);
2496 if (ret != LDB_SUCCESS) {
2497 talloc_free(tmp_ctx);
2502 /* for each new value, either update its meta-data, or add it
2505 for (i=0; i<el->num_values; i++) {
2506 struct parsed_dn *p = &dns[i], *old_p;
2509 (old_p = parsed_dn_find(old_dns,
2510 old_num_values, &p->guid, NULL)) != NULL) {
2511 /* update in place */
2512 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2513 old_p->dsdb_dn, invocation_id,
2514 seq_num, seq_num, now, 0, false);
2515 if (ret != LDB_SUCCESS) {
2516 talloc_free(tmp_ctx);
2521 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2523 if (new_values == NULL) {
2524 ldb_module_oom(module);
2525 talloc_free(tmp_ctx);
2526 return LDB_ERR_OPERATIONS_ERROR;
2528 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2529 invocation_id, seq_num, seq_num, now, 0, false);
2530 if (ret != LDB_SUCCESS) {
2531 talloc_free(tmp_ctx);
2537 ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, false);
2538 if (ret != LDB_SUCCESS) {
2539 talloc_free(tmp_ctx);
2544 /* add the new values to the end of old_el */
2545 if (num_new_values != 0) {
2546 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2547 struct ldb_val, old_num_values+num_new_values);
2548 if (el->values == NULL) {
2549 ldb_module_oom(module);
2550 return LDB_ERR_OPERATIONS_ERROR;
2552 memcpy(&el->values[old_num_values], &new_values[0],
2553 sizeof(struct ldb_val)*num_new_values);
2554 el->num_values = old_num_values + num_new_values;
2555 talloc_steal(msg->elements, new_values);
2557 el->values = old_el->values;
2558 el->num_values = old_el->num_values;
2559 talloc_steal(msg->elements, el->values);
2562 talloc_free(tmp_ctx);
2564 /* we now tell the backend to replace all existing values
2565 with the one we have constructed */
2566 el->flags = LDB_FLAG_MOD_REPLACE;
2573 handle linked attributes in modify requests
2575 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2576 struct ldb_message *msg,
2577 uint64_t seq_num, time_t t,
2578 struct ldb_request *parent)
2580 struct ldb_result *res;
2583 struct ldb_context *ldb = ldb_module_get_ctx(module);
2584 struct ldb_message *old_msg;
2586 const struct dsdb_schema *schema;
2587 struct GUID old_guid;
2589 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2591 * Nothing special is required for modifying or vanishing links
2592 * in fl2000 since they are just strings in a multi-valued
2595 struct ldb_control *ctrl = ldb_request_get_control(parent,
2596 DSDB_CONTROL_REPLMD_VANISH_LINKS);
2598 ctrl->critical = false;
2606 * We should restrict this to the intersection of the list of
2607 * linked attributes in the schema and the list of attributes
2610 * This will help performance a little, as otherwise we have
2611 * to allocate the entire object value-by-value.
2613 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2614 DSDB_FLAG_NEXT_MODULE |
2615 DSDB_SEARCH_SHOW_RECYCLED |
2616 DSDB_SEARCH_REVEAL_INTERNALS |
2617 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2619 if (ret != LDB_SUCCESS) {
2622 schema = dsdb_get_schema(ldb, res);
2624 return LDB_ERR_OPERATIONS_ERROR;
2627 old_msg = res->msgs[0];
2629 old_guid = samdb_result_guid(old_msg, "objectGUID");
2631 for (i=0; i<msg->num_elements; i++) {
2632 struct ldb_message_element *el = &msg->elements[i];
2633 struct ldb_message_element *old_el, *new_el;
2634 const struct dsdb_attribute *schema_attr
2635 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2637 ldb_asprintf_errstring(ldb,
2638 "%s: attribute %s is not a valid attribute in schema",
2639 __FUNCTION__, el->name);
2640 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2642 if (schema_attr->linkID == 0) {
2645 if ((schema_attr->linkID & 1) == 1) {
2646 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2649 /* Odd is for the target. Illegal to modify */
2650 ldb_asprintf_errstring(ldb,
2651 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2652 return LDB_ERR_UNWILLING_TO_PERFORM;
2654 old_el = ldb_msg_find_element(old_msg, el->name);
2655 switch (el->flags & LDB_FLAG_MOD_MASK) {
2656 case LDB_FLAG_MOD_REPLACE:
2657 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2659 case LDB_FLAG_MOD_DELETE:
2660 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2662 case LDB_FLAG_MOD_ADD:
2663 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2666 ldb_asprintf_errstring(ldb,
2667 "invalid flags 0x%x for %s linked attribute",
2668 el->flags, el->name);
2669 return LDB_ERR_UNWILLING_TO_PERFORM;
2671 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2672 ldb_asprintf_errstring(ldb,
2673 "Attribute %s is single valued but more than one value has been supplied",
2675 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2677 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2682 if (ret != LDB_SUCCESS) {
2686 ldb_msg_remove_attr(old_msg, el->name);
2688 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2689 new_el->num_values = el->num_values;
2690 new_el->values = talloc_steal(msg->elements, el->values);
2692 /* TODO: this relises a bit too heavily on the exact
2693 behaviour of ldb_msg_find_element and
2694 ldb_msg_remove_element */
2695 old_el = ldb_msg_find_element(msg, el->name);
2697 ldb_msg_remove_element(msg, old_el);
2708 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2710 struct ldb_context *ldb;
2711 struct replmd_replicated_request *ac;
2712 struct ldb_request *down_req;
2713 struct ldb_message *msg;
2714 time_t t = time(NULL);
2716 bool is_urgent = false, rodc = false;
2717 bool is_schema_nc = false;
2718 unsigned int functional_level;
2719 const struct ldb_message_element *guid_el = NULL;
2720 struct ldb_control *sd_propagation_control;
2721 struct replmd_private *replmd_private =
2722 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2724 /* do not manipulate our control entries */
2725 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2726 return ldb_next_request(module, req);
2729 sd_propagation_control = ldb_request_get_control(req,
2730 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2731 if (sd_propagation_control != NULL) {
2732 if (req->op.mod.message->num_elements != 1) {
2733 return ldb_module_operr(module);
2735 ret = strcmp(req->op.mod.message->elements[0].name,
2736 "nTSecurityDescriptor");
2738 return ldb_module_operr(module);
2741 return ldb_next_request(module, req);
2744 ldb = ldb_module_get_ctx(module);
2746 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2748 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2749 if (guid_el != NULL) {
2750 ldb_set_errstring(ldb,
2751 "replmd_modify: it's not allowed to change the objectGUID!");
2752 return LDB_ERR_CONSTRAINT_VIOLATION;
2755 ac = replmd_ctx_init(module, req);
2757 return ldb_module_oom(module);
2760 functional_level = dsdb_functional_level(ldb);
2762 /* we have to copy the message as the caller might have it as a const */
2763 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2767 return LDB_ERR_OPERATIONS_ERROR;
2770 ldb_msg_remove_attr(msg, "whenChanged");
2771 ldb_msg_remove_attr(msg, "uSNChanged");
2773 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2775 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2776 msg, &ac->seq_num, t, is_schema_nc,
2778 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2779 struct loadparm_context *lp_ctx;
2782 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2783 struct loadparm_context);
2785 referral = talloc_asprintf(req,
2787 lpcfg_dnsdomain(lp_ctx),
2788 ldb_dn_get_linearized(msg->dn));
2789 ret = ldb_module_send_referral(req, referral);
2794 if (ret != LDB_SUCCESS) {
2799 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2800 if (ret != LDB_SUCCESS) {
2806 * - replace the old object with the newly constructed one
2809 ac->is_urgent = is_urgent;
2811 ret = ldb_build_mod_req(&down_req, ldb, ac,
2814 ac, replmd_op_callback,
2816 LDB_REQ_SET_LOCATION(down_req);
2817 if (ret != LDB_SUCCESS) {
2822 /* current partition control is needed by "replmd_op_callback" */
2823 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2824 ret = ldb_request_add_control(down_req,
2825 DSDB_CONTROL_CURRENT_PARTITION_OID,
2827 if (ret != LDB_SUCCESS) {
2833 /* If we are in functional level 2000, then
2834 * replmd_modify_handle_linked_attribs will have done
2836 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2837 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2838 if (ret != LDB_SUCCESS) {
2844 talloc_steal(down_req, msg);
2846 /* we only change whenChanged and uSNChanged if the seq_num
2848 if (ac->seq_num != 0) {
2849 ret = add_time_element(msg, "whenChanged", t);
2850 if (ret != LDB_SUCCESS) {
2856 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2857 if (ret != LDB_SUCCESS) {
2864 /* go on with the call chain */
2865 return ldb_next_request(module, down_req);
2868 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2871 handle a rename request
2873 On a rename we need to do an extra ldb_modify which sets the
2874 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2876 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2878 struct ldb_context *ldb;
2879 struct replmd_replicated_request *ac;
2881 struct ldb_request *down_req;
2883 /* do not manipulate our control entries */
2884 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2885 return ldb_next_request(module, req);
2888 ldb = ldb_module_get_ctx(module);
2890 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2892 ac = replmd_ctx_init(module, req);
2894 return ldb_module_oom(module);
2897 ret = ldb_build_rename_req(&down_req, ldb, ac,
2898 ac->req->op.rename.olddn,
2899 ac->req->op.rename.newdn,
2901 ac, replmd_rename_callback,
2903 LDB_REQ_SET_LOCATION(down_req);
2904 if (ret != LDB_SUCCESS) {
2909 /* go on with the call chain */
2910 return ldb_next_request(module, down_req);
2913 /* After the rename is compleated, update the whenchanged etc */
2914 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2916 struct ldb_context *ldb;
2917 struct ldb_request *down_req;
2918 struct ldb_message *msg;
2919 const struct dsdb_attribute *rdn_attr;
2920 const char *rdn_name;
2921 const struct ldb_val *rdn_val;
2922 const char *attrs[5] = { NULL, };
2923 time_t t = time(NULL);
2925 bool is_urgent = false, rodc = false;
2927 struct replmd_replicated_request *ac =
2928 talloc_get_type(req->context, struct replmd_replicated_request);
2929 struct replmd_private *replmd_private =
2930 talloc_get_type(ldb_module_get_private(ac->module),
2931 struct replmd_private);
2933 ldb = ldb_module_get_ctx(ac->module);
2935 if (ares->error != LDB_SUCCESS) {
2936 return ldb_module_done(ac->req, ares->controls,
2937 ares->response, ares->error);
2940 if (ares->type != LDB_REPLY_DONE) {
2941 ldb_set_errstring(ldb,
2942 "invalid ldb_reply_type in callback");
2944 return ldb_module_done(ac->req, NULL, NULL,
2945 LDB_ERR_OPERATIONS_ERROR);
2949 * - replace the old object with the newly constructed one
2952 msg = ldb_msg_new(ac);
2955 return LDB_ERR_OPERATIONS_ERROR;
2958 msg->dn = ac->req->op.rename.newdn;
2960 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2962 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2963 if (rdn_name == NULL) {
2965 return ldb_module_done(ac->req, NULL, NULL,
2969 /* normalize the rdn attribute name */
2970 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2971 if (rdn_attr == NULL) {
2973 return ldb_module_done(ac->req, NULL, NULL,
2976 rdn_name = rdn_attr->lDAPDisplayName;
2978 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2979 if (rdn_val == NULL) {
2981 return ldb_module_done(ac->req, NULL, NULL,
2985 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2987 return ldb_module_done(ac->req, NULL, NULL,
2990 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2992 return ldb_module_done(ac->req, NULL, NULL,
2995 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2997 return ldb_module_done(ac->req, NULL, NULL,
3000 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3002 return ldb_module_done(ac->req, NULL, NULL,
3007 * here we let replmd_update_rpmd() only search for
3008 * the existing "replPropertyMetaData" and rdn_name attributes.
3010 * We do not want the existing "name" attribute as
3011 * the "name" attribute needs to get the version
3012 * updated on rename even if the rdn value hasn't changed.
3014 * This is the diff of the meta data, for a moved user
3015 * on a w2k8r2 server:
3018 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3019 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3020 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3021 * version : 0x00000001 (1)
3022 * reserved : 0x00000000 (0)
3023 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3024 * local_usn : 0x00000000000037a5 (14245)
3025 * array: struct replPropertyMetaData1
3026 * attid : DRSUAPI_ATTID_name (0x90001)
3027 * - version : 0x00000001 (1)
3028 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3029 * + version : 0x00000002 (2)
3030 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3031 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3032 * - originating_usn : 0x00000000000037a5 (14245)
3033 * - local_usn : 0x00000000000037a5 (14245)
3034 * + originating_usn : 0x0000000000003834 (14388)
3035 * + local_usn : 0x0000000000003834 (14388)
3036 * array: struct replPropertyMetaData1
3037 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3038 * version : 0x00000004 (4)
3040 attrs[0] = "replPropertyMetaData";
3041 attrs[1] = "objectClass";
3042 attrs[2] = "instanceType";
3043 attrs[3] = rdn_name;
3046 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3047 msg, &ac->seq_num, t,
3048 is_schema_nc, &is_urgent, &rodc);
3049 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3050 struct ldb_dn *olddn = ac->req->op.rename.olddn;
3051 struct loadparm_context *lp_ctx;
3054 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3055 struct loadparm_context);
3057 referral = talloc_asprintf(req,
3059 lpcfg_dnsdomain(lp_ctx),
3060 ldb_dn_get_linearized(olddn));
3061 ret = ldb_module_send_referral(req, referral);
3063 return ldb_module_done(req, NULL, NULL, ret);
3066 if (ret != LDB_SUCCESS) {
3068 return ldb_module_done(ac->req, NULL, NULL, ret);
3071 if (ac->seq_num == 0) {
3073 return ldb_module_done(ac->req, NULL, NULL,
3075 "internal error seq_num == 0"));
3077 ac->is_urgent = is_urgent;
3079 ret = ldb_build_mod_req(&down_req, ldb, ac,
3082 ac, replmd_op_callback,
3084 LDB_REQ_SET_LOCATION(down_req);
3085 if (ret != LDB_SUCCESS) {
3090 /* current partition control is needed by "replmd_op_callback" */
3091 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3092 ret = ldb_request_add_control(down_req,
3093 DSDB_CONTROL_CURRENT_PARTITION_OID,
3095 if (ret != LDB_SUCCESS) {
3101 talloc_steal(down_req, msg);
3103 ret = add_time_element(msg, "whenChanged", t);
3104 if (ret != LDB_SUCCESS) {
3110 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3111 if (ret != LDB_SUCCESS) {
3117 /* go on with the call chain - do the modify after the rename */
3118 return ldb_next_request(ac->module, down_req);
3122 * remove links from objects that point at this object when an object
3123 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3124 * RemoveObj which states that link removal due to the object being
3125 * deleted is NOT an originating update - they just go away!
3128 static int replmd_delete_remove_link(struct ldb_module *module,
3129 const struct dsdb_schema *schema,
3131 struct ldb_message_element *el,
3132 const struct dsdb_attribute *sa,
3133 struct ldb_request *parent)
3136 TALLOC_CTX *tmp_ctx = talloc_new(module);
3137 struct ldb_context *ldb = ldb_module_get_ctx(module);
3139 for (i=0; i<el->num_values; i++) {
3140 struct dsdb_dn *dsdb_dn;
3144 struct ldb_message *msg;
3145 const struct dsdb_attribute *target_attr;
3146 struct ldb_message_element *el2;
3147 struct ldb_val dn_val;
3148 uint32_t dsdb_flags = 0;
3150 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3154 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3156 talloc_free(tmp_ctx);
3157 return LDB_ERR_OPERATIONS_ERROR;
3160 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
3161 if (!NT_STATUS_IS_OK(status)) {
3162 talloc_free(tmp_ctx);
3163 return LDB_ERR_OPERATIONS_ERROR;
3166 /* remove the link */
3167 msg = ldb_msg_new(tmp_ctx);
3169 ldb_module_oom(module);
3170 talloc_free(tmp_ctx);
3171 return LDB_ERR_OPERATIONS_ERROR;
3175 msg->dn = dsdb_dn->dn;
3177 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3178 if (target_attr == NULL) {
3182 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
3183 if (ret != LDB_SUCCESS) {
3184 ldb_module_oom(module);
3185 talloc_free(tmp_ctx);
3186 return LDB_ERR_OPERATIONS_ERROR;
3188 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
3189 el2->values = &dn_val;
3190 el2->num_values = 1;
3193 * Ensure that we tell the modification to vanish any linked
3194 * attributes (not simply mark them as isDeleted = TRUE)
3196 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3198 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3199 if (ret != LDB_SUCCESS) {
3200 talloc_free(tmp_ctx);
3204 talloc_free(tmp_ctx);
3210 handle update of replication meta data for deletion of objects
3212 This also handles the mapping of delete to a rename operation
3213 to allow deletes to be replicated.
3215 It also handles the incoming deleted objects, to ensure they are
3216 fully deleted here. In that case re_delete is true, and we do not
3217 use this as a signal to change the deleted state, just reinforce it.
3220 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3222 int ret = LDB_ERR_OTHER;
3223 bool retb, disallow_move_on_delete;
3224 struct ldb_dn *old_dn, *new_dn;
3225 const char *rdn_name;
3226 const struct ldb_val *rdn_value, *new_rdn_value;
3228 struct ldb_context *ldb = ldb_module_get_ctx(module);
3229 const struct dsdb_schema *schema;
3230 struct ldb_message *msg, *old_msg;
3231 struct ldb_message_element *el;
3232 TALLOC_CTX *tmp_ctx;
3233 struct ldb_result *res, *parent_res;
3234 static const char * const preserved_attrs[] = {
3235 /* yes, this really is a hard coded list. See MS-ADTS
3236 section 3.1.1.5.5.1.1 */
3239 "dNReferenceUpdate",
3250 "msDS-LastKnownRDN",
3256 "distinguishedName",
3260 "proxiedObjectName",
3262 "nTSecurityDescriptor",
3263 "replPropertyMetaData",
3265 "securityIdentifier",
3273 "userAccountControl",
3280 static const char * const all_attrs[] = {
3281 DSDB_SECRET_ATTRIBUTES,
3285 unsigned int i, el_count = 0;
3286 uint32_t dsdb_flags = 0;
3287 enum deletion_state deletion_state, next_deletion_state;
3289 if (ldb_dn_is_special(req->op.del.dn)) {
3290 return ldb_next_request(module, req);
3294 * We have to allow dbcheck to remove an object that
3295 * is beyond repair, and to do so totally. This could
3296 * mean we we can get a partial object from the other
3297 * DC, causing havoc, so dbcheck suggests
3298 * re-replication first. dbcheck sets both DBCHECK
3299 * and RELAX in this situation.
3301 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3302 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3303 /* really, really remove it */
3304 return ldb_next_request(module, req);
3307 tmp_ctx = talloc_new(ldb);
3310 return LDB_ERR_OPERATIONS_ERROR;
3313 schema = dsdb_get_schema(ldb, tmp_ctx);
3315 talloc_free(tmp_ctx);
3316 return LDB_ERR_OPERATIONS_ERROR;
3319 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3321 /* we need the complete msg off disk, so we can work out which
3322 attributes need to be removed */
3323 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3324 DSDB_FLAG_NEXT_MODULE |
3325 DSDB_SEARCH_SHOW_RECYCLED |
3326 DSDB_SEARCH_REVEAL_INTERNALS |
3327 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3328 if (ret != LDB_SUCCESS) {
3329 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3330 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3331 re_delete ? "re-delete" : "delete",
3332 ldb_dn_get_linearized(old_dn),
3333 ldb_errstring(ldb_module_get_ctx(module)));
3334 talloc_free(tmp_ctx);
3337 old_msg = res->msgs[0];
3339 replmd_deletion_state(module, old_msg,
3341 &next_deletion_state);
3343 /* This supports us noticing an incoming isDeleted and acting on it */
3345 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3346 next_deletion_state = deletion_state;
3349 if (next_deletion_state == OBJECT_REMOVED) {
3351 * We have to prevent objects being deleted, even if
3352 * the administrator really wants them gone, as
3353 * without the tombstone, we can get a partial object
3354 * from the other DC, causing havoc.
3356 * The only other valid case is when the 180 day
3357 * timeout has expired, when relax is specified.
3359 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3360 /* it is already deleted - really remove it this time */
3361 talloc_free(tmp_ctx);
3362 return ldb_next_request(module, req);
3365 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3366 "This check is to prevent corruption of the replicated state.",
3367 ldb_dn_get_linearized(old_msg->dn));
3368 return LDB_ERR_UNWILLING_TO_PERFORM;
3371 rdn_name = ldb_dn_get_rdn_name(old_dn);
3372 rdn_value = ldb_dn_get_rdn_val(old_dn);
3373 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3374 talloc_free(tmp_ctx);
3375 return ldb_operr(ldb);
3378 msg = ldb_msg_new(tmp_ctx);
3380 ldb_module_oom(module);
3381 talloc_free(tmp_ctx);
3382 return LDB_ERR_OPERATIONS_ERROR;
3387 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3388 disallow_move_on_delete =
3389 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3390 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3392 /* work out where we will be renaming this object to */
3393 if (!disallow_move_on_delete) {
3394 struct ldb_dn *deleted_objects_dn;
3395 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3396 &deleted_objects_dn);
3399 * We should not move objects if we can't find the
3400 * deleted objects DN. Not moving (or otherwise
3401 * harming) the Deleted Objects DN itself is handled
3404 if (re_delete && (ret != LDB_SUCCESS)) {
3405 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3406 if (new_dn == NULL) {
3407 ldb_module_oom(module);
3408 talloc_free(tmp_ctx);
3409 return LDB_ERR_OPERATIONS_ERROR;
3411 } else if (ret != LDB_SUCCESS) {
3412 /* this is probably an attempted delete on a partition
3413 * that doesn't allow delete operations, such as the
3414 * schema partition */
3415 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3416 ldb_dn_get_linearized(old_dn));
3417 talloc_free(tmp_ctx);
3418 return LDB_ERR_UNWILLING_TO_PERFORM;
3420 new_dn = deleted_objects_dn;
3423 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3424 if (new_dn == NULL) {
3425 ldb_module_oom(module);
3426 talloc_free(tmp_ctx);
3427 return LDB_ERR_OPERATIONS_ERROR;
3431 if (deletion_state == OBJECT_NOT_DELETED) {
3432 /* get the objects GUID from the search we just did */
3433 guid = samdb_result_guid(old_msg, "objectGUID");
3435 /* Add a formatted child */
3436 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3438 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3439 GUID_string(tmp_ctx, &guid));
3441 ldb_asprintf_errstring(ldb, __location__
3442 ": Unable to add a formatted child to dn: %s",
3443 ldb_dn_get_linearized(new_dn));
3444 talloc_free(tmp_ctx);
3445 return LDB_ERR_OPERATIONS_ERROR;
3448 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3449 if (ret != LDB_SUCCESS) {
3450 ldb_asprintf_errstring(ldb, __location__
3451 ": Failed to add isDeleted string to the msg");
3452 talloc_free(tmp_ctx);
3455 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3458 * No matter what has happened with other renames etc, try again to
3459 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3462 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3463 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3465 ldb_asprintf_errstring(ldb, __location__
3466 ": Unable to add a prepare rdn of %s",
3467 ldb_dn_get_linearized(rdn));
3468 talloc_free(tmp_ctx);
3469 return LDB_ERR_OPERATIONS_ERROR;
3471 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3473 retb = ldb_dn_add_child(new_dn, rdn);
3475 ldb_asprintf_errstring(ldb, __location__
3476 ": Unable to add rdn %s to base dn: %s",
3477 ldb_dn_get_linearized(rdn),
3478 ldb_dn_get_linearized(new_dn));
3479 talloc_free(tmp_ctx);
3480 return LDB_ERR_OPERATIONS_ERROR;
3485 now we need to modify the object in the following ways:
3487 - add isDeleted=TRUE
3488 - update rDN and name, with new rDN
3489 - remove linked attributes
3490 - remove objectCategory and sAMAccountType
3491 - remove attribs not on the preserved list
3492 - preserved if in above list, or is rDN
3493 - remove all linked attribs from this object
3494 - remove all links from other objects to this object
3495 - add lastKnownParent
3496 - update replPropertyMetaData?
3498 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3501 if (deletion_state == OBJECT_NOT_DELETED) {
3502 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3503 char *parent_dn_str = NULL;
3505 /* we need the storage form of the parent GUID */
3506 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3508 DSDB_FLAG_NEXT_MODULE |
3509 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3510 DSDB_SEARCH_REVEAL_INTERNALS|
3511 DSDB_SEARCH_SHOW_RECYCLED, req);
3512 if (ret != LDB_SUCCESS) {
3513 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3514 "repmd_delete: Failed to %s %s, "
3515 "because we failed to find it's parent (%s): %s",
3516 re_delete ? "re-delete" : "delete",
3517 ldb_dn_get_linearized(old_dn),
3518 ldb_dn_get_linearized(parent_dn),
3519 ldb_errstring(ldb_module_get_ctx(module)));
3520 talloc_free(tmp_ctx);
3525 * Now we can use the DB version,
3526 * it will have the extended DN info in it
3528 parent_dn = parent_res->msgs[0]->dn;
3529 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3532 if (parent_dn_str == NULL) {
3533 talloc_free(tmp_ctx);
3534 return ldb_module_oom(module);
3537 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3539 if (ret != LDB_SUCCESS) {
3540 ldb_asprintf_errstring(ldb, __location__
3541 ": Failed to add lastKnownParent "
3542 "string when deleting %s",
3543 ldb_dn_get_linearized(old_dn));
3544 talloc_free(tmp_ctx);
3547 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3549 if (next_deletion_state == OBJECT_DELETED) {
3550 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3551 if (ret != LDB_SUCCESS) {
3552 ldb_asprintf_errstring(ldb, __location__
3553 ": Failed to add msDS-LastKnownRDN "
3554 "string when deleting %s",
3555 ldb_dn_get_linearized(old_dn));
3556 talloc_free(tmp_ctx);
3559 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3563 switch (next_deletion_state) {
3565 case OBJECT_RECYCLED:
3566 case OBJECT_TOMBSTONE:
3569 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3570 * describes what must be removed from a tombstone
3573 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3574 * describes what must be removed from a recycled
3580 * we also mark it as recycled, meaning this object can't be
3581 * recovered (we are stripping its attributes).
3582 * This is done only if we have this schema object of course ...
3583 * This behavior is identical to the one of Windows 2008R2 which
3584 * always set the isRecycled attribute, even if the recycle-bin is
3585 * not activated and what ever the forest level is.
3587 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3588 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3589 if (ret != LDB_SUCCESS) {
3590 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3591 ldb_module_oom(module);
3592 talloc_free(tmp_ctx);
3595 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3598 /* work out which of the old attributes we will be removing */
3599 for (i=0; i<old_msg->num_elements; i++) {
3600 const struct dsdb_attribute *sa;
3601 el = &old_msg->elements[i];
3602 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3604 talloc_free(tmp_ctx);
3605 return LDB_ERR_OPERATIONS_ERROR;
3607 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3608 /* don't remove the rDN */
3611 if (sa->linkID && (sa->linkID & 1)) {
3613 we have a backlink in this object
3614 that needs to be removed. We're not
3615 allowed to remove it directly
3616 however, so we instead setup a
3617 modify to delete the corresponding
3620 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3621 if (ret != LDB_SUCCESS) {
3622 const char *old_dn_str
3623 = ldb_dn_get_linearized(old_dn);
3624 ldb_asprintf_errstring(ldb,
3626 ": Failed to remove backlink of "
3627 "%s when deleting %s: %s",
3630 ldb_errstring(ldb));
3631 talloc_free(tmp_ctx);
3632 return LDB_ERR_OPERATIONS_ERROR;
3634 /* now we continue, which means we
3635 won't remove this backlink
3641 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3644 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3649 * Ensure that we tell the modification to vanish any linked
3650 * attributes (not simply mark them as isDeleted = TRUE)
3652 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3654 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3655 if (ret != LDB_SUCCESS) {
3656 talloc_free(tmp_ctx);
3657 ldb_module_oom(module);
3664 case OBJECT_DELETED:
3666 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3667 * describes what must be removed from a deleted
3671 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3672 if (ret != LDB_SUCCESS) {
3673 talloc_free(tmp_ctx);
3674 ldb_module_oom(module);
3678 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3679 if (ret != LDB_SUCCESS) {
3680 talloc_free(tmp_ctx);
3681 ldb_module_oom(module);
3691 if (deletion_state == OBJECT_NOT_DELETED) {
3692 const struct dsdb_attribute *sa;
3694 /* work out what the new rdn value is, for updating the
3695 rDN and name fields */
3696 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3697 if (new_rdn_value == NULL) {
3698 talloc_free(tmp_ctx);
3699 return ldb_operr(ldb);
3702 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3704 talloc_free(tmp_ctx);
3705 return LDB_ERR_OPERATIONS_ERROR;
3708 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3710 if (ret != LDB_SUCCESS) {
3711 talloc_free(tmp_ctx);
3714 el->flags = LDB_FLAG_MOD_REPLACE;
3716 el = ldb_msg_find_element(old_msg, "name");
3718 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3719 if (ret != LDB_SUCCESS) {
3720 talloc_free(tmp_ctx);
3723 el->flags = LDB_FLAG_MOD_REPLACE;
3728 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3733 * No matter what has happned with other renames, try again to
3734 * get this to be under the deleted DN.
3736 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3737 /* now rename onto the new DN */
3738 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3739 if (ret != LDB_SUCCESS){
3740 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3741 ldb_dn_get_linearized(old_dn),
3742 ldb_dn_get_linearized(new_dn),
3743 ldb_errstring(ldb)));
3744 talloc_free(tmp_ctx);
3750 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
3751 if (ret != LDB_SUCCESS) {
3752 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3753 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3754 talloc_free(tmp_ctx);
3758 talloc_free(tmp_ctx);
3760 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3763 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3765 return replmd_delete_internals(module, req, false);
3769 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3774 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3776 int ret = LDB_ERR_OTHER;
3777 /* TODO: do some error mapping */
3779 /* Let the caller know the full WERROR */
3780 ar->objs->error = status;
3786 static struct replPropertyMetaData1 *
3787 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3788 enum drsuapi_DsAttributeId attid)
3791 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3793 for (i = 0; i < rpmd_ctr->count; i++) {
3794 if (rpmd_ctr->array[i].attid == attid) {
3795 return &rpmd_ctr->array[i];
3803 return true if an update is newer than an existing entry
3804 see section 5.11 of MS-ADTS
3806 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3807 const struct GUID *update_invocation_id,
3808 uint32_t current_version,
3809 uint32_t update_version,
3810 NTTIME current_change_time,
3811 NTTIME update_change_time)
3813 if (update_version != current_version) {
3814 return update_version > current_version;
3816 if (update_change_time != current_change_time) {
3817 return update_change_time > current_change_time;
3819 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3822 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3823 struct replPropertyMetaData1 *new_m)
3825 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3826 &new_m->originating_invocation_id,
3829 cur_m->originating_change_time,
3830 new_m->originating_change_time);
3833 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
3834 struct replPropertyMetaData1 *cur_m,
3835 struct replPropertyMetaData1 *new_m)
3840 * If the new replPropertyMetaData entry for this attribute is
3841 * not provided (this happens in the case where we look for
3842 * ATTID_name, but the name was not changed), then the local
3843 * state is clearly still current, as the remote
3844 * server didn't send it due to being older the high watermark
3847 if (new_m == NULL) {
3851 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3853 * if we compare equal then do an
3854 * update. This is used when a client
3855 * asks for a FULL_SYNC, and can be
3856 * used to recover a corrupt
3859 * This call is a bit tricky, what we
3860 * are doing it turning the 'is_newer'
3861 * call into a 'not is older' by
3862 * swapping cur_m and new_m, and negating the
3865 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
3868 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
3878 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3880 const struct ldb_val *rdn_val;
3881 const char *rdn_name;
3882 struct ldb_dn *new_dn;
3884 rdn_val = ldb_dn_get_rdn_val(dn);
3885 rdn_name = ldb_dn_get_rdn_name(dn);
3886 if (!rdn_val || !rdn_name) {
3890 new_dn = ldb_dn_copy(mem_ctx, dn);
3895 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3899 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3901 ldb_dn_escape_value(new_dn, *rdn_val),
3902 GUID_string(new_dn, guid))) {
3911 perform a modify operation which sets the rDN and name attributes to
3912 their current values. This has the effect of changing these
3913 attributes to have been last updated by the current DC. This is
3914 needed to ensure that renames performed as part of conflict
3915 resolution are propogated to other DCs
3917 static int replmd_name_modify(struct replmd_replicated_request *ar,
3918 struct ldb_request *req, struct ldb_dn *dn)
3920 struct ldb_message *msg;
3921 const char *rdn_name;
3922 const struct ldb_val *rdn_val;
3923 const struct dsdb_attribute *rdn_attr;
3926 msg = ldb_msg_new(req);
3932 rdn_name = ldb_dn_get_rdn_name(dn);
3933 if (rdn_name == NULL) {
3937 /* normalize the rdn attribute name */
3938 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3939 if (rdn_attr == NULL) {
3942 rdn_name = rdn_attr->lDAPDisplayName;
3944 rdn_val = ldb_dn_get_rdn_val(dn);
3945 if (rdn_val == NULL) {
3949 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3952 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3955 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3958 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3962 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3963 if (ret != LDB_SUCCESS) {
3964 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3965 ldb_dn_get_linearized(dn),
3966 ldb_errstring(ldb_module_get_ctx(ar->module))));
3976 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3977 ldb_dn_get_linearized(dn)));
3978 return LDB_ERR_OPERATIONS_ERROR;
3983 callback for conflict DN handling where we have renamed the incoming
3984 record. After renaming it, we need to ensure the change of name and
3985 rDN for the incoming record is seen as an originating update by this DC.
3987 This also handles updating lastKnownParent for entries sent to lostAndFound
3989 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3991 struct replmd_replicated_request *ar =
3992 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3993 struct ldb_dn *conflict_dn = NULL;
3996 if (ares->error != LDB_SUCCESS) {
3997 /* call the normal callback for everything except success */
3998 return replmd_op_callback(req, ares);
4001 switch (req->operation) {
4003 conflict_dn = req->op.add.message->dn;
4006 conflict_dn = req->op.mod.message->dn;
4009 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4012 /* perform a modify of the rDN and name of the record */
4013 ret = replmd_name_modify(ar, req, conflict_dn);
4014 if (ret != LDB_SUCCESS) {
4016 return replmd_op_callback(req, ares);
4019 if (ar->objs->objects[ar->index_current].last_known_parent) {
4020 struct ldb_message *msg = ldb_msg_new(req);
4022 ldb_module_oom(ar->module);
4023 return LDB_ERR_OPERATIONS_ERROR;
4026 msg->dn = req->op.add.message->dn;
4028 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4029 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4030 if (ret != LDB_SUCCESS) {
4031 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4032 ldb_module_oom(ar->module);
4035 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4037 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4038 if (ret != LDB_SUCCESS) {
4039 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4040 ldb_dn_get_linearized(msg->dn),
4041 ldb_errstring(ldb_module_get_ctx(ar->module))));
4047 return replmd_op_callback(req, ares);
4051 callback for replmd_replicated_apply_add()
4052 This copes with the creation of conflict records in the case where
4053 the DN exists, but with a different objectGUID
4055 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))
4057 struct ldb_dn *conflict_dn;
4058 struct replmd_replicated_request *ar =
4059 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4060 struct ldb_result *res;
4061 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4063 const struct ldb_val *omd_value;
4064 struct replPropertyMetaDataBlob omd, *rmd;
4065 enum ndr_err_code ndr_err;
4066 bool rename_incoming_record, rodc;
4067 struct replPropertyMetaData1 *rmd_name, *omd_name;
4068 struct ldb_message *msg;
4069 struct ldb_request *down_req = NULL;
4071 /* call the normal callback for success */
4072 if (ares->error == LDB_SUCCESS) {
4073 return callback(req, ares);
4077 * we have a conflict, and need to decide if we will keep the
4078 * new record or the old record
4081 msg = ar->objs->objects[ar->index_current].msg;
4082 conflict_dn = msg->dn;
4084 /* For failures other than conflicts, fail the whole operation here */
4085 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4086 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4087 ldb_dn_get_linearized(conflict_dn),
4088 ldb_errstring(ldb_module_get_ctx(ar->module)));
4090 return ldb_module_done(ar->req, NULL, NULL,
4091 LDB_ERR_OPERATIONS_ERROR);
4094 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4095 if (ret != LDB_SUCCESS) {
4096 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)));
4097 return ldb_module_done(ar->req, NULL, NULL,
4098 LDB_ERR_OPERATIONS_ERROR);
4104 * We are on an RODC, or were a GC for this
4105 * partition, so we have to fail this until
4106 * someone who owns the partition sorts it
4109 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4110 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4111 " - We must fail the operation until a master for this partition resolves the conflict",
4112 ldb_dn_get_linearized(conflict_dn));
4117 * first we need the replPropertyMetaData attribute from the
4118 * local, conflicting record
4120 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4122 DSDB_FLAG_NEXT_MODULE |
4123 DSDB_SEARCH_SHOW_DELETED |
4124 DSDB_SEARCH_SHOW_RECYCLED, req);
4125 if (ret != LDB_SUCCESS) {
4126 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4127 ldb_dn_get_linearized(conflict_dn)));
4131 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4132 if (omd_value == NULL) {
4133 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4134 ldb_dn_get_linearized(conflict_dn)));
4138 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4139 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4140 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4141 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4142 ldb_dn_get_linearized(conflict_dn)));
4146 rmd = ar->objs->objects[ar->index_current].meta_data;
4149 * we decide which is newer based on the RPMD on the name
4150 * attribute. See [MS-DRSR] ResolveNameConflict.
4152 * We expect omd_name to be present, as this is from a local
4153 * search, but while rmd_name should have been given to us by
4154 * the remote server, if it is missing we just prefer the
4156 * replmd_replPropertyMetaData1_new_should_be_taken()
4158 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4159 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4161 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4162 ldb_dn_get_linearized(conflict_dn)));
4167 * Should we preserve the current record, and so rename the
4168 * incoming record to be a conflict?
4170 rename_incoming_record
4171 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4172 omd_name, rmd_name);
4174 if (rename_incoming_record) {
4176 struct ldb_dn *new_dn;
4178 guid = samdb_result_guid(msg, "objectGUID");
4179 if (GUID_all_zero(&guid)) {
4180 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4181 ldb_dn_get_linearized(conflict_dn)));
4184 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4185 if (new_dn == NULL) {
4186 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4187 ldb_dn_get_linearized(conflict_dn)));
4191 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4192 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4194 /* re-submit the request, but with the new DN */
4195 callback = replmd_op_name_modify_callback;
4198 /* we are renaming the existing record */
4200 struct ldb_dn *new_dn;
4202 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4203 if (GUID_all_zero(&guid)) {
4204 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4205 ldb_dn_get_linearized(conflict_dn)));
4209 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4210 if (new_dn == NULL) {
4211 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4212 ldb_dn_get_linearized(conflict_dn)));
4216 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4217 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4219 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4220 DSDB_FLAG_OWN_MODULE, req);
4221 if (ret != LDB_SUCCESS) {
4222 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4223 ldb_dn_get_linearized(conflict_dn),
4224 ldb_dn_get_linearized(new_dn),
4225 ldb_errstring(ldb_module_get_ctx(ar->module))));
4230 * now we need to ensure that the rename is seen as an
4231 * originating update. We do that with a modify.
4233 ret = replmd_name_modify(ar, req, new_dn);
4234 if (ret != LDB_SUCCESS) {
4238 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4239 ldb_dn_get_linearized(req->op.add.message->dn)));
4242 ret = ldb_build_add_req(&down_req,
4243 ldb_module_get_ctx(ar->module),
4250 if (ret != LDB_SUCCESS) {
4253 LDB_REQ_SET_LOCATION(down_req);
4255 /* current partition control needed by "repmd_op_callback" */
4256 ret = ldb_request_add_control(down_req,
4257 DSDB_CONTROL_CURRENT_PARTITION_OID,
4259 if (ret != LDB_SUCCESS) {
4260 return replmd_replicated_request_error(ar, ret);
4263 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4264 /* this tells the partition module to make it a
4265 partial replica if creating an NC */
4266 ret = ldb_request_add_control(down_req,
4267 DSDB_CONTROL_PARTIAL_REPLICA,
4269 if (ret != LDB_SUCCESS) {
4270 return replmd_replicated_request_error(ar, ret);
4275 * Finally we re-run the add, otherwise the new record won't
4276 * exist, as we are here because of that exact failure!
4278 return ldb_next_request(ar->module, down_req);
4281 /* on failure make the caller get the error. This means
4282 * replication will stop with an error, but there is not much
4285 return ldb_module_done(ar->req, NULL, NULL,
4290 callback for replmd_replicated_apply_add()
4291 This copes with the creation of conflict records in the case where
4292 the DN exists, but with a different objectGUID
4294 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4296 struct replmd_replicated_request *ar =
4297 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4299 if (ar->objs->objects[ar->index_current].last_known_parent) {
4300 /* This is like a conflict DN, where we put the object in LostAndFound
4301 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4302 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4305 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4309 this is called when a new object comes in over DRS
4311 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4313 struct ldb_context *ldb;
4314 struct ldb_request *change_req;
4315 enum ndr_err_code ndr_err;
4316 struct ldb_message *msg;
4317 struct replPropertyMetaDataBlob *md;
4318 struct ldb_val md_value;
4321 bool remote_isDeleted = false;
4324 time_t t = time(NULL);
4325 const struct ldb_val *rdn_val;
4326 struct replmd_private *replmd_private =
4327 talloc_get_type(ldb_module_get_private(ar->module),
4328 struct replmd_private);
4329 unix_to_nt_time(&now, t);
4331 ldb = ldb_module_get_ctx(ar->module);
4332 msg = ar->objs->objects[ar->index_current].msg;
4333 md = ar->objs->objects[ar->index_current].meta_data;
4334 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4336 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4337 if (ret != LDB_SUCCESS) {
4338 return replmd_replicated_request_error(ar, ret);
4341 ret = dsdb_msg_add_guid(msg,
4342 &ar->objs->objects[ar->index_current].object_guid,
4344 if (ret != LDB_SUCCESS) {
4345 return replmd_replicated_request_error(ar, ret);
4348 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4349 if (ret != LDB_SUCCESS) {
4350 return replmd_replicated_request_error(ar, ret);
4353 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4354 if (ret != LDB_SUCCESS) {
4355 return replmd_replicated_request_error(ar, ret);
4358 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4359 if (ret != LDB_SUCCESS) {
4360 return replmd_replicated_request_error(ar, ret);
4363 /* remove any message elements that have zero values */
4364 for (i=0; i<msg->num_elements; i++) {
4365 struct ldb_message_element *el = &msg->elements[i];
4367 if (el->num_values == 0) {
4368 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4369 ldb_asprintf_errstring(ldb, __location__
4370 ": empty objectClass sent on %s, aborting replication\n",
4371 ldb_dn_get_linearized(msg->dn));
4372 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4375 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4377 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4378 msg->num_elements--;
4385 struct GUID_txt_buf guid_txt;
4387 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4388 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4389 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4394 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4395 "isDeleted", false);
4398 * the meta data array is already sorted by the caller, except
4399 * for the RDN, which needs to be added.
4403 rdn_val = ldb_dn_get_rdn_val(msg->dn);
4404 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
4405 md, ar, now, is_schema_nc);
4406 if (ret != LDB_SUCCESS) {
4407 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4408 return replmd_replicated_request_error(ar, ret);
4411 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
4412 if (ret != LDB_SUCCESS) {
4413 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4414 return replmd_replicated_request_error(ar, ret);
4417 for (i=0; i < md->ctr.ctr1.count; i++) {
4418 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4420 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4421 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4422 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4423 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4424 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4426 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4427 if (ret != LDB_SUCCESS) {
4428 return replmd_replicated_request_error(ar, ret);
4431 replmd_ldb_message_sort(msg, ar->schema);
4433 if (!remote_isDeleted) {
4434 ret = dsdb_module_schedule_sd_propagation(ar->module,
4435 ar->objs->partition_dn,
4437 if (ret != LDB_SUCCESS) {
4438 return replmd_replicated_request_error(ar, ret);
4442 ar->isDeleted = remote_isDeleted;
4444 ret = ldb_build_add_req(&change_req,
4450 replmd_op_add_callback,
4452 LDB_REQ_SET_LOCATION(change_req);
4453 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4455 /* current partition control needed by "repmd_op_callback" */
4456 ret = ldb_request_add_control(change_req,
4457 DSDB_CONTROL_CURRENT_PARTITION_OID,
4459 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4461 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4462 /* this tells the partition module to make it a
4463 partial replica if creating an NC */
4464 ret = ldb_request_add_control(change_req,
4465 DSDB_CONTROL_PARTIAL_REPLICA,
4467 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4470 return ldb_next_request(ar->module, change_req);
4473 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4474 struct ldb_reply *ares)
4476 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4477 struct replmd_replicated_request);
4481 return ldb_module_done(ar->req, NULL, NULL,
4482 LDB_ERR_OPERATIONS_ERROR);
4486 * The error NO_SUCH_OBJECT is not expected, unless the search
4487 * base is the partition DN, and that case doesn't happen here
4488 * because then we wouldn't get a parent_guid_value in any
4491 if (ares->error != LDB_SUCCESS) {
4492 return ldb_module_done(ar->req, ares->controls,
4493 ares->response, ares->error);
4496 switch (ares->type) {
4497 case LDB_REPLY_ENTRY:
4499 struct ldb_message *parent_msg = ares->message;
4500 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4501 struct ldb_dn *parent_dn;
4504 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4505 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4506 /* Per MS-DRSR 4.1.10.6.10
4507 * FindBestParentObject we need to move this
4508 * new object under a deleted object to
4510 struct ldb_dn *nc_root;
4512 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4513 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4514 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4515 "No suitable NC root found for %s. "
4516 "We need to move this object because parent object %s "
4517 "is deleted, but this object is not.",
4518 ldb_dn_get_linearized(msg->dn),
4519 ldb_dn_get_linearized(parent_msg->dn));
4520 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4521 } else if (ret != LDB_SUCCESS) {
4522 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4523 "Unable to find NC root for %s: %s. "
4524 "We need to move this object because parent object %s "
4525 "is deleted, but this object is not.",
4526 ldb_dn_get_linearized(msg->dn),
4527 ldb_errstring(ldb_module_get_ctx(ar->module)),
4528 ldb_dn_get_linearized(parent_msg->dn));
4529 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4532 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4534 DS_GUID_LOSTANDFOUND_CONTAINER,
4536 if (ret != LDB_SUCCESS) {
4537 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4538 "Unable to find LostAndFound Container for %s "
4539 "in partition %s: %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), ldb_dn_get_linearized(nc_root),
4543 ldb_errstring(ldb_module_get_ctx(ar->module)),
4544 ldb_dn_get_linearized(parent_msg->dn));
4545 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4547 ar->objs->objects[ar->index_current].last_known_parent
4548 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4552 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4555 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4557 comp_num = ldb_dn_get_comp_num(msg->dn);
4559 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4561 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4564 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4566 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4570 case LDB_REPLY_REFERRAL:
4571 /* we ignore referrals */
4574 case LDB_REPLY_DONE:
4576 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4577 struct GUID_txt_buf str_buf;
4578 if (ar->search_msg != NULL) {
4579 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4580 "No parent with GUID %s found for object locally known as %s",
4581 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4582 ldb_dn_get_linearized(ar->search_msg->dn));
4584 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4585 "No parent with GUID %s found for object remotely known as %s",
4586 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4587 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4591 * This error code is really important, as it
4592 * is the flag back to the callers to retry
4593 * this with DRSUAPI_DRS_GET_ANC, and so get
4594 * the parent objects before the child
4597 return ldb_module_done(ar->req, NULL, NULL,
4598 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4601 if (ar->search_msg != NULL) {
4602 ret = replmd_replicated_apply_merge(ar);
4604 ret = replmd_replicated_apply_add(ar);
4606 if (ret != LDB_SUCCESS) {
4607 return ldb_module_done(ar->req, NULL, NULL, ret);
4616 * Look for the parent object, so we put the new object in the right
4617 * place This is akin to NameObject in MS-DRSR - this routine and the
4618 * callbacks find the right parent name, and correct name for this
4622 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4624 struct ldb_context *ldb;
4628 struct ldb_request *search_req;
4629 static const char *attrs[] = {"isDeleted", NULL};
4630 struct GUID_txt_buf guid_str_buf;
4632 ldb = ldb_module_get_ctx(ar->module);
4634 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4635 if (ar->search_msg != NULL) {
4636 return replmd_replicated_apply_merge(ar);
4638 return replmd_replicated_apply_add(ar);
4642 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4645 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4646 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4648 ret = ldb_build_search_req(&search_req,
4651 ar->objs->partition_dn,
4657 replmd_replicated_apply_search_for_parent_callback,
4659 LDB_REQ_SET_LOCATION(search_req);
4661 ret = dsdb_request_add_controls(search_req,
4662 DSDB_SEARCH_SHOW_RECYCLED|
4663 DSDB_SEARCH_SHOW_DELETED|
4664 DSDB_SEARCH_SHOW_EXTENDED_DN);
4665 if (ret != LDB_SUCCESS) {
4669 return ldb_next_request(ar->module, search_req);
4673 handle renames that come in over DRS replication
4675 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4676 struct ldb_message *msg,
4677 struct ldb_request *parent,
4681 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4682 struct ldb_result *res;
4683 struct ldb_dn *conflict_dn;
4684 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4685 const struct ldb_val *omd_value;
4686 struct replPropertyMetaDataBlob omd, *rmd;
4687 enum ndr_err_code ndr_err;
4688 bool rename_incoming_record, rodc;
4689 struct replPropertyMetaData1 *rmd_name, *omd_name;
4690 struct ldb_dn *new_dn;
4693 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4694 ldb_dn_get_linearized(ar->search_msg->dn),
4695 ldb_dn_get_linearized(msg->dn)));
4698 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4699 DSDB_FLAG_NEXT_MODULE, ar->req);
4700 if (ret == LDB_SUCCESS) {
4701 talloc_free(tmp_ctx);
4706 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4707 talloc_free(tmp_ctx);
4708 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4709 ldb_dn_get_linearized(ar->search_msg->dn),
4710 ldb_dn_get_linearized(msg->dn),
4711 ldb_errstring(ldb_module_get_ctx(ar->module)));
4715 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4716 if (ret != LDB_SUCCESS) {
4717 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4718 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4719 ldb_errstring(ldb_module_get_ctx(ar->module)));
4720 return LDB_ERR_OPERATIONS_ERROR;
4723 * we have a conflict, and need to decide if we will keep the
4724 * new record or the old record
4727 conflict_dn = msg->dn;
4731 * We are on an RODC, or were a GC for this
4732 * partition, so we have to fail this until
4733 * someone who owns the partition sorts it
4736 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4737 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4738 " - We must fail the operation until a master for this partition resolves the conflict",
4739 ldb_dn_get_linearized(conflict_dn));
4744 * first we need the replPropertyMetaData attribute from the
4747 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4749 DSDB_FLAG_NEXT_MODULE |
4750 DSDB_SEARCH_SHOW_DELETED |
4751 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4752 if (ret != LDB_SUCCESS) {
4753 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4754 ldb_dn_get_linearized(conflict_dn)));
4758 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4759 if (omd_value == NULL) {
4760 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4761 ldb_dn_get_linearized(conflict_dn)));
4765 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4766 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4767 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4768 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4769 ldb_dn_get_linearized(conflict_dn)));
4773 rmd = ar->objs->objects[ar->index_current].meta_data;
4776 * we decide which is newer based on the RPMD on the name
4777 * attribute. See [MS-DRSR] ResolveNameConflict.
4779 * We expect omd_name to be present, as this is from a local
4780 * search, but while rmd_name should have been given to us by
4781 * the remote server, if it is missing we just prefer the
4783 * replmd_replPropertyMetaData1_new_should_be_taken()
4785 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4786 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4788 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4789 ldb_dn_get_linearized(conflict_dn)));
4794 * Should we preserve the current record, and so rename the
4795 * incoming record to be a conflict?
4797 rename_incoming_record =
4798 !replmd_replPropertyMetaData1_new_should_be_taken(
4799 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4800 omd_name, rmd_name);
4802 if (rename_incoming_record) {
4804 new_dn = replmd_conflict_dn(msg, msg->dn,
4805 &ar->objs->objects[ar->index_current].object_guid);
4806 if (new_dn == NULL) {
4807 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4808 "Failed to form conflict DN for %s\n",
4809 ldb_dn_get_linearized(msg->dn));
4811 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4814 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4815 DSDB_FLAG_NEXT_MODULE, ar->req);
4816 if (ret != LDB_SUCCESS) {
4817 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4818 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4819 ldb_dn_get_linearized(conflict_dn),
4820 ldb_dn_get_linearized(ar->search_msg->dn),
4821 ldb_dn_get_linearized(new_dn),
4822 ldb_errstring(ldb_module_get_ctx(ar->module)));
4823 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4831 /* we are renaming the existing record */
4833 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4834 if (GUID_all_zero(&guid)) {
4835 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4836 ldb_dn_get_linearized(conflict_dn)));
4840 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4841 if (new_dn == NULL) {
4842 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4843 ldb_dn_get_linearized(conflict_dn)));
4847 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4848 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4850 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4851 DSDB_FLAG_OWN_MODULE, ar->req);
4852 if (ret != LDB_SUCCESS) {
4853 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4854 ldb_dn_get_linearized(conflict_dn),
4855 ldb_dn_get_linearized(new_dn),
4856 ldb_errstring(ldb_module_get_ctx(ar->module))));
4861 * now we need to ensure that the rename is seen as an
4862 * originating update. We do that with a modify.
4864 ret = replmd_name_modify(ar, ar->req, new_dn);
4865 if (ret != LDB_SUCCESS) {
4869 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4870 ldb_dn_get_linearized(ar->search_msg->dn),
4871 ldb_dn_get_linearized(msg->dn)));
4874 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4875 DSDB_FLAG_NEXT_MODULE, ar->req);
4876 if (ret != LDB_SUCCESS) {
4877 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4878 ldb_dn_get_linearized(ar->search_msg->dn),
4879 ldb_dn_get_linearized(msg->dn),
4880 ldb_errstring(ldb_module_get_ctx(ar->module))));
4886 * On failure make the caller get the error
4887 * This means replication will stop with an error,
4888 * but there is not much else we can do. In the
4889 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4893 talloc_free(tmp_ctx);
4898 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4900 struct ldb_context *ldb;
4901 struct ldb_request *change_req;
4902 enum ndr_err_code ndr_err;
4903 struct ldb_message *msg;
4904 struct replPropertyMetaDataBlob *rmd;
4905 struct replPropertyMetaDataBlob omd;
4906 const struct ldb_val *omd_value;
4907 struct replPropertyMetaDataBlob nmd;
4908 struct ldb_val nmd_value;
4909 struct GUID remote_parent_guid;
4912 unsigned int removed_attrs = 0;
4914 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4915 bool isDeleted = false;
4916 bool local_isDeleted = false;
4917 bool remote_isDeleted = false;
4918 bool take_remote_isDeleted = false;
4919 bool sd_updated = false;
4920 bool renamed = false;
4921 bool is_schema_nc = false;
4923 const struct ldb_val *old_rdn, *new_rdn;
4924 struct replmd_private *replmd_private =
4925 talloc_get_type(ldb_module_get_private(ar->module),
4926 struct replmd_private);
4928 time_t t = time(NULL);
4929 unix_to_nt_time(&now, t);
4931 ldb = ldb_module_get_ctx(ar->module);
4932 msg = ar->objs->objects[ar->index_current].msg;
4934 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4936 rmd = ar->objs->objects[ar->index_current].meta_data;
4940 /* find existing meta data */
4941 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4943 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4944 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4945 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4946 nt_status = ndr_map_error2ntstatus(ndr_err);
4947 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4950 if (omd.version != 1) {
4951 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4956 struct GUID_txt_buf guid_txt;
4958 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4959 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
4962 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4964 ndr_print_struct_string(s,
4965 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4966 "existing replPropertyMetaData",
4968 ndr_print_struct_string(s,
4969 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4970 "incoming replPropertyMetaData",
4975 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4976 "isDeleted", false);
4977 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4978 "isDeleted", false);
4981 * Fill in the remote_parent_guid with the GUID or an all-zero
4984 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4985 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4987 remote_parent_guid = GUID_zero();
4991 * To ensure we follow a complex rename chain around, we have
4992 * to confirm that the DN is the same (mostly to confirm the
4993 * RDN) and the parentGUID is the same.
4995 * This ensures we keep things under the correct parent, which
4996 * replmd_replicated_handle_rename() will do.
4999 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
5000 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5004 * handle renames, even just by case that come in over
5005 * DRS. Changes in the parent DN don't hit us here,
5006 * because the search for a parent will clean up those
5009 * We also have already filtered out the case where
5010 * the peer has an older name to what we have (see
5011 * replmd_replicated_apply_search_callback())
5013 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5016 if (ret != LDB_SUCCESS) {
5017 ldb_debug(ldb, LDB_DEBUG_FATAL,
5018 "replmd_replicated_request rename %s => %s failed - %s\n",
5019 ldb_dn_get_linearized(ar->search_msg->dn),
5020 ldb_dn_get_linearized(msg->dn),
5021 ldb_errstring(ldb));
5022 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5025 if (renamed == true) {
5027 * Set the callback to one that will fix up the name
5028 * metadata on the new conflict DN
5030 callback = replmd_op_name_modify_callback;
5035 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5036 nmd.ctr.ctr1.array = talloc_array(ar,
5037 struct replPropertyMetaData1,
5038 nmd.ctr.ctr1.count);
5039 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5041 /* first copy the old meta data */
5042 for (i=0; i < omd.ctr.ctr1.count; i++) {
5043 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5048 /* now merge in the new meta data */
5049 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5052 for (j=0; j < ni; j++) {
5055 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5059 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5060 ar->objs->dsdb_repl_flags,
5061 &nmd.ctr.ctr1.array[j],
5062 &rmd->ctr.ctr1.array[i]);
5064 /* replace the entry */
5065 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5066 if (ar->seq_num == 0) {
5067 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5068 if (ret != LDB_SUCCESS) {
5069 return replmd_replicated_request_error(ar, ret);
5072 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5073 switch (nmd.ctr.ctr1.array[j].attid) {
5074 case DRSUAPI_ATTID_ntSecurityDescriptor:
5077 case DRSUAPI_ATTID_isDeleted:
5078 take_remote_isDeleted = true;
5087 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5088 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5089 msg->elements[i-removed_attrs].name,
5090 ldb_dn_get_linearized(msg->dn),
5091 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5094 /* we don't want to apply this change so remove the attribute */
5095 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5102 if (found) continue;
5104 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5105 if (ar->seq_num == 0) {
5106 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5107 if (ret != LDB_SUCCESS) {
5108 return replmd_replicated_request_error(ar, ret);
5111 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5112 switch (nmd.ctr.ctr1.array[ni].attid) {
5113 case DRSUAPI_ATTID_ntSecurityDescriptor:
5116 case DRSUAPI_ATTID_isDeleted:
5117 take_remote_isDeleted = true;
5126 * finally correct the size of the meta_data array
5128 nmd.ctr.ctr1.count = ni;
5130 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5131 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5134 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5135 &nmd, ar, now, is_schema_nc);
5136 if (ret != LDB_SUCCESS) {
5137 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5138 return replmd_replicated_request_error(ar, ret);
5142 * sort the new meta data array
5144 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5145 if (ret != LDB_SUCCESS) {
5146 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5151 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5154 * This also controls SD propagation below
5156 if (take_remote_isDeleted) {
5157 isDeleted = remote_isDeleted;
5159 isDeleted = local_isDeleted;
5162 ar->isDeleted = isDeleted;
5165 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5167 if (msg->num_elements == 0) {
5168 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5171 return replmd_replicated_apply_isDeleted(ar);
5174 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5175 ar->index_current, msg->num_elements);
5181 if (sd_updated && !isDeleted) {
5182 ret = dsdb_module_schedule_sd_propagation(ar->module,
5183 ar->objs->partition_dn,
5185 if (ret != LDB_SUCCESS) {
5186 return ldb_operr(ldb);
5190 /* create the meta data value */
5191 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5192 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5193 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5194 nt_status = ndr_map_error2ntstatus(ndr_err);
5195 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5199 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5200 * and replPopertyMetaData attributes
5202 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5203 if (ret != LDB_SUCCESS) {
5204 return replmd_replicated_request_error(ar, ret);
5206 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5207 if (ret != LDB_SUCCESS) {
5208 return replmd_replicated_request_error(ar, ret);
5210 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5211 if (ret != LDB_SUCCESS) {
5212 return replmd_replicated_request_error(ar, ret);
5215 replmd_ldb_message_sort(msg, ar->schema);
5217 /* we want to replace the old values */
5218 for (i=0; i < msg->num_elements; i++) {
5219 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5220 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5221 if (msg->elements[i].num_values == 0) {
5222 ldb_asprintf_errstring(ldb, __location__
5223 ": objectClass removed on %s, aborting replication\n",
5224 ldb_dn_get_linearized(msg->dn));
5225 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5231 struct GUID_txt_buf guid_txt;
5233 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5234 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5235 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5240 ret = ldb_build_mod_req(&change_req,
5248 LDB_REQ_SET_LOCATION(change_req);
5249 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5251 /* current partition control needed by "repmd_op_callback" */
5252 ret = ldb_request_add_control(change_req,
5253 DSDB_CONTROL_CURRENT_PARTITION_OID,
5255 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5257 return ldb_next_request(ar->module, change_req);
5260 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5261 struct ldb_reply *ares)
5263 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5264 struct replmd_replicated_request);
5268 return ldb_module_done(ar->req, NULL, NULL,
5269 LDB_ERR_OPERATIONS_ERROR);
5271 if (ares->error != LDB_SUCCESS &&
5272 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5273 return ldb_module_done(ar->req, ares->controls,
5274 ares->response, ares->error);
5277 switch (ares->type) {
5278 case LDB_REPLY_ENTRY:
5279 ar->search_msg = talloc_steal(ar, ares->message);
5282 case LDB_REPLY_REFERRAL:
5283 /* we ignore referrals */
5286 case LDB_REPLY_DONE:
5288 struct replPropertyMetaData1 *md_remote;
5289 struct replPropertyMetaData1 *md_local;
5291 struct replPropertyMetaDataBlob omd;
5292 const struct ldb_val *omd_value;
5293 struct replPropertyMetaDataBlob *rmd;
5294 struct ldb_message *msg;
5296 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5297 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5300 * This is the ADD case, find the appropriate parent,
5301 * as this object doesn't exist locally:
5303 if (ar->search_msg == NULL) {
5304 ret = replmd_replicated_apply_search_for_parent(ar);
5305 if (ret != LDB_SUCCESS) {
5306 return ldb_module_done(ar->req, NULL, NULL, ret);
5313 * Otherwise, in the MERGE case, work out if we are
5314 * attempting a rename, and if so find the parent the
5315 * newly renamed object wants to belong under (which
5316 * may not be the parent in it's attached string DN
5318 rmd = ar->objs->objects[ar->index_current].meta_data;
5322 /* find existing meta data */
5323 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5325 enum ndr_err_code ndr_err;
5326 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5327 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5328 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5329 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5330 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5333 if (omd.version != 1) {
5334 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5338 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5340 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5341 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5342 && GUID_all_zero(&ar->local_parent_guid)) {
5343 DEBUG(0, ("Refusing to replicate new version of %s "
5344 "as local object has an all-zero parentGUID attribute, "
5345 "despite not being an NC root\n",
5346 ldb_dn_get_linearized(ar->search_msg->dn)));
5347 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5351 * now we need to check for double renames. We could have a
5352 * local rename pending which our replication partner hasn't
5353 * received yet. We choose which one wins by looking at the
5354 * attribute stamps on the two objects, the newer one wins.
5356 * This also simply applies the correct algorithms for
5357 * determining if a change was made to name at all, or
5358 * if the object has just been renamed under the same
5361 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5362 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5364 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5365 ldb_dn_get_linearized(ar->search_msg->dn)));
5366 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5370 * if there is no name attribute given then we have to assume the
5371 * object we've received has the older name
5373 if (replmd_replPropertyMetaData1_new_should_be_taken(
5374 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5375 md_local, md_remote)) {
5376 struct GUID_txt_buf p_guid_local;
5377 struct GUID_txt_buf p_guid_remote;
5378 msg = ar->objs->objects[ar->index_current].msg;
5380 /* Merge on the existing object, with rename */
5382 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5383 "as incoming object changing to %s under %s\n",
5384 ldb_dn_get_linearized(ar->search_msg->dn),
5385 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5386 ldb_dn_get_linearized(msg->dn),
5387 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5389 ret = replmd_replicated_apply_search_for_parent(ar);
5391 struct GUID_txt_buf p_guid_local;
5392 struct GUID_txt_buf p_guid_remote;
5393 msg = ar->objs->objects[ar->index_current].msg;
5396 * Merge on the existing object, force no
5397 * rename (code below just to explain why in
5401 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5402 ldb_dn_get_linearized(msg->dn)) == 0) {
5403 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5404 GUID_equal(&ar->local_parent_guid,
5405 ar->objs->objects[ar->index_current].parent_guid)
5407 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5408 "despite incoming object changing parent to %s\n",
5409 ldb_dn_get_linearized(ar->search_msg->dn),
5410 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5411 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5415 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5416 " and rejecting older rename to %s under %s\n",
5417 ldb_dn_get_linearized(ar->search_msg->dn),
5418 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5419 ldb_dn_get_linearized(msg->dn),
5420 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5424 * This assignment ensures that the strcmp()
5425 * and GUID_equal() calls in
5426 * replmd_replicated_apply_merge() avoids the
5429 ar->objs->objects[ar->index_current].parent_guid =
5430 &ar->local_parent_guid;
5432 msg->dn = ar->search_msg->dn;
5433 ret = replmd_replicated_apply_merge(ar);
5435 if (ret != LDB_SUCCESS) {
5436 return ldb_module_done(ar->req, NULL, NULL, ret);
5445 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5447 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5449 struct ldb_context *ldb;
5453 struct ldb_request *search_req;
5454 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
5455 "parentGUID", "instanceType",
5456 "replPropertyMetaData", "nTSecurityDescriptor",
5457 "isDeleted", NULL };
5458 struct GUID_txt_buf guid_str_buf;
5460 if (ar->index_current >= ar->objs->num_objects) {
5461 /* done with it, go to next stage */
5462 return replmd_replicated_uptodate_vector(ar);
5465 ldb = ldb_module_get_ctx(ar->module);
5466 ar->search_msg = NULL;
5467 ar->isDeleted = false;
5469 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5472 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5473 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5475 ret = ldb_build_search_req(&search_req,
5478 ar->objs->partition_dn,
5484 replmd_replicated_apply_search_callback,
5486 LDB_REQ_SET_LOCATION(search_req);
5488 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5490 if (ret != LDB_SUCCESS) {
5494 return ldb_next_request(ar->module, search_req);
5498 * This is essentially a wrapper for replmd_replicated_apply_next()
5500 * This is needed to ensure that both codepaths call this handler.
5502 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5504 struct ldb_dn *deleted_objects_dn;
5505 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5506 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5507 &deleted_objects_dn);
5508 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5510 * Do a delete here again, so that if there is
5511 * anything local that conflicts with this
5512 * object being deleted, it is removed. This
5513 * includes links. See MS-DRSR 4.1.10.6.9
5516 * If the object is already deleted, and there
5517 * is no more work required, it doesn't do
5521 /* This has been updated to point to the DN we eventually did the modify on */
5523 struct ldb_request *del_req;
5524 struct ldb_result *res;
5526 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5528 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5532 res = talloc_zero(tmp_ctx, struct ldb_result);
5534 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5535 talloc_free(tmp_ctx);
5539 /* Build a delete request, which hopefully will artually turn into nothing */
5540 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5544 ldb_modify_default_callback,
5546 LDB_REQ_SET_LOCATION(del_req);
5547 if (ret != LDB_SUCCESS) {
5548 talloc_free(tmp_ctx);
5553 * This is the guts of the call, call back
5554 * into our delete code, but setting the
5555 * re_delete flag so we delete anything that
5556 * shouldn't be there on a deleted or recycled
5559 ret = replmd_delete_internals(ar->module, del_req, true);
5560 if (ret == LDB_SUCCESS) {
5561 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5564 talloc_free(tmp_ctx);
5565 if (ret != LDB_SUCCESS) {
5570 ar->index_current++;
5571 return replmd_replicated_apply_next(ar);
5574 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5575 struct ldb_reply *ares)
5577 struct ldb_context *ldb;
5578 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5579 struct replmd_replicated_request);
5580 ldb = ldb_module_get_ctx(ar->module);
5583 return ldb_module_done(ar->req, NULL, NULL,
5584 LDB_ERR_OPERATIONS_ERROR);
5586 if (ares->error != LDB_SUCCESS) {
5587 return ldb_module_done(ar->req, ares->controls,
5588 ares->response, ares->error);
5591 if (ares->type != LDB_REPLY_DONE) {
5592 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5593 return ldb_module_done(ar->req, NULL, NULL,
5594 LDB_ERR_OPERATIONS_ERROR);
5599 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5602 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5604 struct ldb_context *ldb;
5605 struct ldb_request *change_req;
5606 enum ndr_err_code ndr_err;
5607 struct ldb_message *msg;
5608 struct replUpToDateVectorBlob ouv;
5609 const struct ldb_val *ouv_value;
5610 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5611 struct replUpToDateVectorBlob nuv;
5612 struct ldb_val nuv_value;
5613 struct ldb_message_element *nuv_el = NULL;
5614 struct ldb_message_element *orf_el = NULL;
5615 struct repsFromToBlob nrf;
5616 struct ldb_val *nrf_value = NULL;
5617 struct ldb_message_element *nrf_el = NULL;
5621 time_t t = time(NULL);
5624 uint32_t instanceType;
5626 ldb = ldb_module_get_ctx(ar->module);
5627 ruv = ar->objs->uptodateness_vector;
5633 unix_to_nt_time(&now, t);
5635 if (ar->search_msg == NULL) {
5636 /* this happens for a REPL_OBJ call where we are
5637 creating the target object by replicating it. The
5638 subdomain join code does this for the partition DN
5640 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5641 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5644 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5645 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5646 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5647 ldb_dn_get_linearized(ar->search_msg->dn)));
5648 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5652 * first create the new replUpToDateVector
5654 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5656 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5657 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5658 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5659 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5660 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5663 if (ouv.version != 2) {
5664 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5669 * the new uptodateness vector will at least
5670 * contain 1 entry, one for the source_dsa
5672 * plus optional values from our old vector and the one from the source_dsa
5674 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5675 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5676 nuv.ctr.ctr2.cursors = talloc_array(ar,
5677 struct drsuapi_DsReplicaCursor2,
5678 nuv.ctr.ctr2.count);
5679 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5681 /* first copy the old vector */
5682 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5683 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5687 /* merge in the source_dsa vector is available */
5688 for (i=0; (ruv && i < ruv->count); i++) {
5691 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5692 &ar->our_invocation_id)) {
5696 for (j=0; j < ni; j++) {
5697 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5698 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5704 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5705 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5710 if (found) continue;
5712 /* if it's not there yet, add it */
5713 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5718 * finally correct the size of the cursors array
5720 nuv.ctr.ctr2.count = ni;
5725 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5728 * create the change ldb_message
5730 msg = ldb_msg_new(ar);
5731 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5732 msg->dn = ar->search_msg->dn;
5734 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5735 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5736 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5737 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5738 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5740 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5741 if (ret != LDB_SUCCESS) {
5742 return replmd_replicated_request_error(ar, ret);
5744 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5747 * now create the new repsFrom value from the given repsFromTo1 structure
5751 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5752 nrf.ctr.ctr1.last_attempt = now;
5753 nrf.ctr.ctr1.last_success = now;
5754 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5757 * first see if we already have a repsFrom value for the current source dsa
5758 * if so we'll later replace this value
5760 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5762 for (i=0; i < orf_el->num_values; i++) {
5763 struct repsFromToBlob *trf;
5765 trf = talloc(ar, struct repsFromToBlob);
5766 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5768 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5769 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5770 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5771 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5772 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5775 if (trf->version != 1) {
5776 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5780 * we compare the source dsa objectGUID not the invocation_id
5781 * because we want only one repsFrom value per source dsa
5782 * and when the invocation_id of the source dsa has changed we don't need
5783 * the old repsFrom with the old invocation_id
5785 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5786 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5792 nrf_value = &orf_el->values[i];
5797 * copy over all old values to the new ldb_message
5799 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5800 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5805 * if we haven't found an old repsFrom value for the current source dsa
5806 * we'll add a new value
5809 struct ldb_val zero_value;
5810 ZERO_STRUCT(zero_value);
5811 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5812 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5814 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5817 /* we now fill the value which is already attached to ldb_message */
5818 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5820 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5821 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5822 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5823 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5827 * the ldb_message_element for the attribute, has all the old values and the new one
5828 * so we'll replace the whole attribute with all values
5830 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5832 if (CHECK_DEBUGLVL(4)) {
5833 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5834 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5838 /* prepare the ldb_modify() request */
5839 ret = ldb_build_mod_req(&change_req,
5845 replmd_replicated_uptodate_modify_callback,
5847 LDB_REQ_SET_LOCATION(change_req);
5848 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5850 return ldb_next_request(ar->module, change_req);
5853 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5854 struct ldb_reply *ares)
5856 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5857 struct replmd_replicated_request);
5861 return ldb_module_done(ar->req, NULL, NULL,
5862 LDB_ERR_OPERATIONS_ERROR);
5864 if (ares->error != LDB_SUCCESS &&
5865 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5866 return ldb_module_done(ar->req, ares->controls,
5867 ares->response, ares->error);
5870 switch (ares->type) {
5871 case LDB_REPLY_ENTRY:
5872 ar->search_msg = talloc_steal(ar, ares->message);
5875 case LDB_REPLY_REFERRAL:
5876 /* we ignore referrals */
5879 case LDB_REPLY_DONE:
5880 ret = replmd_replicated_uptodate_modify(ar);
5881 if (ret != LDB_SUCCESS) {
5882 return ldb_module_done(ar->req, NULL, NULL, ret);
5891 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5893 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5894 struct replmd_private *replmd_private =
5895 talloc_get_type_abort(ldb_module_get_private(ar->module),
5896 struct replmd_private);
5898 static const char *attrs[] = {
5899 "replUpToDateVector",
5904 struct ldb_request *search_req;
5906 ar->search_msg = NULL;
5909 * Let the caller know that we did an originating updates
5911 ar->objs->originating_updates = replmd_private->originating_updates;
5913 ret = ldb_build_search_req(&search_req,
5916 ar->objs->partition_dn,
5922 replmd_replicated_uptodate_search_callback,
5924 LDB_REQ_SET_LOCATION(search_req);
5925 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5927 return ldb_next_request(ar->module, search_req);
5932 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5934 struct ldb_context *ldb;
5935 struct dsdb_extended_replicated_objects *objs;
5936 struct replmd_replicated_request *ar;
5937 struct ldb_control **ctrls;
5940 struct replmd_private *replmd_private =
5941 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5943 ldb = ldb_module_get_ctx(module);
5945 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5947 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5949 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5950 return LDB_ERR_PROTOCOL_ERROR;
5953 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5954 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5955 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5956 return LDB_ERR_PROTOCOL_ERROR;
5959 ar = replmd_ctx_init(module, req);
5961 return LDB_ERR_OPERATIONS_ERROR;
5963 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5964 ar->apply_mode = true;
5966 ar->schema = dsdb_get_schema(ldb, ar);
5968 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5970 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5971 return LDB_ERR_CONSTRAINT_VIOLATION;
5974 ctrls = req->controls;
5976 if (req->controls) {
5977 req->controls = talloc_memdup(ar, req->controls,
5978 talloc_get_size(req->controls));
5979 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5982 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
5983 if (ret != LDB_SUCCESS) {
5987 /* If this change contained linked attributes in the body
5988 * (rather than in the links section) we need to update
5989 * backlinks in linked_attributes */
5990 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5991 if (ret != LDB_SUCCESS) {
5995 ar->controls = req->controls;
5996 req->controls = ctrls;
5998 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
6000 /* save away the linked attributes for the end of the
6002 for (i=0; i<ar->objs->linked_attributes_count; i++) {
6003 struct la_entry *la_entry;
6005 if (replmd_private->la_ctx == NULL) {
6006 replmd_private->la_ctx = talloc_new(replmd_private);
6008 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6009 if (la_entry == NULL) {
6011 return LDB_ERR_OPERATIONS_ERROR;
6013 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6014 if (la_entry->la == NULL) {
6015 talloc_free(la_entry);
6017 return LDB_ERR_OPERATIONS_ERROR;
6019 *la_entry->la = ar->objs->linked_attributes[i];
6021 /* we need to steal the non-scalars so they stay
6022 around until the end of the transaction */
6023 talloc_steal(la_entry->la, la_entry->la->identifier);
6024 talloc_steal(la_entry->la, la_entry->la->value.blob);
6026 DLIST_ADD(replmd_private->la_list, la_entry);
6029 return replmd_replicated_apply_next(ar);
6033 process one linked attribute structure
6035 static int replmd_process_linked_attribute(struct ldb_module *module,
6036 struct la_entry *la_entry,
6037 struct ldb_request *parent)
6039 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6040 struct ldb_context *ldb = ldb_module_get_ctx(module);
6041 struct ldb_message *msg;
6042 struct ldb_message *target_msg = NULL;
6043 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6044 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6046 const struct dsdb_attribute *attr;
6047 struct dsdb_dn *dsdb_dn;
6048 uint64_t seq_num = 0;
6049 struct ldb_message_element *old_el;
6051 time_t t = time(NULL);
6052 struct ldb_result *res;
6053 struct ldb_result *target_res;
6054 const char *attrs[4];
6055 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6056 struct parsed_dn *pdn_list, *pdn;
6057 struct GUID guid = GUID_zero();
6059 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6060 const struct GUID *our_invocation_id;
6062 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6063 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6066 linked_attributes[0]:
6067 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6069 identifier: struct drsuapi_DsReplicaObjectIdentifier
6070 __ndr_size : 0x0000003a (58)
6071 __ndr_size_sid : 0x00000000 (0)
6072 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6074 __ndr_size_dn : 0x00000000 (0)
6076 attid : DRSUAPI_ATTID_member (0x1F)
6077 value: struct drsuapi_DsAttributeValue
6078 __ndr_size : 0x0000007e (126)
6080 blob : DATA_BLOB length=126
6081 flags : 0x00000001 (1)
6082 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6083 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6084 meta_data: struct drsuapi_DsReplicaMetaData
6085 version : 0x00000015 (21)
6086 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6087 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6088 originating_usn : 0x000000000001e19c (123292)
6090 (for cases where the link is to a normal DN)
6091 &target: struct drsuapi_DsReplicaObjectIdentifier3
6092 __ndr_size : 0x0000007e (126)
6093 __ndr_size_sid : 0x0000001c (28)
6094 guid : 7639e594-db75-4086-b0d4-67890ae46031
6095 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6096 __ndr_size_dn : 0x00000022 (34)
6097 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6100 /* find the attribute being modified */
6101 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6103 struct GUID_txt_buf guid_str;
6104 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6106 GUID_buf_string(&la->identifier->guid,
6108 talloc_free(tmp_ctx);
6109 return LDB_ERR_OPERATIONS_ERROR;
6112 attrs[0] = attr->lDAPDisplayName;
6113 attrs[1] = "isDeleted";
6114 attrs[2] = "isRecycled";
6117 /* get the existing message from the db for the object with
6118 this GUID, returning attribute being modified. We will then
6119 use this msg as the basis for a modify call */
6120 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6121 DSDB_FLAG_NEXT_MODULE |
6122 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6123 DSDB_SEARCH_SHOW_RECYCLED |
6124 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6125 DSDB_SEARCH_REVEAL_INTERNALS,
6127 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6128 if (ret != LDB_SUCCESS) {
6129 talloc_free(tmp_ctx);
6132 if (res->count != 1) {
6133 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6134 GUID_string(tmp_ctx, &la->identifier->guid));
6135 talloc_free(tmp_ctx);
6136 return LDB_ERR_NO_SUCH_OBJECT;
6141 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6142 * ProcessLinkValue, because link updates are not applied to
6143 * recycled and tombstone objects. We don't have to delete
6144 * any existing link, that should have happened when the
6145 * object deletion was replicated or initiated.
6148 replmd_deletion_state(module, msg, &deletion_state, NULL);
6150 if (deletion_state >= OBJECT_RECYCLED) {
6151 talloc_free(tmp_ctx);
6155 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6156 if (old_el == NULL) {
6157 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6158 if (ret != LDB_SUCCESS) {
6159 ldb_module_oom(module);
6160 talloc_free(tmp_ctx);
6161 return LDB_ERR_OPERATIONS_ERROR;
6164 old_el->flags = LDB_FLAG_MOD_REPLACE;
6167 /* parse the existing links */
6168 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
6169 if (ret != LDB_SUCCESS) {
6170 talloc_free(tmp_ctx);
6174 /* get our invocationId */
6175 our_invocation_id = samdb_ntds_invocation_id(ldb);
6176 if (!our_invocation_id) {
6177 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
6178 talloc_free(tmp_ctx);
6179 return LDB_ERR_OPERATIONS_ERROR;
6182 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
6183 if (ret != LDB_SUCCESS) {
6184 talloc_free(tmp_ctx);
6188 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6189 if (!W_ERROR_IS_OK(status)) {
6190 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6191 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6192 talloc_free(tmp_ctx);
6193 return LDB_ERR_OPERATIONS_ERROR;
6196 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6197 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6199 * This strange behaviour (allowing a NULL/missing
6200 * GUID) originally comes from:
6202 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6203 * Author: Andrew Tridgell <tridge@samba.org>
6204 * Date: Mon Dec 21 21:21:55 2009 +1100
6206 * s4-drs: cope better with NULL GUIDS from DRS
6208 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6209 * need to match by DN if possible when seeing if we should update an
6212 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6215 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6216 dsdb_dn->dn, attrs2,
6217 DSDB_FLAG_NEXT_MODULE |
6218 DSDB_SEARCH_SHOW_RECYCLED |
6219 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6220 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6222 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6223 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6225 ldb_dn_get_linearized(dsdb_dn->dn),
6226 ldb_dn_get_linearized(msg->dn));
6227 talloc_free(tmp_ctx);
6228 return LDB_ERR_OPERATIONS_ERROR;
6230 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6231 NULL, LDB_SCOPE_SUBTREE,
6233 DSDB_FLAG_NEXT_MODULE |
6234 DSDB_SEARCH_SHOW_RECYCLED |
6235 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6236 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6239 GUID_string(tmp_ctx, &guid));
6242 if (ret != LDB_SUCCESS) {
6243 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6244 GUID_string(tmp_ctx, &guid),
6245 ldb_errstring(ldb_module_get_ctx(module)));
6246 talloc_free(tmp_ctx);
6250 if (target_res->count == 0) {
6251 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6252 GUID_string(tmp_ctx, &guid),
6253 ldb_dn_get_linearized(dsdb_dn->dn)));
6254 } else if (target_res->count != 1) {
6255 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6256 GUID_string(tmp_ctx, &guid));
6257 talloc_free(tmp_ctx);
6258 return LDB_ERR_OPERATIONS_ERROR;
6260 target_msg = target_res->msgs[0];
6261 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6265 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6266 * ProcessLinkValue, because link updates are not applied to
6267 * recycled and tombstone objects. We don't have to delete
6268 * any existing link, that should have happened when the
6269 * object deletion was replicated or initiated.
6271 replmd_deletion_state(module, target_msg,
6272 &target_deletion_state, NULL);
6274 if (target_deletion_state >= OBJECT_RECYCLED) {
6275 talloc_free(tmp_ctx);
6279 /* see if this link already exists */
6280 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
6282 /* see if this update is newer than what we have already */
6283 struct GUID invocation_id = GUID_zero();
6284 uint32_t version = 0;
6285 uint32_t originating_usn = 0;
6286 NTTIME change_time = 0;
6287 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6289 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6290 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6291 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6292 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6294 if (!replmd_update_is_newer(&invocation_id,
6295 &la->meta_data.originating_invocation_id,
6297 la->meta_data.version,
6299 la->meta_data.originating_change_time)) {
6300 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6301 old_el->name, ldb_dn_get_linearized(msg->dn),
6302 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6303 talloc_free(tmp_ctx);
6307 /* get a seq_num for this change */
6308 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6309 if (ret != LDB_SUCCESS) {
6310 talloc_free(tmp_ctx);
6314 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6315 /* remove the existing backlink */
6316 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, true);
6317 if (ret != LDB_SUCCESS) {
6318 talloc_free(tmp_ctx);
6323 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6324 &la->meta_data.originating_invocation_id,
6325 la->meta_data.originating_usn, seq_num,
6326 la->meta_data.originating_change_time,
6327 la->meta_data.version,
6329 if (ret != LDB_SUCCESS) {
6330 talloc_free(tmp_ctx);
6335 /* add the new backlink */
6336 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, true);
6337 if (ret != LDB_SUCCESS) {
6338 talloc_free(tmp_ctx);
6343 /* get a seq_num for this change */
6344 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6345 if (ret != LDB_SUCCESS) {
6346 talloc_free(tmp_ctx);
6350 old_el->values = talloc_realloc(msg->elements, old_el->values,
6351 struct ldb_val, old_el->num_values+1);
6352 if (!old_el->values) {
6353 ldb_module_oom(module);
6354 return LDB_ERR_OPERATIONS_ERROR;
6356 old_el->num_values++;
6358 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
6359 &la->meta_data.originating_invocation_id,
6360 la->meta_data.originating_usn, seq_num,
6361 la->meta_data.originating_change_time,
6362 la->meta_data.version,
6364 if (ret != LDB_SUCCESS) {
6365 talloc_free(tmp_ctx);
6370 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6372 if (ret != LDB_SUCCESS) {
6373 talloc_free(tmp_ctx);
6379 /* we only change whenChanged and uSNChanged if the seq_num
6381 ret = add_time_element(msg, "whenChanged", t);
6382 if (ret != LDB_SUCCESS) {
6383 talloc_free(tmp_ctx);
6388 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6389 if (ret != LDB_SUCCESS) {
6390 talloc_free(tmp_ctx);
6395 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6396 if (old_el == NULL) {
6397 talloc_free(tmp_ctx);
6398 return ldb_operr(ldb);
6401 ret = dsdb_check_single_valued_link(attr, old_el);
6402 if (ret != LDB_SUCCESS) {
6403 talloc_free(tmp_ctx);
6407 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6409 ret = linked_attr_modify(module, msg, parent);
6410 if (ret != LDB_SUCCESS) {
6411 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6413 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6414 talloc_free(tmp_ctx);
6418 talloc_free(tmp_ctx);
6423 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6425 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6426 return replmd_extended_replicated_objects(module, req);
6429 return ldb_next_request(module, req);
6434 we hook into the transaction operations to allow us to
6435 perform the linked attribute updates at the end of the whole
6436 transaction. This allows a forward linked attribute to be created
6437 before the object is created. During a vampire, w2k8 sends us linked
6438 attributes before the objects they are part of.
6440 static int replmd_start_transaction(struct ldb_module *module)
6442 /* create our private structure for this transaction */
6443 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6444 struct replmd_private);
6445 replmd_txn_cleanup(replmd_private);
6447 /* free any leftover mod_usn records from cancelled
6449 while (replmd_private->ncs) {
6450 struct nc_entry *e = replmd_private->ncs;
6451 DLIST_REMOVE(replmd_private->ncs, e);
6455 replmd_private->originating_updates = false;
6457 return ldb_next_start_trans(module);
6461 on prepare commit we loop over our queued la_context structures and
6464 static int replmd_prepare_commit(struct ldb_module *module)
6466 struct replmd_private *replmd_private =
6467 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6468 struct la_entry *la, *prev;
6469 struct la_backlink *bl;
6472 /* walk the list backwards, to do the first entry first, as we
6473 * added the entries with DLIST_ADD() which puts them at the
6474 * start of the list */
6475 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6476 prev = DLIST_PREV(la);
6477 DLIST_REMOVE(replmd_private->la_list, la);
6478 ret = replmd_process_linked_attribute(module, la, NULL);
6479 if (ret != LDB_SUCCESS) {
6480 replmd_txn_cleanup(replmd_private);
6485 /* process our backlink list, creating and deleting backlinks
6487 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6488 ret = replmd_process_backlink(module, bl, NULL);
6489 if (ret != LDB_SUCCESS) {
6490 replmd_txn_cleanup(replmd_private);
6495 replmd_txn_cleanup(replmd_private);
6497 /* possibly change @REPLCHANGED */
6498 ret = replmd_notify_store(module, NULL);
6499 if (ret != LDB_SUCCESS) {
6503 return ldb_next_prepare_commit(module);
6506 static int replmd_del_transaction(struct ldb_module *module)
6508 struct replmd_private *replmd_private =
6509 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6510 replmd_txn_cleanup(replmd_private);
6512 return ldb_next_del_trans(module);
6516 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6517 .name = "repl_meta_data",
6518 .init_context = replmd_init,
6520 .modify = replmd_modify,
6521 .rename = replmd_rename,
6522 .del = replmd_delete,
6523 .extended = replmd_extended,
6524 .start_transaction = replmd_start_transaction,
6525 .prepare_commit = replmd_prepare_commit,
6526 .del_transaction = replmd_del_transaction,
6529 int ldb_repl_meta_data_module_init(const char *version)
6531 LDB_MODULE_CHECK_VERSION(version);
6532 return ldb_register_module(&ldb_repl_meta_data_module_ops);