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;
2590 /* there the replmd_update_rpmd code has already
2591 * checked and saw that there are no linked
2596 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2598 * Nothing special is required for modifying or vanishing links
2599 * in fl2000 since they are just strings in a multi-valued
2602 struct ldb_control *ctrl = ldb_request_get_control(parent,
2603 DSDB_CONTROL_REPLMD_VANISH_LINKS);
2605 ctrl->critical = false;
2610 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2611 DSDB_FLAG_NEXT_MODULE |
2612 DSDB_SEARCH_SHOW_RECYCLED |
2613 DSDB_SEARCH_REVEAL_INTERNALS |
2614 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2616 if (ret != LDB_SUCCESS) {
2619 schema = dsdb_get_schema(ldb, res);
2621 return LDB_ERR_OPERATIONS_ERROR;
2624 old_msg = res->msgs[0];
2626 old_guid = samdb_result_guid(old_msg, "objectGUID");
2628 for (i=0; i<msg->num_elements; i++) {
2629 struct ldb_message_element *el = &msg->elements[i];
2630 struct ldb_message_element *old_el, *new_el;
2631 const struct dsdb_attribute *schema_attr
2632 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2634 ldb_asprintf_errstring(ldb,
2635 "%s: attribute %s is not a valid attribute in schema",
2636 __FUNCTION__, el->name);
2637 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2639 if (schema_attr->linkID == 0) {
2642 if ((schema_attr->linkID & 1) == 1) {
2643 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2646 /* Odd is for the target. Illegal to modify */
2647 ldb_asprintf_errstring(ldb,
2648 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2649 return LDB_ERR_UNWILLING_TO_PERFORM;
2651 old_el = ldb_msg_find_element(old_msg, el->name);
2652 switch (el->flags & LDB_FLAG_MOD_MASK) {
2653 case LDB_FLAG_MOD_REPLACE:
2654 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2656 case LDB_FLAG_MOD_DELETE:
2657 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2659 case LDB_FLAG_MOD_ADD:
2660 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2663 ldb_asprintf_errstring(ldb,
2664 "invalid flags 0x%x for %s linked attribute",
2665 el->flags, el->name);
2666 return LDB_ERR_UNWILLING_TO_PERFORM;
2668 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2669 ldb_asprintf_errstring(ldb,
2670 "Attribute %s is single valued but more than one value has been supplied",
2672 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2674 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2679 if (ret != LDB_SUCCESS) {
2683 ldb_msg_remove_attr(old_msg, el->name);
2685 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2686 new_el->num_values = el->num_values;
2687 new_el->values = talloc_steal(msg->elements, el->values);
2689 /* TODO: this relises a bit too heavily on the exact
2690 behaviour of ldb_msg_find_element and
2691 ldb_msg_remove_element */
2692 old_el = ldb_msg_find_element(msg, el->name);
2694 ldb_msg_remove_element(msg, old_el);
2705 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2707 struct ldb_context *ldb;
2708 struct replmd_replicated_request *ac;
2709 struct ldb_request *down_req;
2710 struct ldb_message *msg;
2711 time_t t = time(NULL);
2713 bool is_urgent = false, rodc = false;
2714 bool is_schema_nc = false;
2715 unsigned int functional_level;
2716 const struct ldb_message_element *guid_el = NULL;
2717 struct ldb_control *sd_propagation_control;
2718 struct replmd_private *replmd_private =
2719 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2721 /* do not manipulate our control entries */
2722 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2723 return ldb_next_request(module, req);
2726 sd_propagation_control = ldb_request_get_control(req,
2727 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2728 if (sd_propagation_control != NULL) {
2729 if (req->op.mod.message->num_elements != 1) {
2730 return ldb_module_operr(module);
2732 ret = strcmp(req->op.mod.message->elements[0].name,
2733 "nTSecurityDescriptor");
2735 return ldb_module_operr(module);
2738 return ldb_next_request(module, req);
2741 ldb = ldb_module_get_ctx(module);
2743 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2745 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2746 if (guid_el != NULL) {
2747 ldb_set_errstring(ldb,
2748 "replmd_modify: it's not allowed to change the objectGUID!");
2749 return LDB_ERR_CONSTRAINT_VIOLATION;
2752 ac = replmd_ctx_init(module, req);
2754 return ldb_module_oom(module);
2757 functional_level = dsdb_functional_level(ldb);
2759 /* we have to copy the message as the caller might have it as a const */
2760 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2764 return LDB_ERR_OPERATIONS_ERROR;
2767 ldb_msg_remove_attr(msg, "whenChanged");
2768 ldb_msg_remove_attr(msg, "uSNChanged");
2770 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2772 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2773 msg, &ac->seq_num, t, is_schema_nc,
2775 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2776 struct loadparm_context *lp_ctx;
2779 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2780 struct loadparm_context);
2782 referral = talloc_asprintf(req,
2784 lpcfg_dnsdomain(lp_ctx),
2785 ldb_dn_get_linearized(msg->dn));
2786 ret = ldb_module_send_referral(req, referral);
2791 if (ret != LDB_SUCCESS) {
2796 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2797 if (ret != LDB_SUCCESS) {
2803 * - replace the old object with the newly constructed one
2806 ac->is_urgent = is_urgent;
2808 ret = ldb_build_mod_req(&down_req, ldb, ac,
2811 ac, replmd_op_callback,
2813 LDB_REQ_SET_LOCATION(down_req);
2814 if (ret != LDB_SUCCESS) {
2819 /* current partition control is needed by "replmd_op_callback" */
2820 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2821 ret = ldb_request_add_control(down_req,
2822 DSDB_CONTROL_CURRENT_PARTITION_OID,
2824 if (ret != LDB_SUCCESS) {
2830 /* If we are in functional level 2000, then
2831 * replmd_modify_handle_linked_attribs will have done
2833 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2834 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2835 if (ret != LDB_SUCCESS) {
2841 talloc_steal(down_req, msg);
2843 /* we only change whenChanged and uSNChanged if the seq_num
2845 if (ac->seq_num != 0) {
2846 ret = add_time_element(msg, "whenChanged", t);
2847 if (ret != LDB_SUCCESS) {
2853 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2854 if (ret != LDB_SUCCESS) {
2861 /* go on with the call chain */
2862 return ldb_next_request(module, down_req);
2865 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2868 handle a rename request
2870 On a rename we need to do an extra ldb_modify which sets the
2871 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2873 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2875 struct ldb_context *ldb;
2876 struct replmd_replicated_request *ac;
2878 struct ldb_request *down_req;
2880 /* do not manipulate our control entries */
2881 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2882 return ldb_next_request(module, req);
2885 ldb = ldb_module_get_ctx(module);
2887 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2889 ac = replmd_ctx_init(module, req);
2891 return ldb_module_oom(module);
2894 ret = ldb_build_rename_req(&down_req, ldb, ac,
2895 ac->req->op.rename.olddn,
2896 ac->req->op.rename.newdn,
2898 ac, replmd_rename_callback,
2900 LDB_REQ_SET_LOCATION(down_req);
2901 if (ret != LDB_SUCCESS) {
2906 /* go on with the call chain */
2907 return ldb_next_request(module, down_req);
2910 /* After the rename is compleated, update the whenchanged etc */
2911 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2913 struct ldb_context *ldb;
2914 struct ldb_request *down_req;
2915 struct ldb_message *msg;
2916 const struct dsdb_attribute *rdn_attr;
2917 const char *rdn_name;
2918 const struct ldb_val *rdn_val;
2919 const char *attrs[5] = { NULL, };
2920 time_t t = time(NULL);
2922 bool is_urgent = false, rodc = false;
2924 struct replmd_replicated_request *ac =
2925 talloc_get_type(req->context, struct replmd_replicated_request);
2926 struct replmd_private *replmd_private =
2927 talloc_get_type(ldb_module_get_private(ac->module),
2928 struct replmd_private);
2930 ldb = ldb_module_get_ctx(ac->module);
2932 if (ares->error != LDB_SUCCESS) {
2933 return ldb_module_done(ac->req, ares->controls,
2934 ares->response, ares->error);
2937 if (ares->type != LDB_REPLY_DONE) {
2938 ldb_set_errstring(ldb,
2939 "invalid ldb_reply_type in callback");
2941 return ldb_module_done(ac->req, NULL, NULL,
2942 LDB_ERR_OPERATIONS_ERROR);
2946 * - replace the old object with the newly constructed one
2949 msg = ldb_msg_new(ac);
2952 return LDB_ERR_OPERATIONS_ERROR;
2955 msg->dn = ac->req->op.rename.newdn;
2957 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2959 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2960 if (rdn_name == NULL) {
2962 return ldb_module_done(ac->req, NULL, NULL,
2966 /* normalize the rdn attribute name */
2967 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2968 if (rdn_attr == NULL) {
2970 return ldb_module_done(ac->req, NULL, NULL,
2973 rdn_name = rdn_attr->lDAPDisplayName;
2975 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2976 if (rdn_val == NULL) {
2978 return ldb_module_done(ac->req, NULL, NULL,
2982 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2984 return ldb_module_done(ac->req, NULL, NULL,
2987 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2989 return ldb_module_done(ac->req, NULL, NULL,
2992 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2994 return ldb_module_done(ac->req, NULL, NULL,
2997 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2999 return ldb_module_done(ac->req, NULL, NULL,
3004 * here we let replmd_update_rpmd() only search for
3005 * the existing "replPropertyMetaData" and rdn_name attributes.
3007 * We do not want the existing "name" attribute as
3008 * the "name" attribute needs to get the version
3009 * updated on rename even if the rdn value hasn't changed.
3011 * This is the diff of the meta data, for a moved user
3012 * on a w2k8r2 server:
3015 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3016 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3017 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3018 * version : 0x00000001 (1)
3019 * reserved : 0x00000000 (0)
3020 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3021 * local_usn : 0x00000000000037a5 (14245)
3022 * array: struct replPropertyMetaData1
3023 * attid : DRSUAPI_ATTID_name (0x90001)
3024 * - version : 0x00000001 (1)
3025 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3026 * + version : 0x00000002 (2)
3027 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3028 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3029 * - originating_usn : 0x00000000000037a5 (14245)
3030 * - local_usn : 0x00000000000037a5 (14245)
3031 * + originating_usn : 0x0000000000003834 (14388)
3032 * + local_usn : 0x0000000000003834 (14388)
3033 * array: struct replPropertyMetaData1
3034 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3035 * version : 0x00000004 (4)
3037 attrs[0] = "replPropertyMetaData";
3038 attrs[1] = "objectClass";
3039 attrs[2] = "instanceType";
3040 attrs[3] = rdn_name;
3043 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3044 msg, &ac->seq_num, t,
3045 is_schema_nc, &is_urgent, &rodc);
3046 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3047 struct ldb_dn *olddn = ac->req->op.rename.olddn;
3048 struct loadparm_context *lp_ctx;
3051 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3052 struct loadparm_context);
3054 referral = talloc_asprintf(req,
3056 lpcfg_dnsdomain(lp_ctx),
3057 ldb_dn_get_linearized(olddn));
3058 ret = ldb_module_send_referral(req, referral);
3060 return ldb_module_done(req, NULL, NULL, ret);
3063 if (ret != LDB_SUCCESS) {
3065 return ldb_module_done(ac->req, NULL, NULL, ret);
3068 if (ac->seq_num == 0) {
3070 return ldb_module_done(ac->req, NULL, NULL,
3072 "internal error seq_num == 0"));
3074 ac->is_urgent = is_urgent;
3076 ret = ldb_build_mod_req(&down_req, ldb, ac,
3079 ac, replmd_op_callback,
3081 LDB_REQ_SET_LOCATION(down_req);
3082 if (ret != LDB_SUCCESS) {
3087 /* current partition control is needed by "replmd_op_callback" */
3088 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3089 ret = ldb_request_add_control(down_req,
3090 DSDB_CONTROL_CURRENT_PARTITION_OID,
3092 if (ret != LDB_SUCCESS) {
3098 talloc_steal(down_req, msg);
3100 ret = add_time_element(msg, "whenChanged", t);
3101 if (ret != LDB_SUCCESS) {
3107 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3108 if (ret != LDB_SUCCESS) {
3114 /* go on with the call chain - do the modify after the rename */
3115 return ldb_next_request(ac->module, down_req);
3119 * remove links from objects that point at this object when an object
3120 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3121 * RemoveObj which states that link removal due to the object being
3122 * deleted is NOT an originating update - they just go away!
3125 static int replmd_delete_remove_link(struct ldb_module *module,
3126 const struct dsdb_schema *schema,
3128 struct ldb_message_element *el,
3129 const struct dsdb_attribute *sa,
3130 struct ldb_request *parent)
3133 TALLOC_CTX *tmp_ctx = talloc_new(module);
3134 struct ldb_context *ldb = ldb_module_get_ctx(module);
3136 for (i=0; i<el->num_values; i++) {
3137 struct dsdb_dn *dsdb_dn;
3141 struct ldb_message *msg;
3142 const struct dsdb_attribute *target_attr;
3143 struct ldb_message_element *el2;
3144 struct ldb_val dn_val;
3145 uint32_t dsdb_flags = 0;
3147 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3151 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3153 talloc_free(tmp_ctx);
3154 return LDB_ERR_OPERATIONS_ERROR;
3157 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
3158 if (!NT_STATUS_IS_OK(status)) {
3159 talloc_free(tmp_ctx);
3160 return LDB_ERR_OPERATIONS_ERROR;
3163 /* remove the link */
3164 msg = ldb_msg_new(tmp_ctx);
3166 ldb_module_oom(module);
3167 talloc_free(tmp_ctx);
3168 return LDB_ERR_OPERATIONS_ERROR;
3172 msg->dn = dsdb_dn->dn;
3174 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3175 if (target_attr == NULL) {
3179 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
3180 if (ret != LDB_SUCCESS) {
3181 ldb_module_oom(module);
3182 talloc_free(tmp_ctx);
3183 return LDB_ERR_OPERATIONS_ERROR;
3185 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
3186 el2->values = &dn_val;
3187 el2->num_values = 1;
3190 * Ensure that we tell the modification to vanish any linked
3191 * attributes (not simply mark them as isDeleted = TRUE)
3193 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3195 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3196 if (ret != LDB_SUCCESS) {
3197 talloc_free(tmp_ctx);
3201 talloc_free(tmp_ctx);
3207 handle update of replication meta data for deletion of objects
3209 This also handles the mapping of delete to a rename operation
3210 to allow deletes to be replicated.
3212 It also handles the incoming deleted objects, to ensure they are
3213 fully deleted here. In that case re_delete is true, and we do not
3214 use this as a signal to change the deleted state, just reinforce it.
3217 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3219 int ret = LDB_ERR_OTHER;
3220 bool retb, disallow_move_on_delete;
3221 struct ldb_dn *old_dn, *new_dn;
3222 const char *rdn_name;
3223 const struct ldb_val *rdn_value, *new_rdn_value;
3225 struct ldb_context *ldb = ldb_module_get_ctx(module);
3226 const struct dsdb_schema *schema;
3227 struct ldb_message *msg, *old_msg;
3228 struct ldb_message_element *el;
3229 TALLOC_CTX *tmp_ctx;
3230 struct ldb_result *res, *parent_res;
3231 static const char * const preserved_attrs[] = {
3232 /* yes, this really is a hard coded list. See MS-ADTS
3233 section 3.1.1.5.5.1.1 */
3236 "dNReferenceUpdate",
3247 "msDS-LastKnownRDN",
3253 "distinguishedName",
3257 "proxiedObjectName",
3259 "nTSecurityDescriptor",
3260 "replPropertyMetaData",
3262 "securityIdentifier",
3270 "userAccountControl",
3277 static const char * const all_attrs[] = {
3278 DSDB_SECRET_ATTRIBUTES,
3282 unsigned int i, el_count = 0;
3283 uint32_t dsdb_flags = 0;
3284 enum deletion_state deletion_state, next_deletion_state;
3286 if (ldb_dn_is_special(req->op.del.dn)) {
3287 return ldb_next_request(module, req);
3291 * We have to allow dbcheck to remove an object that
3292 * is beyond repair, and to do so totally. This could
3293 * mean we we can get a partial object from the other
3294 * DC, causing havoc, so dbcheck suggests
3295 * re-replication first. dbcheck sets both DBCHECK
3296 * and RELAX in this situation.
3298 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3299 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3300 /* really, really remove it */
3301 return ldb_next_request(module, req);
3304 tmp_ctx = talloc_new(ldb);
3307 return LDB_ERR_OPERATIONS_ERROR;
3310 schema = dsdb_get_schema(ldb, tmp_ctx);
3312 talloc_free(tmp_ctx);
3313 return LDB_ERR_OPERATIONS_ERROR;
3316 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3318 /* we need the complete msg off disk, so we can work out which
3319 attributes need to be removed */
3320 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3321 DSDB_FLAG_NEXT_MODULE |
3322 DSDB_SEARCH_SHOW_RECYCLED |
3323 DSDB_SEARCH_REVEAL_INTERNALS |
3324 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3325 if (ret != LDB_SUCCESS) {
3326 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3327 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3328 re_delete ? "re-delete" : "delete",
3329 ldb_dn_get_linearized(old_dn),
3330 ldb_errstring(ldb_module_get_ctx(module)));
3331 talloc_free(tmp_ctx);
3334 old_msg = res->msgs[0];
3336 replmd_deletion_state(module, old_msg,
3338 &next_deletion_state);
3340 /* This supports us noticing an incoming isDeleted and acting on it */
3342 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3343 next_deletion_state = deletion_state;
3346 if (next_deletion_state == OBJECT_REMOVED) {
3348 * We have to prevent objects being deleted, even if
3349 * the administrator really wants them gone, as
3350 * without the tombstone, we can get a partial object
3351 * from the other DC, causing havoc.
3353 * The only other valid case is when the 180 day
3354 * timeout has expired, when relax is specified.
3356 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3357 /* it is already deleted - really remove it this time */
3358 talloc_free(tmp_ctx);
3359 return ldb_next_request(module, req);
3362 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3363 "This check is to prevent corruption of the replicated state.",
3364 ldb_dn_get_linearized(old_msg->dn));
3365 return LDB_ERR_UNWILLING_TO_PERFORM;
3368 rdn_name = ldb_dn_get_rdn_name(old_dn);
3369 rdn_value = ldb_dn_get_rdn_val(old_dn);
3370 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3371 talloc_free(tmp_ctx);
3372 return ldb_operr(ldb);
3375 msg = ldb_msg_new(tmp_ctx);
3377 ldb_module_oom(module);
3378 talloc_free(tmp_ctx);
3379 return LDB_ERR_OPERATIONS_ERROR;
3384 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3385 disallow_move_on_delete =
3386 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3387 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3389 /* work out where we will be renaming this object to */
3390 if (!disallow_move_on_delete) {
3391 struct ldb_dn *deleted_objects_dn;
3392 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3393 &deleted_objects_dn);
3396 * We should not move objects if we can't find the
3397 * deleted objects DN. Not moving (or otherwise
3398 * harming) the Deleted Objects DN itself is handled
3401 if (re_delete && (ret != LDB_SUCCESS)) {
3402 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3403 if (new_dn == NULL) {
3404 ldb_module_oom(module);
3405 talloc_free(tmp_ctx);
3406 return LDB_ERR_OPERATIONS_ERROR;
3408 } else if (ret != LDB_SUCCESS) {
3409 /* this is probably an attempted delete on a partition
3410 * that doesn't allow delete operations, such as the
3411 * schema partition */
3412 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3413 ldb_dn_get_linearized(old_dn));
3414 talloc_free(tmp_ctx);
3415 return LDB_ERR_UNWILLING_TO_PERFORM;
3417 new_dn = deleted_objects_dn;
3420 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3421 if (new_dn == NULL) {
3422 ldb_module_oom(module);
3423 talloc_free(tmp_ctx);
3424 return LDB_ERR_OPERATIONS_ERROR;
3428 if (deletion_state == OBJECT_NOT_DELETED) {
3429 /* get the objects GUID from the search we just did */
3430 guid = samdb_result_guid(old_msg, "objectGUID");
3432 /* Add a formatted child */
3433 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3435 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3436 GUID_string(tmp_ctx, &guid));
3438 ldb_asprintf_errstring(ldb, __location__
3439 ": Unable to add a formatted child to dn: %s",
3440 ldb_dn_get_linearized(new_dn));
3441 talloc_free(tmp_ctx);
3442 return LDB_ERR_OPERATIONS_ERROR;
3445 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3446 if (ret != LDB_SUCCESS) {
3447 ldb_asprintf_errstring(ldb, __location__
3448 ": Failed to add isDeleted string to the msg");
3449 talloc_free(tmp_ctx);
3452 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3455 * No matter what has happened with other renames etc, try again to
3456 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3459 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3460 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3462 ldb_asprintf_errstring(ldb, __location__
3463 ": Unable to add a prepare rdn of %s",
3464 ldb_dn_get_linearized(rdn));
3465 talloc_free(tmp_ctx);
3466 return LDB_ERR_OPERATIONS_ERROR;
3468 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3470 retb = ldb_dn_add_child(new_dn, rdn);
3472 ldb_asprintf_errstring(ldb, __location__
3473 ": Unable to add rdn %s to base dn: %s",
3474 ldb_dn_get_linearized(rdn),
3475 ldb_dn_get_linearized(new_dn));
3476 talloc_free(tmp_ctx);
3477 return LDB_ERR_OPERATIONS_ERROR;
3482 now we need to modify the object in the following ways:
3484 - add isDeleted=TRUE
3485 - update rDN and name, with new rDN
3486 - remove linked attributes
3487 - remove objectCategory and sAMAccountType
3488 - remove attribs not on the preserved list
3489 - preserved if in above list, or is rDN
3490 - remove all linked attribs from this object
3491 - remove all links from other objects to this object
3492 - add lastKnownParent
3493 - update replPropertyMetaData?
3495 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3498 if (deletion_state == OBJECT_NOT_DELETED) {
3499 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3500 char *parent_dn_str = NULL;
3502 /* we need the storage form of the parent GUID */
3503 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3505 DSDB_FLAG_NEXT_MODULE |
3506 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3507 DSDB_SEARCH_REVEAL_INTERNALS|
3508 DSDB_SEARCH_SHOW_RECYCLED, req);
3509 if (ret != LDB_SUCCESS) {
3510 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3511 "repmd_delete: Failed to %s %s, "
3512 "because we failed to find it's parent (%s): %s",
3513 re_delete ? "re-delete" : "delete",
3514 ldb_dn_get_linearized(old_dn),
3515 ldb_dn_get_linearized(parent_dn),
3516 ldb_errstring(ldb_module_get_ctx(module)));
3517 talloc_free(tmp_ctx);
3522 * Now we can use the DB version,
3523 * it will have the extended DN info in it
3525 parent_dn = parent_res->msgs[0]->dn;
3526 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3529 if (parent_dn_str == NULL) {
3530 talloc_free(tmp_ctx);
3531 return ldb_module_oom(module);
3534 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3536 if (ret != LDB_SUCCESS) {
3537 ldb_asprintf_errstring(ldb, __location__
3538 ": Failed to add lastKnownParent "
3539 "string when deleting %s",
3540 ldb_dn_get_linearized(old_dn));
3541 talloc_free(tmp_ctx);
3544 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3546 if (next_deletion_state == OBJECT_DELETED) {
3547 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3548 if (ret != LDB_SUCCESS) {
3549 ldb_asprintf_errstring(ldb, __location__
3550 ": Failed to add msDS-LastKnownRDN "
3551 "string when deleting %s",
3552 ldb_dn_get_linearized(old_dn));
3553 talloc_free(tmp_ctx);
3556 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3560 switch (next_deletion_state) {
3562 case OBJECT_RECYCLED:
3563 case OBJECT_TOMBSTONE:
3566 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3567 * describes what must be removed from a tombstone
3570 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3571 * describes what must be removed from a recycled
3577 * we also mark it as recycled, meaning this object can't be
3578 * recovered (we are stripping its attributes).
3579 * This is done only if we have this schema object of course ...
3580 * This behavior is identical to the one of Windows 2008R2 which
3581 * always set the isRecycled attribute, even if the recycle-bin is
3582 * not activated and what ever the forest level is.
3584 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3585 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3586 if (ret != LDB_SUCCESS) {
3587 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3588 ldb_module_oom(module);
3589 talloc_free(tmp_ctx);
3592 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3595 /* work out which of the old attributes we will be removing */
3596 for (i=0; i<old_msg->num_elements; i++) {
3597 const struct dsdb_attribute *sa;
3598 el = &old_msg->elements[i];
3599 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3601 talloc_free(tmp_ctx);
3602 return LDB_ERR_OPERATIONS_ERROR;
3604 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3605 /* don't remove the rDN */
3608 if (sa->linkID && (sa->linkID & 1)) {
3610 we have a backlink in this object
3611 that needs to be removed. We're not
3612 allowed to remove it directly
3613 however, so we instead setup a
3614 modify to delete the corresponding
3617 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3618 if (ret != LDB_SUCCESS) {
3619 const char *old_dn_str
3620 = ldb_dn_get_linearized(old_dn);
3621 ldb_asprintf_errstring(ldb,
3623 ": Failed to remove backlink of "
3624 "%s when deleting %s",
3627 talloc_free(tmp_ctx);
3628 return LDB_ERR_OPERATIONS_ERROR;
3630 /* now we continue, which means we
3631 won't remove this backlink
3637 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3640 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3645 * Ensure that we tell the modification to vanish any linked
3646 * attributes (not simply mark them as isDeleted = TRUE)
3648 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3650 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3651 if (ret != LDB_SUCCESS) {
3652 talloc_free(tmp_ctx);
3653 ldb_module_oom(module);
3660 case OBJECT_DELETED:
3662 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3663 * describes what must be removed from a deleted
3667 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3668 if (ret != LDB_SUCCESS) {
3669 talloc_free(tmp_ctx);
3670 ldb_module_oom(module);
3674 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3675 if (ret != LDB_SUCCESS) {
3676 talloc_free(tmp_ctx);
3677 ldb_module_oom(module);
3687 if (deletion_state == OBJECT_NOT_DELETED) {
3688 const struct dsdb_attribute *sa;
3690 /* work out what the new rdn value is, for updating the
3691 rDN and name fields */
3692 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3693 if (new_rdn_value == NULL) {
3694 talloc_free(tmp_ctx);
3695 return ldb_operr(ldb);
3698 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3700 talloc_free(tmp_ctx);
3701 return LDB_ERR_OPERATIONS_ERROR;
3704 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3706 if (ret != LDB_SUCCESS) {
3707 talloc_free(tmp_ctx);
3710 el->flags = LDB_FLAG_MOD_REPLACE;
3712 el = ldb_msg_find_element(old_msg, "name");
3714 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3715 if (ret != LDB_SUCCESS) {
3716 talloc_free(tmp_ctx);
3719 el->flags = LDB_FLAG_MOD_REPLACE;
3724 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3729 * No matter what has happned with other renames, try again to
3730 * get this to be under the deleted DN.
3732 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3733 /* now rename onto the new DN */
3734 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3735 if (ret != LDB_SUCCESS){
3736 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3737 ldb_dn_get_linearized(old_dn),
3738 ldb_dn_get_linearized(new_dn),
3739 ldb_errstring(ldb)));
3740 talloc_free(tmp_ctx);
3746 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
3747 if (ret != LDB_SUCCESS) {
3748 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3749 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3750 talloc_free(tmp_ctx);
3754 talloc_free(tmp_ctx);
3756 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3759 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3761 return replmd_delete_internals(module, req, false);
3765 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3770 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3772 int ret = LDB_ERR_OTHER;
3773 /* TODO: do some error mapping */
3775 /* Let the caller know the full WERROR */
3776 ar->objs->error = status;
3782 static struct replPropertyMetaData1 *
3783 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3784 enum drsuapi_DsAttributeId attid)
3787 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3789 for (i = 0; i < rpmd_ctr->count; i++) {
3790 if (rpmd_ctr->array[i].attid == attid) {
3791 return &rpmd_ctr->array[i];
3799 return true if an update is newer than an existing entry
3800 see section 5.11 of MS-ADTS
3802 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3803 const struct GUID *update_invocation_id,
3804 uint32_t current_version,
3805 uint32_t update_version,
3806 NTTIME current_change_time,
3807 NTTIME update_change_time)
3809 if (update_version != current_version) {
3810 return update_version > current_version;
3812 if (update_change_time != current_change_time) {
3813 return update_change_time > current_change_time;
3815 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3818 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3819 struct replPropertyMetaData1 *new_m)
3821 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3822 &new_m->originating_invocation_id,
3825 cur_m->originating_change_time,
3826 new_m->originating_change_time);
3829 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
3830 struct replPropertyMetaData1 *cur_m,
3831 struct replPropertyMetaData1 *new_m)
3836 * If the new replPropertyMetaData entry for this attribute is
3837 * not provided (this happens in the case where we look for
3838 * ATTID_name, but the name was not changed), then the local
3839 * state is clearly still current, as the remote
3840 * server didn't send it due to being older the high watermark
3843 if (new_m == NULL) {
3847 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3849 * if we compare equal then do an
3850 * update. This is used when a client
3851 * asks for a FULL_SYNC, and can be
3852 * used to recover a corrupt
3855 * This call is a bit tricky, what we
3856 * are doing it turning the 'is_newer'
3857 * call into a 'not is older' by
3858 * swapping cur_m and new_m, and negating the
3861 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
3864 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
3874 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3876 const struct ldb_val *rdn_val;
3877 const char *rdn_name;
3878 struct ldb_dn *new_dn;
3880 rdn_val = ldb_dn_get_rdn_val(dn);
3881 rdn_name = ldb_dn_get_rdn_name(dn);
3882 if (!rdn_val || !rdn_name) {
3886 new_dn = ldb_dn_copy(mem_ctx, dn);
3891 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3895 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3897 ldb_dn_escape_value(new_dn, *rdn_val),
3898 GUID_string(new_dn, guid))) {
3907 perform a modify operation which sets the rDN and name attributes to
3908 their current values. This has the effect of changing these
3909 attributes to have been last updated by the current DC. This is
3910 needed to ensure that renames performed as part of conflict
3911 resolution are propogated to other DCs
3913 static int replmd_name_modify(struct replmd_replicated_request *ar,
3914 struct ldb_request *req, struct ldb_dn *dn)
3916 struct ldb_message *msg;
3917 const char *rdn_name;
3918 const struct ldb_val *rdn_val;
3919 const struct dsdb_attribute *rdn_attr;
3922 msg = ldb_msg_new(req);
3928 rdn_name = ldb_dn_get_rdn_name(dn);
3929 if (rdn_name == NULL) {
3933 /* normalize the rdn attribute name */
3934 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3935 if (rdn_attr == NULL) {
3938 rdn_name = rdn_attr->lDAPDisplayName;
3940 rdn_val = ldb_dn_get_rdn_val(dn);
3941 if (rdn_val == NULL) {
3945 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3948 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3951 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3954 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3958 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3959 if (ret != LDB_SUCCESS) {
3960 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3961 ldb_dn_get_linearized(dn),
3962 ldb_errstring(ldb_module_get_ctx(ar->module))));
3972 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3973 ldb_dn_get_linearized(dn)));
3974 return LDB_ERR_OPERATIONS_ERROR;
3979 callback for conflict DN handling where we have renamed the incoming
3980 record. After renaming it, we need to ensure the change of name and
3981 rDN for the incoming record is seen as an originating update by this DC.
3983 This also handles updating lastKnownParent for entries sent to lostAndFound
3985 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3987 struct replmd_replicated_request *ar =
3988 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3989 struct ldb_dn *conflict_dn = NULL;
3992 if (ares->error != LDB_SUCCESS) {
3993 /* call the normal callback for everything except success */
3994 return replmd_op_callback(req, ares);
3997 switch (req->operation) {
3999 conflict_dn = req->op.add.message->dn;
4002 conflict_dn = req->op.mod.message->dn;
4005 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4008 /* perform a modify of the rDN and name of the record */
4009 ret = replmd_name_modify(ar, req, conflict_dn);
4010 if (ret != LDB_SUCCESS) {
4012 return replmd_op_callback(req, ares);
4015 if (ar->objs->objects[ar->index_current].last_known_parent) {
4016 struct ldb_message *msg = ldb_msg_new(req);
4018 ldb_module_oom(ar->module);
4019 return LDB_ERR_OPERATIONS_ERROR;
4022 msg->dn = req->op.add.message->dn;
4024 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4025 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4026 if (ret != LDB_SUCCESS) {
4027 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4028 ldb_module_oom(ar->module);
4031 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4033 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4034 if (ret != LDB_SUCCESS) {
4035 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4036 ldb_dn_get_linearized(msg->dn),
4037 ldb_errstring(ldb_module_get_ctx(ar->module))));
4043 return replmd_op_callback(req, ares);
4047 callback for replmd_replicated_apply_add()
4048 This copes with the creation of conflict records in the case where
4049 the DN exists, but with a different objectGUID
4051 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))
4053 struct ldb_dn *conflict_dn;
4054 struct replmd_replicated_request *ar =
4055 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4056 struct ldb_result *res;
4057 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4059 const struct ldb_val *omd_value;
4060 struct replPropertyMetaDataBlob omd, *rmd;
4061 enum ndr_err_code ndr_err;
4062 bool rename_incoming_record, rodc;
4063 struct replPropertyMetaData1 *rmd_name, *omd_name;
4064 struct ldb_message *msg;
4065 struct ldb_request *down_req = NULL;
4067 /* call the normal callback for success */
4068 if (ares->error == LDB_SUCCESS) {
4069 return callback(req, ares);
4073 * we have a conflict, and need to decide if we will keep the
4074 * new record or the old record
4077 msg = ar->objs->objects[ar->index_current].msg;
4078 conflict_dn = msg->dn;
4080 /* For failures other than conflicts, fail the whole operation here */
4081 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4082 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4083 ldb_dn_get_linearized(conflict_dn),
4084 ldb_errstring(ldb_module_get_ctx(ar->module)));
4086 return ldb_module_done(ar->req, NULL, NULL,
4087 LDB_ERR_OPERATIONS_ERROR);
4090 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4091 if (ret != LDB_SUCCESS) {
4092 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)));
4093 return ldb_module_done(ar->req, NULL, NULL,
4094 LDB_ERR_OPERATIONS_ERROR);
4100 * We are on an RODC, or were a GC for this
4101 * partition, so we have to fail this until
4102 * someone who owns the partition sorts it
4105 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4106 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4107 " - We must fail the operation until a master for this partition resolves the conflict",
4108 ldb_dn_get_linearized(conflict_dn));
4113 * first we need the replPropertyMetaData attribute from the
4114 * local, conflicting record
4116 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4118 DSDB_FLAG_NEXT_MODULE |
4119 DSDB_SEARCH_SHOW_DELETED |
4120 DSDB_SEARCH_SHOW_RECYCLED, req);
4121 if (ret != LDB_SUCCESS) {
4122 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4123 ldb_dn_get_linearized(conflict_dn)));
4127 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4128 if (omd_value == NULL) {
4129 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4130 ldb_dn_get_linearized(conflict_dn)));
4134 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4135 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4137 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4138 ldb_dn_get_linearized(conflict_dn)));
4142 rmd = ar->objs->objects[ar->index_current].meta_data;
4145 * we decide which is newer based on the RPMD on the name
4146 * attribute. See [MS-DRSR] ResolveNameConflict.
4148 * We expect omd_name to be present, as this is from a local
4149 * search, but while rmd_name should have been given to us by
4150 * the remote server, if it is missing we just prefer the
4152 * replmd_replPropertyMetaData1_new_should_be_taken()
4154 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4155 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4157 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4158 ldb_dn_get_linearized(conflict_dn)));
4163 * Should we preserve the current record, and so rename the
4164 * incoming record to be a conflict?
4166 rename_incoming_record
4167 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4168 omd_name, rmd_name);
4170 if (rename_incoming_record) {
4172 struct ldb_dn *new_dn;
4174 guid = samdb_result_guid(msg, "objectGUID");
4175 if (GUID_all_zero(&guid)) {
4176 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4177 ldb_dn_get_linearized(conflict_dn)));
4180 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4181 if (new_dn == NULL) {
4182 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4183 ldb_dn_get_linearized(conflict_dn)));
4187 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4188 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4190 /* re-submit the request, but with the new DN */
4191 callback = replmd_op_name_modify_callback;
4194 /* we are renaming the existing record */
4196 struct ldb_dn *new_dn;
4198 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4199 if (GUID_all_zero(&guid)) {
4200 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4201 ldb_dn_get_linearized(conflict_dn)));
4205 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4206 if (new_dn == NULL) {
4207 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4208 ldb_dn_get_linearized(conflict_dn)));
4212 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4213 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4215 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4216 DSDB_FLAG_OWN_MODULE, req);
4217 if (ret != LDB_SUCCESS) {
4218 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4219 ldb_dn_get_linearized(conflict_dn),
4220 ldb_dn_get_linearized(new_dn),
4221 ldb_errstring(ldb_module_get_ctx(ar->module))));
4226 * now we need to ensure that the rename is seen as an
4227 * originating update. We do that with a modify.
4229 ret = replmd_name_modify(ar, req, new_dn);
4230 if (ret != LDB_SUCCESS) {
4234 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4235 ldb_dn_get_linearized(req->op.add.message->dn)));
4238 ret = ldb_build_add_req(&down_req,
4239 ldb_module_get_ctx(ar->module),
4246 if (ret != LDB_SUCCESS) {
4249 LDB_REQ_SET_LOCATION(down_req);
4251 /* current partition control needed by "repmd_op_callback" */
4252 ret = ldb_request_add_control(down_req,
4253 DSDB_CONTROL_CURRENT_PARTITION_OID,
4255 if (ret != LDB_SUCCESS) {
4256 return replmd_replicated_request_error(ar, ret);
4259 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4260 /* this tells the partition module to make it a
4261 partial replica if creating an NC */
4262 ret = ldb_request_add_control(down_req,
4263 DSDB_CONTROL_PARTIAL_REPLICA,
4265 if (ret != LDB_SUCCESS) {
4266 return replmd_replicated_request_error(ar, ret);
4271 * Finally we re-run the add, otherwise the new record won't
4272 * exist, as we are here because of that exact failure!
4274 return ldb_next_request(ar->module, down_req);
4277 /* on failure make the caller get the error. This means
4278 * replication will stop with an error, but there is not much
4281 return ldb_module_done(ar->req, NULL, NULL,
4286 callback for replmd_replicated_apply_add()
4287 This copes with the creation of conflict records in the case where
4288 the DN exists, but with a different objectGUID
4290 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4292 struct replmd_replicated_request *ar =
4293 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4295 if (ar->objs->objects[ar->index_current].last_known_parent) {
4296 /* This is like a conflict DN, where we put the object in LostAndFound
4297 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4298 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4301 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4305 this is called when a new object comes in over DRS
4307 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4309 struct ldb_context *ldb;
4310 struct ldb_request *change_req;
4311 enum ndr_err_code ndr_err;
4312 struct ldb_message *msg;
4313 struct replPropertyMetaDataBlob *md;
4314 struct ldb_val md_value;
4317 bool remote_isDeleted = false;
4320 time_t t = time(NULL);
4321 const struct ldb_val *rdn_val;
4322 struct replmd_private *replmd_private =
4323 talloc_get_type(ldb_module_get_private(ar->module),
4324 struct replmd_private);
4325 unix_to_nt_time(&now, t);
4327 ldb = ldb_module_get_ctx(ar->module);
4328 msg = ar->objs->objects[ar->index_current].msg;
4329 md = ar->objs->objects[ar->index_current].meta_data;
4330 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4332 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4333 if (ret != LDB_SUCCESS) {
4334 return replmd_replicated_request_error(ar, ret);
4337 ret = dsdb_msg_add_guid(msg,
4338 &ar->objs->objects[ar->index_current].object_guid,
4340 if (ret != LDB_SUCCESS) {
4341 return replmd_replicated_request_error(ar, ret);
4344 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4345 if (ret != LDB_SUCCESS) {
4346 return replmd_replicated_request_error(ar, ret);
4349 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4350 if (ret != LDB_SUCCESS) {
4351 return replmd_replicated_request_error(ar, ret);
4354 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4355 if (ret != LDB_SUCCESS) {
4356 return replmd_replicated_request_error(ar, ret);
4359 /* remove any message elements that have zero values */
4360 for (i=0; i<msg->num_elements; i++) {
4361 struct ldb_message_element *el = &msg->elements[i];
4363 if (el->num_values == 0) {
4364 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4365 ldb_asprintf_errstring(ldb, __location__
4366 ": empty objectClass sent on %s, aborting replication\n",
4367 ldb_dn_get_linearized(msg->dn));
4368 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4371 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4373 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4374 msg->num_elements--;
4381 struct GUID_txt_buf guid_txt;
4383 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4384 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4385 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4390 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4391 "isDeleted", false);
4394 * the meta data array is already sorted by the caller, except
4395 * for the RDN, which needs to be added.
4399 rdn_val = ldb_dn_get_rdn_val(msg->dn);
4400 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
4401 md, ar, now, is_schema_nc);
4402 if (ret != LDB_SUCCESS) {
4403 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4404 return replmd_replicated_request_error(ar, ret);
4407 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
4408 if (ret != LDB_SUCCESS) {
4409 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4410 return replmd_replicated_request_error(ar, ret);
4413 for (i=0; i < md->ctr.ctr1.count; i++) {
4414 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4416 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4417 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4418 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4419 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4420 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4422 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4423 if (ret != LDB_SUCCESS) {
4424 return replmd_replicated_request_error(ar, ret);
4427 replmd_ldb_message_sort(msg, ar->schema);
4429 if (!remote_isDeleted) {
4430 ret = dsdb_module_schedule_sd_propagation(ar->module,
4431 ar->objs->partition_dn,
4433 if (ret != LDB_SUCCESS) {
4434 return replmd_replicated_request_error(ar, ret);
4438 ar->isDeleted = remote_isDeleted;
4440 ret = ldb_build_add_req(&change_req,
4446 replmd_op_add_callback,
4448 LDB_REQ_SET_LOCATION(change_req);
4449 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4451 /* current partition control needed by "repmd_op_callback" */
4452 ret = ldb_request_add_control(change_req,
4453 DSDB_CONTROL_CURRENT_PARTITION_OID,
4455 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4457 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4458 /* this tells the partition module to make it a
4459 partial replica if creating an NC */
4460 ret = ldb_request_add_control(change_req,
4461 DSDB_CONTROL_PARTIAL_REPLICA,
4463 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4466 return ldb_next_request(ar->module, change_req);
4469 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4470 struct ldb_reply *ares)
4472 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4473 struct replmd_replicated_request);
4477 return ldb_module_done(ar->req, NULL, NULL,
4478 LDB_ERR_OPERATIONS_ERROR);
4482 * The error NO_SUCH_OBJECT is not expected, unless the search
4483 * base is the partition DN, and that case doesn't happen here
4484 * because then we wouldn't get a parent_guid_value in any
4487 if (ares->error != LDB_SUCCESS) {
4488 return ldb_module_done(ar->req, ares->controls,
4489 ares->response, ares->error);
4492 switch (ares->type) {
4493 case LDB_REPLY_ENTRY:
4495 struct ldb_message *parent_msg = ares->message;
4496 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4497 struct ldb_dn *parent_dn;
4500 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4501 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4502 /* Per MS-DRSR 4.1.10.6.10
4503 * FindBestParentObject we need to move this
4504 * new object under a deleted object to
4506 struct ldb_dn *nc_root;
4508 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4509 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4510 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4511 "No suitable NC root found for %s. "
4512 "We need to move this object because parent object %s "
4513 "is deleted, but this object is not.",
4514 ldb_dn_get_linearized(msg->dn),
4515 ldb_dn_get_linearized(parent_msg->dn));
4516 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4517 } else if (ret != LDB_SUCCESS) {
4518 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4519 "Unable to find NC root for %s: %s. "
4520 "We need to move this object because parent object %s "
4521 "is deleted, but this object is not.",
4522 ldb_dn_get_linearized(msg->dn),
4523 ldb_errstring(ldb_module_get_ctx(ar->module)),
4524 ldb_dn_get_linearized(parent_msg->dn));
4525 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4528 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4530 DS_GUID_LOSTANDFOUND_CONTAINER,
4532 if (ret != LDB_SUCCESS) {
4533 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4534 "Unable to find LostAndFound Container for %s "
4535 "in partition %s: %s. "
4536 "We need to move this object because parent object %s "
4537 "is deleted, but this object is not.",
4538 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4539 ldb_errstring(ldb_module_get_ctx(ar->module)),
4540 ldb_dn_get_linearized(parent_msg->dn));
4541 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4543 ar->objs->objects[ar->index_current].last_known_parent
4544 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4548 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4551 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4553 comp_num = ldb_dn_get_comp_num(msg->dn);
4555 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4557 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4560 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4562 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4566 case LDB_REPLY_REFERRAL:
4567 /* we ignore referrals */
4570 case LDB_REPLY_DONE:
4572 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4573 struct GUID_txt_buf str_buf;
4574 if (ar->search_msg != NULL) {
4575 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4576 "No parent with GUID %s found for object locally known as %s",
4577 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4578 ldb_dn_get_linearized(ar->search_msg->dn));
4580 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4581 "No parent with GUID %s found for object remotely known as %s",
4582 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4583 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4587 * This error code is really important, as it
4588 * is the flag back to the callers to retry
4589 * this with DRSUAPI_DRS_GET_ANC, and so get
4590 * the parent objects before the child
4593 return ldb_module_done(ar->req, NULL, NULL,
4594 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4597 if (ar->search_msg != NULL) {
4598 ret = replmd_replicated_apply_merge(ar);
4600 ret = replmd_replicated_apply_add(ar);
4602 if (ret != LDB_SUCCESS) {
4603 return ldb_module_done(ar->req, NULL, NULL, ret);
4612 * Look for the parent object, so we put the new object in the right
4613 * place This is akin to NameObject in MS-DRSR - this routine and the
4614 * callbacks find the right parent name, and correct name for this
4618 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4620 struct ldb_context *ldb;
4624 struct ldb_request *search_req;
4625 static const char *attrs[] = {"isDeleted", NULL};
4626 struct GUID_txt_buf guid_str_buf;
4628 ldb = ldb_module_get_ctx(ar->module);
4630 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4631 if (ar->search_msg != NULL) {
4632 return replmd_replicated_apply_merge(ar);
4634 return replmd_replicated_apply_add(ar);
4638 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4641 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4642 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4644 ret = ldb_build_search_req(&search_req,
4647 ar->objs->partition_dn,
4653 replmd_replicated_apply_search_for_parent_callback,
4655 LDB_REQ_SET_LOCATION(search_req);
4657 ret = dsdb_request_add_controls(search_req,
4658 DSDB_SEARCH_SHOW_RECYCLED|
4659 DSDB_SEARCH_SHOW_DELETED|
4660 DSDB_SEARCH_SHOW_EXTENDED_DN);
4661 if (ret != LDB_SUCCESS) {
4665 return ldb_next_request(ar->module, search_req);
4669 handle renames that come in over DRS replication
4671 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4672 struct ldb_message *msg,
4673 struct ldb_request *parent,
4677 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4678 struct ldb_result *res;
4679 struct ldb_dn *conflict_dn;
4680 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4681 const struct ldb_val *omd_value;
4682 struct replPropertyMetaDataBlob omd, *rmd;
4683 enum ndr_err_code ndr_err;
4684 bool rename_incoming_record, rodc;
4685 struct replPropertyMetaData1 *rmd_name, *omd_name;
4686 struct ldb_dn *new_dn;
4689 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4690 ldb_dn_get_linearized(ar->search_msg->dn),
4691 ldb_dn_get_linearized(msg->dn)));
4694 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4695 DSDB_FLAG_NEXT_MODULE, ar->req);
4696 if (ret == LDB_SUCCESS) {
4697 talloc_free(tmp_ctx);
4702 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4703 talloc_free(tmp_ctx);
4704 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4705 ldb_dn_get_linearized(ar->search_msg->dn),
4706 ldb_dn_get_linearized(msg->dn),
4707 ldb_errstring(ldb_module_get_ctx(ar->module)));
4711 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4712 if (ret != LDB_SUCCESS) {
4713 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4714 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4715 ldb_errstring(ldb_module_get_ctx(ar->module)));
4716 return LDB_ERR_OPERATIONS_ERROR;
4719 * we have a conflict, and need to decide if we will keep the
4720 * new record or the old record
4723 conflict_dn = msg->dn;
4727 * We are on an RODC, or were a GC for this
4728 * partition, so we have to fail this until
4729 * someone who owns the partition sorts it
4732 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4733 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4734 " - We must fail the operation until a master for this partition resolves the conflict",
4735 ldb_dn_get_linearized(conflict_dn));
4740 * first we need the replPropertyMetaData attribute from the
4743 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4745 DSDB_FLAG_NEXT_MODULE |
4746 DSDB_SEARCH_SHOW_DELETED |
4747 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4748 if (ret != LDB_SUCCESS) {
4749 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4750 ldb_dn_get_linearized(conflict_dn)));
4754 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4755 if (omd_value == NULL) {
4756 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4757 ldb_dn_get_linearized(conflict_dn)));
4761 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4762 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4763 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4764 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4765 ldb_dn_get_linearized(conflict_dn)));
4769 rmd = ar->objs->objects[ar->index_current].meta_data;
4772 * we decide which is newer based on the RPMD on the name
4773 * attribute. See [MS-DRSR] ResolveNameConflict.
4775 * We expect omd_name to be present, as this is from a local
4776 * search, but while rmd_name should have been given to us by
4777 * the remote server, if it is missing we just prefer the
4779 * replmd_replPropertyMetaData1_new_should_be_taken()
4781 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4782 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4784 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4785 ldb_dn_get_linearized(conflict_dn)));
4790 * Should we preserve the current record, and so rename the
4791 * incoming record to be a conflict?
4793 rename_incoming_record =
4794 !replmd_replPropertyMetaData1_new_should_be_taken(
4795 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4796 omd_name, rmd_name);
4798 if (rename_incoming_record) {
4800 new_dn = replmd_conflict_dn(msg, msg->dn,
4801 &ar->objs->objects[ar->index_current].object_guid);
4802 if (new_dn == NULL) {
4803 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4804 "Failed to form conflict DN for %s\n",
4805 ldb_dn_get_linearized(msg->dn));
4807 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4810 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4811 DSDB_FLAG_NEXT_MODULE, ar->req);
4812 if (ret != LDB_SUCCESS) {
4813 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4814 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4815 ldb_dn_get_linearized(conflict_dn),
4816 ldb_dn_get_linearized(ar->search_msg->dn),
4817 ldb_dn_get_linearized(new_dn),
4818 ldb_errstring(ldb_module_get_ctx(ar->module)));
4819 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4827 /* we are renaming the existing record */
4829 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4830 if (GUID_all_zero(&guid)) {
4831 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4832 ldb_dn_get_linearized(conflict_dn)));
4836 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4837 if (new_dn == NULL) {
4838 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4839 ldb_dn_get_linearized(conflict_dn)));
4843 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4844 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4846 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4847 DSDB_FLAG_OWN_MODULE, ar->req);
4848 if (ret != LDB_SUCCESS) {
4849 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4850 ldb_dn_get_linearized(conflict_dn),
4851 ldb_dn_get_linearized(new_dn),
4852 ldb_errstring(ldb_module_get_ctx(ar->module))));
4857 * now we need to ensure that the rename is seen as an
4858 * originating update. We do that with a modify.
4860 ret = replmd_name_modify(ar, ar->req, new_dn);
4861 if (ret != LDB_SUCCESS) {
4865 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4866 ldb_dn_get_linearized(ar->search_msg->dn),
4867 ldb_dn_get_linearized(msg->dn)));
4870 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4871 DSDB_FLAG_NEXT_MODULE, ar->req);
4872 if (ret != LDB_SUCCESS) {
4873 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4874 ldb_dn_get_linearized(ar->search_msg->dn),
4875 ldb_dn_get_linearized(msg->dn),
4876 ldb_errstring(ldb_module_get_ctx(ar->module))));
4882 * On failure make the caller get the error
4883 * This means replication will stop with an error,
4884 * but there is not much else we can do. In the
4885 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4889 talloc_free(tmp_ctx);
4894 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4896 struct ldb_context *ldb;
4897 struct ldb_request *change_req;
4898 enum ndr_err_code ndr_err;
4899 struct ldb_message *msg;
4900 struct replPropertyMetaDataBlob *rmd;
4901 struct replPropertyMetaDataBlob omd;
4902 const struct ldb_val *omd_value;
4903 struct replPropertyMetaDataBlob nmd;
4904 struct ldb_val nmd_value;
4905 struct GUID remote_parent_guid;
4908 unsigned int removed_attrs = 0;
4910 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4911 bool isDeleted = false;
4912 bool local_isDeleted = false;
4913 bool remote_isDeleted = false;
4914 bool take_remote_isDeleted = false;
4915 bool sd_updated = false;
4916 bool renamed = false;
4917 bool is_schema_nc = false;
4919 const struct ldb_val *old_rdn, *new_rdn;
4920 struct replmd_private *replmd_private =
4921 talloc_get_type(ldb_module_get_private(ar->module),
4922 struct replmd_private);
4924 time_t t = time(NULL);
4925 unix_to_nt_time(&now, t);
4927 ldb = ldb_module_get_ctx(ar->module);
4928 msg = ar->objs->objects[ar->index_current].msg;
4930 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4932 rmd = ar->objs->objects[ar->index_current].meta_data;
4936 /* find existing meta data */
4937 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4939 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4940 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4941 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4942 nt_status = ndr_map_error2ntstatus(ndr_err);
4943 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4946 if (omd.version != 1) {
4947 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4952 struct GUID_txt_buf guid_txt;
4954 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4955 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
4958 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4960 ndr_print_struct_string(s,
4961 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4962 "existing replPropertyMetaData",
4964 ndr_print_struct_string(s,
4965 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4966 "incoming replPropertyMetaData",
4971 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4972 "isDeleted", false);
4973 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4974 "isDeleted", false);
4977 * Fill in the remote_parent_guid with the GUID or an all-zero
4980 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4981 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4983 remote_parent_guid = GUID_zero();
4987 * To ensure we follow a complex rename chain around, we have
4988 * to confirm that the DN is the same (mostly to confirm the
4989 * RDN) and the parentGUID is the same.
4991 * This ensures we keep things under the correct parent, which
4992 * replmd_replicated_handle_rename() will do.
4995 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
4996 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
5000 * handle renames, even just by case that come in over
5001 * DRS. Changes in the parent DN don't hit us here,
5002 * because the search for a parent will clean up those
5005 * We also have already filtered out the case where
5006 * the peer has an older name to what we have (see
5007 * replmd_replicated_apply_search_callback())
5009 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5012 if (ret != LDB_SUCCESS) {
5013 ldb_debug(ldb, LDB_DEBUG_FATAL,
5014 "replmd_replicated_request rename %s => %s failed - %s\n",
5015 ldb_dn_get_linearized(ar->search_msg->dn),
5016 ldb_dn_get_linearized(msg->dn),
5017 ldb_errstring(ldb));
5018 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5021 if (renamed == true) {
5023 * Set the callback to one that will fix up the name
5024 * metadata on the new conflict DN
5026 callback = replmd_op_name_modify_callback;
5031 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5032 nmd.ctr.ctr1.array = talloc_array(ar,
5033 struct replPropertyMetaData1,
5034 nmd.ctr.ctr1.count);
5035 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5037 /* first copy the old meta data */
5038 for (i=0; i < omd.ctr.ctr1.count; i++) {
5039 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5044 /* now merge in the new meta data */
5045 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5048 for (j=0; j < ni; j++) {
5051 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5055 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5056 ar->objs->dsdb_repl_flags,
5057 &nmd.ctr.ctr1.array[j],
5058 &rmd->ctr.ctr1.array[i]);
5060 /* replace the entry */
5061 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5062 if (ar->seq_num == 0) {
5063 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5064 if (ret != LDB_SUCCESS) {
5065 return replmd_replicated_request_error(ar, ret);
5068 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5069 switch (nmd.ctr.ctr1.array[j].attid) {
5070 case DRSUAPI_ATTID_ntSecurityDescriptor:
5073 case DRSUAPI_ATTID_isDeleted:
5074 take_remote_isDeleted = true;
5083 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5084 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5085 msg->elements[i-removed_attrs].name,
5086 ldb_dn_get_linearized(msg->dn),
5087 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5090 /* we don't want to apply this change so remove the attribute */
5091 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5098 if (found) continue;
5100 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5101 if (ar->seq_num == 0) {
5102 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5103 if (ret != LDB_SUCCESS) {
5104 return replmd_replicated_request_error(ar, ret);
5107 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5108 switch (nmd.ctr.ctr1.array[ni].attid) {
5109 case DRSUAPI_ATTID_ntSecurityDescriptor:
5112 case DRSUAPI_ATTID_isDeleted:
5113 take_remote_isDeleted = true;
5122 * finally correct the size of the meta_data array
5124 nmd.ctr.ctr1.count = ni;
5126 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5127 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5130 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5131 &nmd, ar, now, is_schema_nc);
5132 if (ret != LDB_SUCCESS) {
5133 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5134 return replmd_replicated_request_error(ar, ret);
5138 * sort the new meta data array
5140 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5141 if (ret != LDB_SUCCESS) {
5142 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5147 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5150 * This also controls SD propagation below
5152 if (take_remote_isDeleted) {
5153 isDeleted = remote_isDeleted;
5155 isDeleted = local_isDeleted;
5158 ar->isDeleted = isDeleted;
5161 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5163 if (msg->num_elements == 0) {
5164 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5167 return replmd_replicated_apply_isDeleted(ar);
5170 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5171 ar->index_current, msg->num_elements);
5177 if (sd_updated && !isDeleted) {
5178 ret = dsdb_module_schedule_sd_propagation(ar->module,
5179 ar->objs->partition_dn,
5181 if (ret != LDB_SUCCESS) {
5182 return ldb_operr(ldb);
5186 /* create the meta data value */
5187 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5188 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5189 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5190 nt_status = ndr_map_error2ntstatus(ndr_err);
5191 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5195 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5196 * and replPopertyMetaData attributes
5198 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5199 if (ret != LDB_SUCCESS) {
5200 return replmd_replicated_request_error(ar, ret);
5202 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5203 if (ret != LDB_SUCCESS) {
5204 return replmd_replicated_request_error(ar, ret);
5206 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5207 if (ret != LDB_SUCCESS) {
5208 return replmd_replicated_request_error(ar, ret);
5211 replmd_ldb_message_sort(msg, ar->schema);
5213 /* we want to replace the old values */
5214 for (i=0; i < msg->num_elements; i++) {
5215 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5216 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5217 if (msg->elements[i].num_values == 0) {
5218 ldb_asprintf_errstring(ldb, __location__
5219 ": objectClass removed on %s, aborting replication\n",
5220 ldb_dn_get_linearized(msg->dn));
5221 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5227 struct GUID_txt_buf guid_txt;
5229 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5230 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5231 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5236 ret = ldb_build_mod_req(&change_req,
5244 LDB_REQ_SET_LOCATION(change_req);
5245 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5247 /* current partition control needed by "repmd_op_callback" */
5248 ret = ldb_request_add_control(change_req,
5249 DSDB_CONTROL_CURRENT_PARTITION_OID,
5251 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5253 return ldb_next_request(ar->module, change_req);
5256 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5257 struct ldb_reply *ares)
5259 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5260 struct replmd_replicated_request);
5264 return ldb_module_done(ar->req, NULL, NULL,
5265 LDB_ERR_OPERATIONS_ERROR);
5267 if (ares->error != LDB_SUCCESS &&
5268 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5269 return ldb_module_done(ar->req, ares->controls,
5270 ares->response, ares->error);
5273 switch (ares->type) {
5274 case LDB_REPLY_ENTRY:
5275 ar->search_msg = talloc_steal(ar, ares->message);
5278 case LDB_REPLY_REFERRAL:
5279 /* we ignore referrals */
5282 case LDB_REPLY_DONE:
5284 struct replPropertyMetaData1 *md_remote;
5285 struct replPropertyMetaData1 *md_local;
5287 struct replPropertyMetaDataBlob omd;
5288 const struct ldb_val *omd_value;
5289 struct replPropertyMetaDataBlob *rmd;
5290 struct ldb_message *msg;
5292 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5293 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5296 * This is the ADD case, find the appropriate parent,
5297 * as this object doesn't exist locally:
5299 if (ar->search_msg == NULL) {
5300 ret = replmd_replicated_apply_search_for_parent(ar);
5301 if (ret != LDB_SUCCESS) {
5302 return ldb_module_done(ar->req, NULL, NULL, ret);
5309 * Otherwise, in the MERGE case, work out if we are
5310 * attempting a rename, and if so find the parent the
5311 * newly renamed object wants to belong under (which
5312 * may not be the parent in it's attached string DN
5314 rmd = ar->objs->objects[ar->index_current].meta_data;
5318 /* find existing meta data */
5319 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5321 enum ndr_err_code ndr_err;
5322 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5323 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5324 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5325 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5326 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5329 if (omd.version != 1) {
5330 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5334 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5336 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5337 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5338 && GUID_all_zero(&ar->local_parent_guid)) {
5339 DEBUG(0, ("Refusing to replicate new version of %s "
5340 "as local object has an all-zero parentGUID attribute, "
5341 "despite not being an NC root\n",
5342 ldb_dn_get_linearized(ar->search_msg->dn)));
5343 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5347 * now we need to check for double renames. We could have a
5348 * local rename pending which our replication partner hasn't
5349 * received yet. We choose which one wins by looking at the
5350 * attribute stamps on the two objects, the newer one wins.
5352 * This also simply applies the correct algorithms for
5353 * determining if a change was made to name at all, or
5354 * if the object has just been renamed under the same
5357 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5358 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5360 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5361 ldb_dn_get_linearized(ar->search_msg->dn)));
5362 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5366 * if there is no name attribute given then we have to assume the
5367 * object we've received has the older name
5369 if (replmd_replPropertyMetaData1_new_should_be_taken(
5370 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5371 md_local, md_remote)) {
5372 struct GUID_txt_buf p_guid_local;
5373 struct GUID_txt_buf p_guid_remote;
5374 msg = ar->objs->objects[ar->index_current].msg;
5376 /* Merge on the existing object, with rename */
5378 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5379 "as incoming object changing to %s under %s\n",
5380 ldb_dn_get_linearized(ar->search_msg->dn),
5381 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5382 ldb_dn_get_linearized(msg->dn),
5383 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5385 ret = replmd_replicated_apply_search_for_parent(ar);
5387 struct GUID_txt_buf p_guid_local;
5388 struct GUID_txt_buf p_guid_remote;
5389 msg = ar->objs->objects[ar->index_current].msg;
5392 * Merge on the existing object, force no
5393 * rename (code below just to explain why in
5397 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5398 ldb_dn_get_linearized(msg->dn)) == 0) {
5399 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5400 GUID_equal(&ar->local_parent_guid,
5401 ar->objs->objects[ar->index_current].parent_guid)
5403 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5404 "despite incoming object changing parent to %s\n",
5405 ldb_dn_get_linearized(ar->search_msg->dn),
5406 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5407 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5411 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5412 " and rejecting older rename to %s under %s\n",
5413 ldb_dn_get_linearized(ar->search_msg->dn),
5414 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5415 ldb_dn_get_linearized(msg->dn),
5416 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5420 * This assignment ensures that the strcmp()
5421 * and GUID_equal() calls in
5422 * replmd_replicated_apply_merge() avoids the
5425 ar->objs->objects[ar->index_current].parent_guid =
5426 &ar->local_parent_guid;
5428 msg->dn = ar->search_msg->dn;
5429 ret = replmd_replicated_apply_merge(ar);
5431 if (ret != LDB_SUCCESS) {
5432 return ldb_module_done(ar->req, NULL, NULL, ret);
5441 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5443 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5445 struct ldb_context *ldb;
5449 struct ldb_request *search_req;
5450 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
5451 "parentGUID", "instanceType",
5452 "replPropertyMetaData", "nTSecurityDescriptor",
5453 "isDeleted", NULL };
5454 struct GUID_txt_buf guid_str_buf;
5456 if (ar->index_current >= ar->objs->num_objects) {
5457 /* done with it, go to next stage */
5458 return replmd_replicated_uptodate_vector(ar);
5461 ldb = ldb_module_get_ctx(ar->module);
5462 ar->search_msg = NULL;
5463 ar->isDeleted = false;
5465 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5468 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5469 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5471 ret = ldb_build_search_req(&search_req,
5474 ar->objs->partition_dn,
5480 replmd_replicated_apply_search_callback,
5482 LDB_REQ_SET_LOCATION(search_req);
5484 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5486 if (ret != LDB_SUCCESS) {
5490 return ldb_next_request(ar->module, search_req);
5494 * This is essentially a wrapper for replmd_replicated_apply_next()
5496 * This is needed to ensure that both codepaths call this handler.
5498 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5500 struct ldb_dn *deleted_objects_dn;
5501 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5502 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5503 &deleted_objects_dn);
5504 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5506 * Do a delete here again, so that if there is
5507 * anything local that conflicts with this
5508 * object being deleted, it is removed. This
5509 * includes links. See MS-DRSR 4.1.10.6.9
5512 * If the object is already deleted, and there
5513 * is no more work required, it doesn't do
5517 /* This has been updated to point to the DN we eventually did the modify on */
5519 struct ldb_request *del_req;
5520 struct ldb_result *res;
5522 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5524 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5528 res = talloc_zero(tmp_ctx, struct ldb_result);
5530 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5531 talloc_free(tmp_ctx);
5535 /* Build a delete request, which hopefully will artually turn into nothing */
5536 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5540 ldb_modify_default_callback,
5542 LDB_REQ_SET_LOCATION(del_req);
5543 if (ret != LDB_SUCCESS) {
5544 talloc_free(tmp_ctx);
5549 * This is the guts of the call, call back
5550 * into our delete code, but setting the
5551 * re_delete flag so we delete anything that
5552 * shouldn't be there on a deleted or recycled
5555 ret = replmd_delete_internals(ar->module, del_req, true);
5556 if (ret == LDB_SUCCESS) {
5557 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5560 talloc_free(tmp_ctx);
5561 if (ret != LDB_SUCCESS) {
5566 ar->index_current++;
5567 return replmd_replicated_apply_next(ar);
5570 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5571 struct ldb_reply *ares)
5573 struct ldb_context *ldb;
5574 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5575 struct replmd_replicated_request);
5576 ldb = ldb_module_get_ctx(ar->module);
5579 return ldb_module_done(ar->req, NULL, NULL,
5580 LDB_ERR_OPERATIONS_ERROR);
5582 if (ares->error != LDB_SUCCESS) {
5583 return ldb_module_done(ar->req, ares->controls,
5584 ares->response, ares->error);
5587 if (ares->type != LDB_REPLY_DONE) {
5588 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5589 return ldb_module_done(ar->req, NULL, NULL,
5590 LDB_ERR_OPERATIONS_ERROR);
5595 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5598 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5600 struct ldb_context *ldb;
5601 struct ldb_request *change_req;
5602 enum ndr_err_code ndr_err;
5603 struct ldb_message *msg;
5604 struct replUpToDateVectorBlob ouv;
5605 const struct ldb_val *ouv_value;
5606 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5607 struct replUpToDateVectorBlob nuv;
5608 struct ldb_val nuv_value;
5609 struct ldb_message_element *nuv_el = NULL;
5610 struct ldb_message_element *orf_el = NULL;
5611 struct repsFromToBlob nrf;
5612 struct ldb_val *nrf_value = NULL;
5613 struct ldb_message_element *nrf_el = NULL;
5617 time_t t = time(NULL);
5620 uint32_t instanceType;
5622 ldb = ldb_module_get_ctx(ar->module);
5623 ruv = ar->objs->uptodateness_vector;
5629 unix_to_nt_time(&now, t);
5631 if (ar->search_msg == NULL) {
5632 /* this happens for a REPL_OBJ call where we are
5633 creating the target object by replicating it. The
5634 subdomain join code does this for the partition DN
5636 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5637 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5640 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5641 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5642 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5643 ldb_dn_get_linearized(ar->search_msg->dn)));
5644 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5648 * first create the new replUpToDateVector
5650 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5652 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5653 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5654 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5655 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5656 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5659 if (ouv.version != 2) {
5660 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5665 * the new uptodateness vector will at least
5666 * contain 1 entry, one for the source_dsa
5668 * plus optional values from our old vector and the one from the source_dsa
5670 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5671 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5672 nuv.ctr.ctr2.cursors = talloc_array(ar,
5673 struct drsuapi_DsReplicaCursor2,
5674 nuv.ctr.ctr2.count);
5675 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5677 /* first copy the old vector */
5678 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5679 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5683 /* merge in the source_dsa vector is available */
5684 for (i=0; (ruv && i < ruv->count); i++) {
5687 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5688 &ar->our_invocation_id)) {
5692 for (j=0; j < ni; j++) {
5693 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5694 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5700 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5701 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5706 if (found) continue;
5708 /* if it's not there yet, add it */
5709 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5714 * finally correct the size of the cursors array
5716 nuv.ctr.ctr2.count = ni;
5721 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5724 * create the change ldb_message
5726 msg = ldb_msg_new(ar);
5727 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5728 msg->dn = ar->search_msg->dn;
5730 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5731 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5732 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5733 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5734 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5736 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5737 if (ret != LDB_SUCCESS) {
5738 return replmd_replicated_request_error(ar, ret);
5740 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5743 * now create the new repsFrom value from the given repsFromTo1 structure
5747 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5748 nrf.ctr.ctr1.last_attempt = now;
5749 nrf.ctr.ctr1.last_success = now;
5750 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5753 * first see if we already have a repsFrom value for the current source dsa
5754 * if so we'll later replace this value
5756 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5758 for (i=0; i < orf_el->num_values; i++) {
5759 struct repsFromToBlob *trf;
5761 trf = talloc(ar, struct repsFromToBlob);
5762 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5764 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5765 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5766 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5767 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5768 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5771 if (trf->version != 1) {
5772 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5776 * we compare the source dsa objectGUID not the invocation_id
5777 * because we want only one repsFrom value per source dsa
5778 * and when the invocation_id of the source dsa has changed we don't need
5779 * the old repsFrom with the old invocation_id
5781 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5782 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5788 nrf_value = &orf_el->values[i];
5793 * copy over all old values to the new ldb_message
5795 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5796 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5801 * if we haven't found an old repsFrom value for the current source dsa
5802 * we'll add a new value
5805 struct ldb_val zero_value;
5806 ZERO_STRUCT(zero_value);
5807 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5808 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5810 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5813 /* we now fill the value which is already attached to ldb_message */
5814 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5816 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5817 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5818 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5819 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5823 * the ldb_message_element for the attribute, has all the old values and the new one
5824 * so we'll replace the whole attribute with all values
5826 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5828 if (CHECK_DEBUGLVL(4)) {
5829 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5830 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5834 /* prepare the ldb_modify() request */
5835 ret = ldb_build_mod_req(&change_req,
5841 replmd_replicated_uptodate_modify_callback,
5843 LDB_REQ_SET_LOCATION(change_req);
5844 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5846 return ldb_next_request(ar->module, change_req);
5849 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5850 struct ldb_reply *ares)
5852 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5853 struct replmd_replicated_request);
5857 return ldb_module_done(ar->req, NULL, NULL,
5858 LDB_ERR_OPERATIONS_ERROR);
5860 if (ares->error != LDB_SUCCESS &&
5861 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5862 return ldb_module_done(ar->req, ares->controls,
5863 ares->response, ares->error);
5866 switch (ares->type) {
5867 case LDB_REPLY_ENTRY:
5868 ar->search_msg = talloc_steal(ar, ares->message);
5871 case LDB_REPLY_REFERRAL:
5872 /* we ignore referrals */
5875 case LDB_REPLY_DONE:
5876 ret = replmd_replicated_uptodate_modify(ar);
5877 if (ret != LDB_SUCCESS) {
5878 return ldb_module_done(ar->req, NULL, NULL, ret);
5887 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5889 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5890 struct replmd_private *replmd_private =
5891 talloc_get_type_abort(ldb_module_get_private(ar->module),
5892 struct replmd_private);
5894 static const char *attrs[] = {
5895 "replUpToDateVector",
5900 struct ldb_request *search_req;
5902 ar->search_msg = NULL;
5905 * Let the caller know that we did an originating updates
5907 ar->objs->originating_updates = replmd_private->originating_updates;
5909 ret = ldb_build_search_req(&search_req,
5912 ar->objs->partition_dn,
5918 replmd_replicated_uptodate_search_callback,
5920 LDB_REQ_SET_LOCATION(search_req);
5921 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5923 return ldb_next_request(ar->module, search_req);
5928 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5930 struct ldb_context *ldb;
5931 struct dsdb_extended_replicated_objects *objs;
5932 struct replmd_replicated_request *ar;
5933 struct ldb_control **ctrls;
5936 struct replmd_private *replmd_private =
5937 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5939 ldb = ldb_module_get_ctx(module);
5941 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5943 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5945 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5946 return LDB_ERR_PROTOCOL_ERROR;
5949 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5950 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5951 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5952 return LDB_ERR_PROTOCOL_ERROR;
5955 ar = replmd_ctx_init(module, req);
5957 return LDB_ERR_OPERATIONS_ERROR;
5959 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5960 ar->apply_mode = true;
5962 ar->schema = dsdb_get_schema(ldb, ar);
5964 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5966 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5967 return LDB_ERR_CONSTRAINT_VIOLATION;
5970 ctrls = req->controls;
5972 if (req->controls) {
5973 req->controls = talloc_memdup(ar, req->controls,
5974 talloc_get_size(req->controls));
5975 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5978 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
5979 if (ret != LDB_SUCCESS) {
5983 /* If this change contained linked attributes in the body
5984 * (rather than in the links section) we need to update
5985 * backlinks in linked_attributes */
5986 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5987 if (ret != LDB_SUCCESS) {
5991 ar->controls = req->controls;
5992 req->controls = ctrls;
5994 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5996 /* save away the linked attributes for the end of the
5998 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5999 struct la_entry *la_entry;
6001 if (replmd_private->la_ctx == NULL) {
6002 replmd_private->la_ctx = talloc_new(replmd_private);
6004 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6005 if (la_entry == NULL) {
6007 return LDB_ERR_OPERATIONS_ERROR;
6009 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6010 if (la_entry->la == NULL) {
6011 talloc_free(la_entry);
6013 return LDB_ERR_OPERATIONS_ERROR;
6015 *la_entry->la = ar->objs->linked_attributes[i];
6017 /* we need to steal the non-scalars so they stay
6018 around until the end of the transaction */
6019 talloc_steal(la_entry->la, la_entry->la->identifier);
6020 talloc_steal(la_entry->la, la_entry->la->value.blob);
6022 DLIST_ADD(replmd_private->la_list, la_entry);
6025 return replmd_replicated_apply_next(ar);
6029 process one linked attribute structure
6031 static int replmd_process_linked_attribute(struct ldb_module *module,
6032 struct la_entry *la_entry,
6033 struct ldb_request *parent)
6035 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6036 struct ldb_context *ldb = ldb_module_get_ctx(module);
6037 struct ldb_message *msg;
6038 struct ldb_message *target_msg = NULL;
6039 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6040 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6042 const struct dsdb_attribute *attr;
6043 struct dsdb_dn *dsdb_dn;
6044 uint64_t seq_num = 0;
6045 struct ldb_message_element *old_el;
6047 time_t t = time(NULL);
6048 struct ldb_result *res;
6049 struct ldb_result *target_res;
6050 const char *attrs[4];
6051 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6052 struct parsed_dn *pdn_list, *pdn;
6053 struct GUID guid = GUID_zero();
6055 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6056 const struct GUID *our_invocation_id;
6058 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6059 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6062 linked_attributes[0]:
6063 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6065 identifier: struct drsuapi_DsReplicaObjectIdentifier
6066 __ndr_size : 0x0000003a (58)
6067 __ndr_size_sid : 0x00000000 (0)
6068 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6070 __ndr_size_dn : 0x00000000 (0)
6072 attid : DRSUAPI_ATTID_member (0x1F)
6073 value: struct drsuapi_DsAttributeValue
6074 __ndr_size : 0x0000007e (126)
6076 blob : DATA_BLOB length=126
6077 flags : 0x00000001 (1)
6078 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6079 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6080 meta_data: struct drsuapi_DsReplicaMetaData
6081 version : 0x00000015 (21)
6082 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6083 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6084 originating_usn : 0x000000000001e19c (123292)
6086 (for cases where the link is to a normal DN)
6087 &target: struct drsuapi_DsReplicaObjectIdentifier3
6088 __ndr_size : 0x0000007e (126)
6089 __ndr_size_sid : 0x0000001c (28)
6090 guid : 7639e594-db75-4086-b0d4-67890ae46031
6091 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6092 __ndr_size_dn : 0x00000022 (34)
6093 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6096 /* find the attribute being modified */
6097 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6099 struct GUID_txt_buf guid_str;
6100 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6102 GUID_buf_string(&la->identifier->guid,
6104 talloc_free(tmp_ctx);
6105 return LDB_ERR_OPERATIONS_ERROR;
6108 attrs[0] = attr->lDAPDisplayName;
6109 attrs[1] = "isDeleted";
6110 attrs[2] = "isRecycled";
6113 /* get the existing message from the db for the object with
6114 this GUID, returning attribute being modified. We will then
6115 use this msg as the basis for a modify call */
6116 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6117 DSDB_FLAG_NEXT_MODULE |
6118 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6119 DSDB_SEARCH_SHOW_RECYCLED |
6120 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6121 DSDB_SEARCH_REVEAL_INTERNALS,
6123 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6124 if (ret != LDB_SUCCESS) {
6125 talloc_free(tmp_ctx);
6128 if (res->count != 1) {
6129 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6130 GUID_string(tmp_ctx, &la->identifier->guid));
6131 talloc_free(tmp_ctx);
6132 return LDB_ERR_NO_SUCH_OBJECT;
6137 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6138 * ProcessLinkValue, because link updates are not applied to
6139 * recycled and tombstone objects. We don't have to delete
6140 * any existing link, that should have happened when the
6141 * object deletion was replicated or initiated.
6144 replmd_deletion_state(module, msg, &deletion_state, NULL);
6146 if (deletion_state >= OBJECT_RECYCLED) {
6147 talloc_free(tmp_ctx);
6151 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6152 if (old_el == NULL) {
6153 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6154 if (ret != LDB_SUCCESS) {
6155 ldb_module_oom(module);
6156 talloc_free(tmp_ctx);
6157 return LDB_ERR_OPERATIONS_ERROR;
6160 old_el->flags = LDB_FLAG_MOD_REPLACE;
6163 /* parse the existing links */
6164 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
6165 if (ret != LDB_SUCCESS) {
6166 talloc_free(tmp_ctx);
6170 /* get our invocationId */
6171 our_invocation_id = samdb_ntds_invocation_id(ldb);
6172 if (!our_invocation_id) {
6173 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
6174 talloc_free(tmp_ctx);
6175 return LDB_ERR_OPERATIONS_ERROR;
6178 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
6179 if (ret != LDB_SUCCESS) {
6180 talloc_free(tmp_ctx);
6184 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6185 if (!W_ERROR_IS_OK(status)) {
6186 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6187 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6188 talloc_free(tmp_ctx);
6189 return LDB_ERR_OPERATIONS_ERROR;
6192 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6193 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6195 * This strange behaviour (allowing a NULL/missing
6196 * GUID) originally comes from:
6198 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6199 * Author: Andrew Tridgell <tridge@samba.org>
6200 * Date: Mon Dec 21 21:21:55 2009 +1100
6202 * s4-drs: cope better with NULL GUIDS from DRS
6204 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6205 * need to match by DN if possible when seeing if we should update an
6208 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6211 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6212 dsdb_dn->dn, attrs2,
6213 DSDB_FLAG_NEXT_MODULE |
6214 DSDB_SEARCH_SHOW_RECYCLED |
6215 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6216 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6218 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6219 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6221 ldb_dn_get_linearized(dsdb_dn->dn),
6222 ldb_dn_get_linearized(msg->dn));
6223 talloc_free(tmp_ctx);
6224 return LDB_ERR_OPERATIONS_ERROR;
6226 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6227 NULL, LDB_SCOPE_SUBTREE,
6229 DSDB_FLAG_NEXT_MODULE |
6230 DSDB_SEARCH_SHOW_RECYCLED |
6231 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6232 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6235 GUID_string(tmp_ctx, &guid));
6238 if (ret != LDB_SUCCESS) {
6239 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6240 GUID_string(tmp_ctx, &guid),
6241 ldb_errstring(ldb_module_get_ctx(module)));
6242 talloc_free(tmp_ctx);
6246 if (target_res->count == 0) {
6247 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6248 GUID_string(tmp_ctx, &guid),
6249 ldb_dn_get_linearized(dsdb_dn->dn)));
6250 } else if (target_res->count != 1) {
6251 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6252 GUID_string(tmp_ctx, &guid));
6253 talloc_free(tmp_ctx);
6254 return LDB_ERR_OPERATIONS_ERROR;
6256 target_msg = target_res->msgs[0];
6257 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6261 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6262 * ProcessLinkValue, because link updates are not applied to
6263 * recycled and tombstone objects. We don't have to delete
6264 * any existing link, that should have happened when the
6265 * object deletion was replicated or initiated.
6267 replmd_deletion_state(module, target_msg,
6268 &target_deletion_state, NULL);
6270 if (target_deletion_state >= OBJECT_RECYCLED) {
6271 talloc_free(tmp_ctx);
6275 /* see if this link already exists */
6276 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
6278 /* see if this update is newer than what we have already */
6279 struct GUID invocation_id = GUID_zero();
6280 uint32_t version = 0;
6281 uint32_t originating_usn = 0;
6282 NTTIME change_time = 0;
6283 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6285 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6286 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6287 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6288 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6290 if (!replmd_update_is_newer(&invocation_id,
6291 &la->meta_data.originating_invocation_id,
6293 la->meta_data.version,
6295 la->meta_data.originating_change_time)) {
6296 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6297 old_el->name, ldb_dn_get_linearized(msg->dn),
6298 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6299 talloc_free(tmp_ctx);
6303 /* get a seq_num for this change */
6304 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6305 if (ret != LDB_SUCCESS) {
6306 talloc_free(tmp_ctx);
6310 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6311 /* remove the existing backlink */
6312 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, true);
6313 if (ret != LDB_SUCCESS) {
6314 talloc_free(tmp_ctx);
6319 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6320 &la->meta_data.originating_invocation_id,
6321 la->meta_data.originating_usn, seq_num,
6322 la->meta_data.originating_change_time,
6323 la->meta_data.version,
6325 if (ret != LDB_SUCCESS) {
6326 talloc_free(tmp_ctx);
6331 /* add the new backlink */
6332 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, true);
6333 if (ret != LDB_SUCCESS) {
6334 talloc_free(tmp_ctx);
6339 /* get a seq_num for this change */
6340 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6341 if (ret != LDB_SUCCESS) {
6342 talloc_free(tmp_ctx);
6346 old_el->values = talloc_realloc(msg->elements, old_el->values,
6347 struct ldb_val, old_el->num_values+1);
6348 if (!old_el->values) {
6349 ldb_module_oom(module);
6350 return LDB_ERR_OPERATIONS_ERROR;
6352 old_el->num_values++;
6354 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
6355 &la->meta_data.originating_invocation_id,
6356 la->meta_data.originating_usn, seq_num,
6357 la->meta_data.originating_change_time,
6358 la->meta_data.version,
6360 if (ret != LDB_SUCCESS) {
6361 talloc_free(tmp_ctx);
6366 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6368 if (ret != LDB_SUCCESS) {
6369 talloc_free(tmp_ctx);
6375 /* we only change whenChanged and uSNChanged if the seq_num
6377 ret = add_time_element(msg, "whenChanged", t);
6378 if (ret != LDB_SUCCESS) {
6379 talloc_free(tmp_ctx);
6384 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6385 if (ret != LDB_SUCCESS) {
6386 talloc_free(tmp_ctx);
6391 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6392 if (old_el == NULL) {
6393 talloc_free(tmp_ctx);
6394 return ldb_operr(ldb);
6397 ret = dsdb_check_single_valued_link(attr, old_el);
6398 if (ret != LDB_SUCCESS) {
6399 talloc_free(tmp_ctx);
6403 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6405 ret = linked_attr_modify(module, msg, parent);
6406 if (ret != LDB_SUCCESS) {
6407 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6409 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6410 talloc_free(tmp_ctx);
6414 talloc_free(tmp_ctx);
6419 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6421 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6422 return replmd_extended_replicated_objects(module, req);
6425 return ldb_next_request(module, req);
6430 we hook into the transaction operations to allow us to
6431 perform the linked attribute updates at the end of the whole
6432 transaction. This allows a forward linked attribute to be created
6433 before the object is created. During a vampire, w2k8 sends us linked
6434 attributes before the objects they are part of.
6436 static int replmd_start_transaction(struct ldb_module *module)
6438 /* create our private structure for this transaction */
6439 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6440 struct replmd_private);
6441 replmd_txn_cleanup(replmd_private);
6443 /* free any leftover mod_usn records from cancelled
6445 while (replmd_private->ncs) {
6446 struct nc_entry *e = replmd_private->ncs;
6447 DLIST_REMOVE(replmd_private->ncs, e);
6451 replmd_private->originating_updates = false;
6453 return ldb_next_start_trans(module);
6457 on prepare commit we loop over our queued la_context structures and
6460 static int replmd_prepare_commit(struct ldb_module *module)
6462 struct replmd_private *replmd_private =
6463 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6464 struct la_entry *la, *prev;
6465 struct la_backlink *bl;
6468 /* walk the list backwards, to do the first entry first, as we
6469 * added the entries with DLIST_ADD() which puts them at the
6470 * start of the list */
6471 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6472 prev = DLIST_PREV(la);
6473 DLIST_REMOVE(replmd_private->la_list, la);
6474 ret = replmd_process_linked_attribute(module, la, NULL);
6475 if (ret != LDB_SUCCESS) {
6476 replmd_txn_cleanup(replmd_private);
6481 /* process our backlink list, creating and deleting backlinks
6483 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6484 ret = replmd_process_backlink(module, bl, NULL);
6485 if (ret != LDB_SUCCESS) {
6486 replmd_txn_cleanup(replmd_private);
6491 replmd_txn_cleanup(replmd_private);
6493 /* possibly change @REPLCHANGED */
6494 ret = replmd_notify_store(module, NULL);
6495 if (ret != LDB_SUCCESS) {
6499 return ldb_next_prepare_commit(module);
6502 static int replmd_del_transaction(struct ldb_module *module)
6504 struct replmd_private *replmd_private =
6505 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6506 replmd_txn_cleanup(replmd_private);
6508 return ldb_next_del_trans(module);
6512 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6513 .name = "repl_meta_data",
6514 .init_context = replmd_init,
6516 .modify = replmd_modify,
6517 .rename = replmd_rename,
6518 .del = replmd_delete,
6519 .extended = replmd_extended,
6520 .start_transaction = replmd_start_transaction,
6521 .prepare_commit = replmd_prepare_commit,
6522 .del_transaction = replmd_del_transaction,
6525 int ldb_repl_meta_data_module_init(const char *version)
6527 LDB_MODULE_CHECK_VERSION(version);
6528 return ldb_register_module(&ldb_repl_meta_data_module_ops);