4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 Copyright (C) Andrew Tridgell 2005-2009
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
54 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
55 * Deleted Objects Container
57 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
59 struct replmd_private {
61 struct la_entry *la_list;
63 struct la_backlink *la_backlinks;
65 struct nc_entry *prev, *next;
68 uint64_t mod_usn_urgent;
70 struct ldb_dn *schema_dn;
71 bool originating_updates;
75 struct la_entry *next, *prev;
76 struct drsuapi_DsReplicaLinkedAttribute *la;
79 struct replmd_replicated_request {
80 struct ldb_module *module;
81 struct ldb_request *req;
83 const struct dsdb_schema *schema;
84 struct GUID our_invocation_id;
86 /* the controls we pass down */
87 struct ldb_control **controls;
89 /* details for the mode where we apply a bunch of inbound replication meessages */
91 uint32_t index_current;
92 struct dsdb_extended_replicated_objects *objs;
94 struct ldb_message *search_msg;
95 struct GUID local_parent_guid;
103 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
104 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
106 enum urgent_situation {
107 REPL_URGENT_ON_CREATE = 1,
108 REPL_URGENT_ON_UPDATE = 2,
109 REPL_URGENT_ON_DELETE = 4
112 enum deletion_state {
113 OBJECT_NOT_DELETED=1,
120 static void replmd_deletion_state(struct ldb_module *module,
121 const struct ldb_message *msg,
122 enum deletion_state *current_state,
123 enum deletion_state *next_state)
126 bool enabled = false;
129 *current_state = OBJECT_REMOVED;
130 if (next_state != NULL) {
131 *next_state = OBJECT_REMOVED;
136 ret = dsdb_recyclebin_enabled(module, &enabled);
137 if (ret != LDB_SUCCESS) {
141 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
143 *current_state = OBJECT_TOMBSTONE;
144 if (next_state != NULL) {
145 *next_state = OBJECT_REMOVED;
150 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
151 *current_state = OBJECT_RECYCLED;
152 if (next_state != NULL) {
153 *next_state = OBJECT_REMOVED;
158 *current_state = OBJECT_DELETED;
159 if (next_state != NULL) {
160 *next_state = OBJECT_RECYCLED;
165 *current_state = OBJECT_NOT_DELETED;
166 if (next_state == NULL) {
171 *next_state = OBJECT_DELETED;
173 *next_state = OBJECT_TOMBSTONE;
177 static const struct {
178 const char *update_name;
179 enum urgent_situation repl_situation;
180 } urgent_objects[] = {
181 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
182 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
183 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
184 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
185 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
186 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
190 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
191 static const char *urgent_attrs[] = {
194 "userAccountControl",
199 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
200 enum urgent_situation situation)
203 for (i=0; urgent_objects[i].update_name; i++) {
205 if ((situation & urgent_objects[i].repl_situation) == 0) {
209 for (j=0; j<objectclass_el->num_values; j++) {
210 const struct ldb_val *v = &objectclass_el->values[j];
211 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
219 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
221 if (ldb_attr_in_list(urgent_attrs, el->name)) {
228 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
231 initialise the module
232 allocate the private structure and build the list
233 of partition DNs for use by replmd_notify()
235 static int replmd_init(struct ldb_module *module)
237 struct replmd_private *replmd_private;
238 struct ldb_context *ldb = ldb_module_get_ctx(module);
240 replmd_private = talloc_zero(module, struct replmd_private);
241 if (replmd_private == NULL) {
243 return LDB_ERR_OPERATIONS_ERROR;
245 ldb_module_set_private(module, replmd_private);
247 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
249 return ldb_next_init(module);
253 cleanup our per-transaction contexts
255 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
257 talloc_free(replmd_private->la_ctx);
258 replmd_private->la_list = NULL;
259 replmd_private->la_ctx = NULL;
261 talloc_free(replmd_private->bl_ctx);
262 replmd_private->la_backlinks = NULL;
263 replmd_private->bl_ctx = NULL;
268 struct la_backlink *next, *prev;
269 const char *attr_name;
270 struct GUID forward_guid, target_guid;
275 a ldb_modify request operating on modules below the
278 static int linked_attr_modify(struct ldb_module *module,
279 const struct ldb_message *message,
280 struct ldb_request *parent)
282 struct ldb_request *mod_req;
284 struct ldb_context *ldb = ldb_module_get_ctx(module);
285 TALLOC_CTX *tmp_ctx = talloc_new(module);
286 struct ldb_result *res;
288 res = talloc_zero(tmp_ctx, struct ldb_result);
290 talloc_free(tmp_ctx);
291 return ldb_oom(ldb_module_get_ctx(module));
294 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
298 ldb_modify_default_callback,
300 LDB_REQ_SET_LOCATION(mod_req);
301 if (ret != LDB_SUCCESS) {
302 talloc_free(tmp_ctx);
306 ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
308 if (ret != LDB_SUCCESS) {
312 /* Run the new request */
313 ret = ldb_next_request(module, mod_req);
315 if (ret == LDB_SUCCESS) {
316 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
319 talloc_free(tmp_ctx);
324 process a backlinks we accumulated during a transaction, adding and
325 deleting the backlinks from the target objects
327 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
329 struct ldb_dn *target_dn, *source_dn;
331 struct ldb_context *ldb = ldb_module_get_ctx(module);
332 struct ldb_message *msg;
333 TALLOC_CTX *tmp_ctx = talloc_new(bl);
339 - construct ldb_message
340 - either an add or a delete
342 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
343 if (ret != LDB_SUCCESS) {
344 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
345 GUID_string(bl, &bl->target_guid)));
349 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
350 if (ret != LDB_SUCCESS) {
351 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
352 GUID_string(bl, &bl->forward_guid));
353 talloc_free(tmp_ctx);
357 msg = ldb_msg_new(tmp_ctx);
359 ldb_module_oom(module);
360 talloc_free(tmp_ctx);
361 return LDB_ERR_OPERATIONS_ERROR;
364 /* construct a ldb_message for adding/deleting the backlink */
366 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
368 ldb_module_oom(module);
369 talloc_free(tmp_ctx);
370 return LDB_ERR_OPERATIONS_ERROR;
372 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
373 if (ret != LDB_SUCCESS) {
374 talloc_free(tmp_ctx);
377 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
379 /* a backlink should never be single valued. Unfortunately the
380 exchange schema has a attribute
381 msExchBridgeheadedLocalConnectorsDNBL which is single
382 valued and a backlink. We need to cope with that by
383 ignoring the single value flag */
384 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
386 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
387 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
388 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
389 cope with possible corruption where the backlink has
390 already been removed */
391 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
392 ldb_dn_get_linearized(target_dn),
393 ldb_dn_get_linearized(source_dn),
394 ldb_errstring(ldb)));
396 } else if (ret != LDB_SUCCESS) {
397 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
398 bl->active?"add":"remove",
399 ldb_dn_get_linearized(source_dn),
400 ldb_dn_get_linearized(target_dn),
402 talloc_free(tmp_ctx);
405 talloc_free(tmp_ctx);
410 add a backlink to the list of backlinks to add/delete in the prepare
413 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
414 struct GUID *forward_guid, struct GUID *target_guid,
415 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
417 const struct dsdb_attribute *target_attr;
418 struct la_backlink *bl;
419 struct replmd_private *replmd_private =
420 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
422 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
425 * windows 2003 has a broken schema where the
426 * definition of msDS-IsDomainFor is missing (which is
427 * supposed to be the backlink of the
428 * msDS-HasDomainNCs attribute
433 /* see if its already in the list */
434 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
435 if (GUID_equal(forward_guid, &bl->forward_guid) &&
436 GUID_equal(target_guid, &bl->target_guid) &&
437 (target_attr->lDAPDisplayName == bl->attr_name ||
438 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
444 /* we found an existing one */
445 if (bl->active == active) {
448 DLIST_REMOVE(replmd_private->la_backlinks, bl);
453 if (replmd_private->bl_ctx == NULL) {
454 replmd_private->bl_ctx = talloc_new(replmd_private);
455 if (replmd_private->bl_ctx == NULL) {
456 ldb_module_oom(module);
457 return LDB_ERR_OPERATIONS_ERROR;
462 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
464 ldb_module_oom(module);
465 return LDB_ERR_OPERATIONS_ERROR;
468 /* Ensure the schema does not go away before the bl->attr_name is used */
469 if (!talloc_reference(bl, schema)) {
471 ldb_module_oom(module);
472 return LDB_ERR_OPERATIONS_ERROR;
475 bl->attr_name = target_attr->lDAPDisplayName;
476 bl->forward_guid = *forward_guid;
477 bl->target_guid = *target_guid;
480 /* the caller may ask for this backlink to be processed
483 int ret = replmd_process_backlink(module, bl, NULL);
488 DLIST_ADD(replmd_private->la_backlinks, bl);
495 * Callback for most write operations in this module:
497 * notify the repl task that a object has changed. The notifies are
498 * gathered up in the replmd_private structure then written to the
499 * @REPLCHANGED object in each partition during the prepare_commit
501 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
504 struct replmd_replicated_request *ac =
505 talloc_get_type_abort(req->context, struct replmd_replicated_request);
506 struct replmd_private *replmd_private =
507 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
508 struct nc_entry *modified_partition;
509 struct ldb_control *partition_ctrl;
510 const struct dsdb_control_current_partition *partition;
512 struct ldb_control **controls;
514 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
516 controls = ares->controls;
517 if (ldb_request_get_control(ac->req,
518 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
520 * Remove the current partition control from what we pass up
521 * the chain if it hasn't been requested manually.
523 controls = ldb_controls_except_specified(ares->controls, ares,
527 if (ares->error != LDB_SUCCESS) {
528 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
529 return ldb_module_done(ac->req, controls,
530 ares->response, ares->error);
533 if (ares->type != LDB_REPLY_DONE) {
534 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
535 return ldb_module_done(ac->req, NULL,
536 NULL, LDB_ERR_OPERATIONS_ERROR);
539 if (!partition_ctrl) {
540 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
541 return ldb_module_done(ac->req, NULL,
542 NULL, LDB_ERR_OPERATIONS_ERROR);
545 partition = talloc_get_type_abort(partition_ctrl->data,
546 struct dsdb_control_current_partition);
548 if (ac->seq_num > 0) {
549 for (modified_partition = replmd_private->ncs; modified_partition;
550 modified_partition = modified_partition->next) {
551 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
556 if (modified_partition == NULL) {
557 modified_partition = talloc_zero(replmd_private, struct nc_entry);
558 if (!modified_partition) {
559 ldb_oom(ldb_module_get_ctx(ac->module));
560 return ldb_module_done(ac->req, NULL,
561 NULL, LDB_ERR_OPERATIONS_ERROR);
563 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
564 if (!modified_partition->dn) {
565 ldb_oom(ldb_module_get_ctx(ac->module));
566 return ldb_module_done(ac->req, NULL,
567 NULL, LDB_ERR_OPERATIONS_ERROR);
569 DLIST_ADD(replmd_private->ncs, modified_partition);
572 if (ac->seq_num > modified_partition->mod_usn) {
573 modified_partition->mod_usn = ac->seq_num;
575 modified_partition->mod_usn_urgent = ac->seq_num;
578 if (!ac->apply_mode) {
579 replmd_private->originating_updates = true;
583 if (ac->apply_mode) {
584 ret = replmd_replicated_apply_isDeleted(ac);
585 if (ret != LDB_SUCCESS) {
586 return ldb_module_done(ac->req, NULL, NULL, ret);
590 /* free the partition control container here, for the
591 * common path. Other cases will have it cleaned up
592 * eventually with the ares */
593 talloc_free(partition_ctrl);
594 return ldb_module_done(ac->req, controls,
595 ares->response, LDB_SUCCESS);
601 * update a @REPLCHANGED record in each partition if there have been
602 * any writes of replicated data in the partition
604 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
606 struct replmd_private *replmd_private =
607 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
609 while (replmd_private->ncs) {
611 struct nc_entry *modified_partition = replmd_private->ncs;
613 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
614 modified_partition->mod_usn,
615 modified_partition->mod_usn_urgent, parent);
616 if (ret != LDB_SUCCESS) {
617 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
618 ldb_dn_get_linearized(modified_partition->dn)));
622 if (ldb_dn_compare(modified_partition->dn,
623 replmd_private->schema_dn) == 0) {
624 struct ldb_result *ext_res;
625 ret = dsdb_module_extended(module,
626 replmd_private->schema_dn,
628 DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
630 DSDB_FLAG_NEXT_MODULE,
632 if (ret != LDB_SUCCESS) {
635 talloc_free(ext_res);
638 DLIST_REMOVE(replmd_private->ncs, modified_partition);
639 talloc_free(modified_partition);
647 created a replmd_replicated_request context
649 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
650 struct ldb_request *req)
652 struct ldb_context *ldb;
653 struct replmd_replicated_request *ac;
654 const struct GUID *our_invocation_id;
656 ldb = ldb_module_get_ctx(module);
658 ac = talloc_zero(req, struct replmd_replicated_request);
667 ac->schema = dsdb_get_schema(ldb, ac);
669 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
670 "replmd_modify: no dsdb_schema loaded");
671 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
676 /* get our invocationId */
677 our_invocation_id = samdb_ntds_invocation_id(ldb);
678 if (!our_invocation_id) {
679 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
680 "replmd_add: unable to find invocationId\n");
684 ac->our_invocation_id = *our_invocation_id;
690 add a time element to a record
692 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
694 struct ldb_message_element *el;
698 if (ldb_msg_find_element(msg, attr) != NULL) {
702 s = ldb_timestring(msg, t);
704 return LDB_ERR_OPERATIONS_ERROR;
707 ret = ldb_msg_add_string(msg, attr, s);
708 if (ret != LDB_SUCCESS) {
712 el = ldb_msg_find_element(msg, attr);
713 /* always set as replace. This works because on add ops, the flag
715 el->flags = LDB_FLAG_MOD_REPLACE;
721 add a uint64_t element to a record
723 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
724 const char *attr, uint64_t v)
726 struct ldb_message_element *el;
729 if (ldb_msg_find_element(msg, attr) != NULL) {
733 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
734 if (ret != LDB_SUCCESS) {
738 el = ldb_msg_find_element(msg, attr);
739 /* always set as replace. This works because on add ops, the flag
741 el->flags = LDB_FLAG_MOD_REPLACE;
746 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
747 const struct replPropertyMetaData1 *m2,
748 const uint32_t *rdn_attid)
751 * This assignment seems inoccous, but it is critical for the
752 * system, as we need to do the comparisons as a unsigned
753 * quantity, not signed (enums are signed integers)
755 uint32_t attid_1 = m1->attid;
756 uint32_t attid_2 = m2->attid;
758 if (attid_1 == attid_2) {
763 * See above regarding this being an unsigned comparison.
764 * Otherwise when the high bit is set on non-standard
765 * attributes, they would end up first, before objectClass
768 return attid_1 > attid_2 ? 1 : -1;
771 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
772 struct replPropertyMetaDataCtr1 *ctr1,
775 if (ctr1->count == 0) {
776 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
777 "No elements found in replPropertyMetaData for %s!\n",
778 ldb_dn_get_linearized(dn));
779 return LDB_ERR_CONSTRAINT_VIOLATION;
782 /* the objectClass attribute is value 0x00000000, so must be first */
783 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
784 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
785 "No objectClass found in replPropertyMetaData for %s!\n",
786 ldb_dn_get_linearized(dn));
787 return LDB_ERR_OBJECT_CLASS_VIOLATION;
793 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
794 struct replPropertyMetaDataCtr1 *ctr1,
797 /* Note this is O(n^2) for the almost-sorted case, which this is */
798 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
799 replmd_replPropertyMetaData1_attid_sort);
800 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
803 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
804 const struct ldb_message_element *e2,
805 const struct dsdb_schema *schema)
807 const struct dsdb_attribute *a1;
808 const struct dsdb_attribute *a2;
811 * TODO: make this faster by caching the dsdb_attribute pointer
812 * on the ldb_messag_element
815 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
816 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
819 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
823 return strcasecmp(e1->name, e2->name);
825 if (a1->attributeID_id == a2->attributeID_id) {
828 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
831 static void replmd_ldb_message_sort(struct ldb_message *msg,
832 const struct dsdb_schema *schema)
834 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
837 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
838 const struct GUID *invocation_id, uint64_t seq_num,
839 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
843 fix up linked attributes in replmd_add.
844 This involves setting up the right meta-data in extended DN
845 components, and creating backlinks to the object
847 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
848 uint64_t seq_num, const struct GUID *invocationId, NTTIME now,
849 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
852 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
853 struct ldb_context *ldb = ldb_module_get_ctx(module);
855 /* We will take a reference to the schema in replmd_add_backlink */
856 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
858 for (i=0; i<el->num_values; i++) {
859 struct ldb_val *v = &el->values[i];
860 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
861 struct GUID target_guid;
865 if (dsdb_dn == NULL) {
866 talloc_free(tmp_ctx);
867 return LDB_ERR_INVALID_DN_SYNTAX;
870 /* note that the DN already has the extended
871 components from the extended_dn_store module */
872 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
873 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
874 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
875 if (ret != LDB_SUCCESS) {
876 talloc_free(tmp_ctx);
879 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
880 if (ret != LDB_SUCCESS) {
881 talloc_free(tmp_ctx);
886 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
887 seq_num, seq_num, now, 0, false);
888 if (ret != LDB_SUCCESS) {
889 talloc_free(tmp_ctx);
893 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
894 if (ret != LDB_SUCCESS) {
895 talloc_free(tmp_ctx);
900 talloc_free(tmp_ctx);
906 intercept add requests
908 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
910 struct ldb_context *ldb;
911 struct ldb_control *control;
912 struct replmd_replicated_request *ac;
913 enum ndr_err_code ndr_err;
914 struct ldb_request *down_req;
915 struct ldb_message *msg;
916 const DATA_BLOB *guid_blob;
918 struct replPropertyMetaDataBlob nmd;
919 struct ldb_val nmd_value;
922 * The use of a time_t here seems odd, but as the NTTIME
923 * elements are actually declared as NTTIME_1sec in the IDL,
924 * getting a higher resolution timestamp is not required.
926 time_t t = time(NULL);
931 unsigned int functional_level;
933 bool allow_add_guid = false;
934 bool remove_current_guid = false;
935 bool is_urgent = false;
936 bool is_schema_nc = false;
937 struct ldb_message_element *objectclass_el;
938 struct replmd_private *replmd_private =
939 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
941 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
942 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
944 allow_add_guid = true;
947 /* do not manipulate our control entries */
948 if (ldb_dn_is_special(req->op.add.message->dn)) {
949 return ldb_next_request(module, req);
952 ldb = ldb_module_get_ctx(module);
954 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
956 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
957 if (guid_blob != NULL) {
958 if (!allow_add_guid) {
959 ldb_set_errstring(ldb,
960 "replmd_add: it's not allowed to add an object with objectGUID!");
961 return LDB_ERR_UNWILLING_TO_PERFORM;
963 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
964 if (!NT_STATUS_IS_OK(status)) {
965 ldb_set_errstring(ldb,
966 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
967 return LDB_ERR_UNWILLING_TO_PERFORM;
969 /* we remove this attribute as it can be a string and
970 * will not be treated correctly and then we will re-add
971 * it later on in the good format */
972 remove_current_guid = true;
976 guid = GUID_random();
979 ac = replmd_ctx_init(module, req);
981 return ldb_module_oom(module);
984 functional_level = dsdb_functional_level(ldb);
986 /* Get a sequence number from the backend */
987 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
988 if (ret != LDB_SUCCESS) {
993 /* we have to copy the message as the caller might have it as a const */
994 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
998 return LDB_ERR_OPERATIONS_ERROR;
1001 /* generated times */
1002 unix_to_nt_time(&now, t);
1003 time_str = ldb_timestring(msg, t);
1007 return LDB_ERR_OPERATIONS_ERROR;
1009 if (remove_current_guid) {
1010 ldb_msg_remove_attr(msg,"objectGUID");
1014 * remove autogenerated attributes
1016 ldb_msg_remove_attr(msg, "whenCreated");
1017 ldb_msg_remove_attr(msg, "whenChanged");
1018 ldb_msg_remove_attr(msg, "uSNCreated");
1019 ldb_msg_remove_attr(msg, "uSNChanged");
1020 ldb_msg_remove_attr(msg, "replPropertyMetaData");
1023 * readd replicated attributes
1025 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1026 if (ret != LDB_SUCCESS) {
1032 /* build the replication meta_data */
1035 nmd.ctr.ctr1.count = msg->num_elements;
1036 nmd.ctr.ctr1.array = talloc_array(msg,
1037 struct replPropertyMetaData1,
1038 nmd.ctr.ctr1.count);
1039 if (!nmd.ctr.ctr1.array) {
1042 return LDB_ERR_OPERATIONS_ERROR;
1045 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1047 for (i=0; i < msg->num_elements;) {
1048 struct ldb_message_element *e = &msg->elements[i];
1049 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1050 const struct dsdb_attribute *sa;
1052 if (e->name[0] == '@') {
1057 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1059 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1060 "replmd_add: attribute '%s' not defined in schema\n",
1063 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1066 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1067 /* if the attribute is not replicated (0x00000001)
1068 * or constructed (0x00000004) it has no metadata
1074 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1075 ret = replmd_add_fix_la(module, e, ac->seq_num,
1076 &ac->our_invocation_id, now,
1078 if (ret != LDB_SUCCESS) {
1082 /* linked attributes are not stored in
1083 replPropertyMetaData in FL above w2k */
1088 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1090 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1091 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1094 if (rdn_val == NULL) {
1097 return LDB_ERR_OPERATIONS_ERROR;
1100 rdn = (const char*)rdn_val->data;
1101 if (strcmp(rdn, "Deleted Objects") == 0) {
1103 * Set the originating_change_time to 29/12/9999 at 23:59:59
1104 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1106 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1108 m->originating_change_time = now;
1111 m->originating_change_time = now;
1113 m->originating_invocation_id = ac->our_invocation_id;
1114 m->originating_usn = ac->seq_num;
1115 m->local_usn = ac->seq_num;
1118 if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1123 e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1125 if (e->num_values != 0) {
1130 ldb_msg_remove_element(msg, e);
1133 /* fix meta data count */
1134 nmd.ctr.ctr1.count = ni;
1137 * sort meta data array
1139 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1140 if (ret != LDB_SUCCESS) {
1141 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1146 /* generated NDR encoded values */
1147 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1149 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1153 return LDB_ERR_OPERATIONS_ERROR;
1157 * add the autogenerated values
1159 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1160 if (ret != LDB_SUCCESS) {
1165 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1166 if (ret != LDB_SUCCESS) {
1171 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1172 if (ret != LDB_SUCCESS) {
1177 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1178 if (ret != LDB_SUCCESS) {
1183 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1184 if (ret != LDB_SUCCESS) {
1191 * sort the attributes by attid before storing the object
1193 replmd_ldb_message_sort(msg, ac->schema);
1196 * Assert that we do have an objectClass
1198 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1199 if (objectclass_el == NULL) {
1200 ldb_asprintf_errstring(ldb, __location__
1201 ": objectClass missing on %s\n",
1202 ldb_dn_get_linearized(msg->dn));
1204 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1206 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1207 REPL_URGENT_ON_CREATE);
1209 ac->is_urgent = is_urgent;
1210 ret = ldb_build_add_req(&down_req, ldb, ac,
1213 ac, replmd_op_callback,
1216 LDB_REQ_SET_LOCATION(down_req);
1217 if (ret != LDB_SUCCESS) {
1222 /* current partition control is needed by "replmd_op_callback" */
1223 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1224 ret = ldb_request_add_control(down_req,
1225 DSDB_CONTROL_CURRENT_PARTITION_OID,
1227 if (ret != LDB_SUCCESS) {
1233 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1234 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1235 if (ret != LDB_SUCCESS) {
1241 /* mark the control done */
1243 control->critical = 0;
1245 /* go on with the call chain */
1246 return ldb_next_request(module, down_req);
1251 * update the replPropertyMetaData for one element
1253 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1254 struct ldb_message *msg,
1255 struct ldb_message_element *el,
1256 struct ldb_message_element *old_el,
1257 struct replPropertyMetaDataBlob *omd,
1258 const struct dsdb_schema *schema,
1260 const struct GUID *our_invocation_id,
1263 struct ldb_request *req)
1266 const struct dsdb_attribute *a;
1267 struct replPropertyMetaData1 *md1;
1268 bool may_skip = false;
1271 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1273 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1274 /* allow this to make it possible for dbcheck
1275 to remove bad attributes */
1279 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1281 return LDB_ERR_OPERATIONS_ERROR;
1284 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1286 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1291 * if the attribute's value haven't changed, and this isn't
1292 * just a delete of everything then return LDB_SUCCESS Unless
1293 * we have the provision control or if the attribute is
1294 * interSiteTopologyGenerator as this page explain:
1295 * http://support.microsoft.com/kb/224815 this attribute is
1296 * periodicaly written by the DC responsible for the intersite
1297 * generation in a given site
1299 * Unchanged could be deleting or replacing an already-gone
1300 * thing with an unconstrained delete/empty replace or a
1301 * replace with the same value, but not an add with the same
1302 * value because that could be about adding a duplicate (which
1303 * is for someone else to error out on).
1305 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1306 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1309 } else if (old_el == NULL && el->num_values == 0) {
1310 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1312 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1317 if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1319 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1323 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1324 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1326 * allow this to make it possible for dbcheck
1327 * to rebuild broken metadata
1333 for (i=0; i<omd->ctr.ctr1.count; i++) {
1335 * First check if we find it under the msDS-IntID,
1336 * then check if we find it under the OID and
1339 * This allows the administrator to simply re-write
1340 * the attributes and so restore replication, which is
1341 * likely what they will try to do.
1343 if (attid == omd->ctr.ctr1.array[i].attid) {
1347 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1352 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1353 /* linked attributes are not stored in
1354 replPropertyMetaData in FL above w2k, but we do
1355 raise the seqnum for the object */
1356 if (*seq_num == 0 &&
1357 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1358 return LDB_ERR_OPERATIONS_ERROR;
1363 if (i == omd->ctr.ctr1.count) {
1364 /* we need to add a new one */
1365 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1366 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1367 if (omd->ctr.ctr1.array == NULL) {
1369 return LDB_ERR_OPERATIONS_ERROR;
1371 omd->ctr.ctr1.count++;
1372 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1375 /* Get a new sequence number from the backend. We only do this
1376 * if we have a change that requires a new
1377 * replPropertyMetaData element
1379 if (*seq_num == 0) {
1380 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1381 if (ret != LDB_SUCCESS) {
1382 return LDB_ERR_OPERATIONS_ERROR;
1386 md1 = &omd->ctr.ctr1.array[i];
1389 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1390 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1393 if (rdn_val == NULL) {
1395 return LDB_ERR_OPERATIONS_ERROR;
1398 rdn = (const char*)rdn_val->data;
1399 if (strcmp(rdn, "Deleted Objects") == 0) {
1401 * Set the originating_change_time to 29/12/9999 at 23:59:59
1402 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1404 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1406 md1->originating_change_time = now;
1409 md1->originating_change_time = now;
1411 md1->originating_invocation_id = *our_invocation_id;
1412 md1->originating_usn = *seq_num;
1413 md1->local_usn = *seq_num;
1419 * Bump the replPropertyMetaData version on an attribute, and if it
1420 * has changed (or forced by leaving rdn_old NULL), update the value
1423 * This is important, as calling a modify operation may not change the
1424 * version number if the values appear unchanged, but a rename between
1425 * parents bumps this value.
1428 static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1429 struct ldb_message *msg,
1430 const struct ldb_val *rdn_new,
1431 const struct ldb_val *rdn_old,
1432 struct replPropertyMetaDataBlob *omd,
1433 struct replmd_replicated_request *ar,
1437 struct ldb_message_element new_el = {
1438 .flags = LDB_FLAG_MOD_REPLACE,
1439 .name = ldb_dn_get_rdn_name(msg->dn),
1441 .values = discard_const_p(struct ldb_val, rdn_new)
1443 struct ldb_message_element old_el = {
1444 .flags = LDB_FLAG_MOD_REPLACE,
1445 .name = ldb_dn_get_rdn_name(msg->dn),
1446 .num_values = rdn_old ? 1 : 0,
1447 .values = discard_const_p(struct ldb_val, rdn_old)
1450 if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1451 int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1452 if (ret != LDB_SUCCESS) {
1453 return ldb_oom(ldb);
1457 return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1458 omd, ar->schema, &ar->seq_num,
1459 &ar->our_invocation_id,
1460 now, is_schema_nc, ar->req);
1464 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1466 uint32_t count = omd.ctr.ctr1.count;
1469 for (i=0; i < count; i++) {
1470 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1471 if (max < m.local_usn) {
1479 * update the replPropertyMetaData object each time we modify an
1480 * object. This is needed for DRS replication, as the merge on the
1481 * client is based on this object
1483 static int replmd_update_rpmd(struct ldb_module *module,
1484 const struct dsdb_schema *schema,
1485 struct ldb_request *req,
1486 const char * const *rename_attrs,
1487 struct ldb_message *msg, uint64_t *seq_num,
1488 time_t t, bool is_schema_nc,
1489 bool *is_urgent, bool *rodc)
1491 const struct ldb_val *omd_value;
1492 enum ndr_err_code ndr_err;
1493 struct replPropertyMetaDataBlob omd;
1496 const struct GUID *our_invocation_id;
1498 const char * const *attrs = NULL;
1499 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1500 struct ldb_result *res;
1501 struct ldb_context *ldb;
1502 struct ldb_message_element *objectclass_el;
1503 enum urgent_situation situation;
1504 bool rmd_is_provided;
1505 bool rmd_is_just_resorted = false;
1506 const char *not_rename_attrs[4 + msg->num_elements];
1509 attrs = rename_attrs;
1511 for (i = 0; i < msg->num_elements; i++) {
1512 not_rename_attrs[i] = msg->elements[i].name;
1514 not_rename_attrs[i] = "replPropertyMetaData";
1515 not_rename_attrs[i+1] = "objectClass";
1516 not_rename_attrs[i+2] = "instanceType";
1517 not_rename_attrs[i+3] = NULL;
1518 attrs = not_rename_attrs;
1521 ldb = ldb_module_get_ctx(module);
1523 our_invocation_id = samdb_ntds_invocation_id(ldb);
1524 if (!our_invocation_id) {
1525 /* this happens during an initial vampire while
1526 updating the schema */
1527 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1531 unix_to_nt_time(&now, t);
1533 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1534 rmd_is_provided = true;
1535 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1536 rmd_is_just_resorted = true;
1539 rmd_is_provided = false;
1542 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1543 * otherwise we consider we are updating */
1544 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1545 situation = REPL_URGENT_ON_DELETE;
1546 } else if (rename_attrs) {
1547 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1549 situation = REPL_URGENT_ON_UPDATE;
1552 if (rmd_is_provided) {
1553 /* In this case the change_replmetadata control was supplied */
1554 /* We check that it's the only attribute that is provided
1555 * (it's a rare case so it's better to keep the code simplier)
1556 * We also check that the highest local_usn is bigger or the same as
1559 if( msg->num_elements != 1 ||
1560 strncmp(msg->elements[0].name,
1561 "replPropertyMetaData", 20) ) {
1562 DEBUG(0,(__location__ ": changereplmetada control called without "\
1563 "a specified replPropertyMetaData attribute or with others\n"));
1564 return LDB_ERR_OPERATIONS_ERROR;
1566 if (situation != REPL_URGENT_ON_UPDATE) {
1567 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1568 return LDB_ERR_OPERATIONS_ERROR;
1570 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1572 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1573 ldb_dn_get_linearized(msg->dn)));
1574 return LDB_ERR_OPERATIONS_ERROR;
1576 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1577 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1578 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1579 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1580 ldb_dn_get_linearized(msg->dn)));
1581 return LDB_ERR_OPERATIONS_ERROR;
1584 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1585 DSDB_FLAG_NEXT_MODULE |
1586 DSDB_SEARCH_SHOW_RECYCLED |
1587 DSDB_SEARCH_SHOW_EXTENDED_DN |
1588 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1589 DSDB_SEARCH_REVEAL_INTERNALS, req);
1591 if (ret != LDB_SUCCESS) {
1595 if (rmd_is_just_resorted == false) {
1596 *seq_num = find_max_local_usn(omd);
1598 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1601 * The test here now allows for a new
1602 * replPropertyMetaData with no change, if was
1603 * just dbcheck re-sorting the values.
1605 if (*seq_num <= db_seq) {
1606 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1607 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1608 (long long)*seq_num, (long long)db_seq));
1609 return LDB_ERR_OPERATIONS_ERROR;
1614 /* search for the existing replPropertyMetaDataBlob. We need
1615 * to use REVEAL and ask for DNs in storage format to support
1616 * the check for values being the same in
1617 * replmd_update_rpmd_element()
1619 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1620 DSDB_FLAG_NEXT_MODULE |
1621 DSDB_SEARCH_SHOW_RECYCLED |
1622 DSDB_SEARCH_SHOW_EXTENDED_DN |
1623 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1624 DSDB_SEARCH_REVEAL_INTERNALS, req);
1625 if (ret != LDB_SUCCESS) {
1629 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1631 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1632 ldb_dn_get_linearized(msg->dn)));
1633 return LDB_ERR_OPERATIONS_ERROR;
1636 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1637 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1638 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1639 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1640 ldb_dn_get_linearized(msg->dn)));
1641 return LDB_ERR_OPERATIONS_ERROR;
1644 if (omd.version != 1) {
1645 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1646 omd.version, ldb_dn_get_linearized(msg->dn)));
1647 return LDB_ERR_OPERATIONS_ERROR;
1650 for (i=0; i<msg->num_elements;) {
1651 struct ldb_message_element *el = &msg->elements[i];
1652 struct ldb_message_element *old_el;
1654 old_el = ldb_msg_find_element(res->msgs[0], el->name);
1655 ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1656 &omd, schema, seq_num,
1660 if (ret != LDB_SUCCESS) {
1664 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1665 *is_urgent = replmd_check_urgent_attribute(el);
1668 if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1673 el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1675 if (el->num_values != 0) {
1680 ldb_msg_remove_element(msg, el);
1685 * Assert that we have an objectClass attribute - this is major
1686 * corruption if we don't have this!
1688 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1689 if (objectclass_el != NULL) {
1691 * Now check if this objectClass means we need to do urgent replication
1693 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1697 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1698 ldb_asprintf_errstring(ldb, __location__
1699 ": objectClass missing on %s\n",
1700 ldb_dn_get_linearized(msg->dn));
1701 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1705 * replmd_update_rpmd_element has done an update if the
1708 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1709 struct ldb_val *md_value;
1710 struct ldb_message_element *el;
1712 /*if we are RODC and this is a DRSR update then its ok*/
1713 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1714 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1715 unsigned instanceType;
1717 ret = samdb_rodc(ldb, rodc);
1718 if (ret != LDB_SUCCESS) {
1719 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1721 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1722 return LDB_ERR_REFERRAL;
1725 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1726 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1727 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1728 "cannot change replicated attribute on partial replica");
1732 md_value = talloc(msg, struct ldb_val);
1733 if (md_value == NULL) {
1735 return LDB_ERR_OPERATIONS_ERROR;
1738 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
1739 if (ret != LDB_SUCCESS) {
1740 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1744 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1745 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1746 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1747 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1748 ldb_dn_get_linearized(msg->dn)));
1749 return LDB_ERR_OPERATIONS_ERROR;
1752 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1753 if (ret != LDB_SUCCESS) {
1754 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1755 ldb_dn_get_linearized(msg->dn)));
1760 el->values = md_value;
1767 struct dsdb_dn *dsdb_dn;
1772 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1774 return GUID_compare(&pdn1->guid, &pdn2->guid);
1777 static int GUID_compare_struct(struct GUID *g1, struct GUID g2)
1779 return GUID_compare(g1, &g2);
1782 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1783 unsigned int count, struct GUID *guid,
1786 struct parsed_dn *ret;
1788 if (dn && GUID_all_zero(guid)) {
1789 /* when updating a link using DRS, we sometimes get a
1790 NULL GUID. We then need to try and match by DN */
1791 for (i=0; i<count; i++) {
1792 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1793 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1799 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare_struct, ret);
1804 get a series of message element values as an array of DNs and GUIDs
1805 the result is sorted by GUID
1807 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1808 struct ldb_message_element *el, struct parsed_dn **pdn,
1809 const char *ldap_oid, struct ldb_request *parent)
1812 struct ldb_context *ldb = ldb_module_get_ctx(module);
1819 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1821 ldb_module_oom(module);
1822 return LDB_ERR_OPERATIONS_ERROR;
1825 for (i=0; i<el->num_values; i++) {
1826 struct ldb_val *v = &el->values[i];
1829 struct parsed_dn *p;
1833 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1834 if (p->dsdb_dn == NULL) {
1835 return LDB_ERR_INVALID_DN_SYNTAX;
1838 dn = p->dsdb_dn->dn;
1840 status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
1841 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1842 /* we got a DN without a GUID - go find the GUID */
1843 int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
1844 if (ret != LDB_SUCCESS) {
1845 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1846 ldb_dn_get_linearized(dn));
1847 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1848 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1849 ldb_attr_cmp(el->name, "member") == 0) {
1850 return LDB_ERR_UNWILLING_TO_PERFORM;
1854 ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
1855 if (ret != LDB_SUCCESS) {
1858 } else if (!NT_STATUS_IS_OK(status)) {
1859 return LDB_ERR_OPERATIONS_ERROR;
1862 /* keep a pointer to the original ldb_val */
1866 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1872 build a new extended DN, including all meta data fields
1874 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1875 RMD_ADDTIME = originating_add_time
1876 RMD_INVOCID = originating_invocation_id
1877 RMD_CHANGETIME = originating_change_time
1878 RMD_ORIGINATING_USN = originating_usn
1879 RMD_LOCAL_USN = local_usn
1880 RMD_VERSION = version
1882 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1883 const struct GUID *invocation_id, uint64_t seq_num,
1884 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1886 struct ldb_dn *dn = dsdb_dn->dn;
1887 const char *tstring, *usn_string, *flags_string;
1888 struct ldb_val tval;
1890 struct ldb_val usnv, local_usnv;
1891 struct ldb_val vers, flagsv;
1894 const char *dnstring;
1896 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1898 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1900 return LDB_ERR_OPERATIONS_ERROR;
1902 tval = data_blob_string_const(tstring);
1904 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1906 return LDB_ERR_OPERATIONS_ERROR;
1908 usnv = data_blob_string_const(usn_string);
1910 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1912 return LDB_ERR_OPERATIONS_ERROR;
1914 local_usnv = data_blob_string_const(usn_string);
1916 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1918 return LDB_ERR_OPERATIONS_ERROR;
1920 vers = data_blob_string_const(vstring);
1922 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1923 if (!NT_STATUS_IS_OK(status)) {
1924 return LDB_ERR_OPERATIONS_ERROR;
1927 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1928 if (!flags_string) {
1929 return LDB_ERR_OPERATIONS_ERROR;
1931 flagsv = data_blob_string_const(flags_string);
1933 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1934 if (ret != LDB_SUCCESS) return ret;
1935 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1936 if (ret != LDB_SUCCESS) return ret;
1937 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1938 if (ret != LDB_SUCCESS) return ret;
1939 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1940 if (ret != LDB_SUCCESS) return ret;
1941 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1942 if (ret != LDB_SUCCESS) return ret;
1943 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1944 if (ret != LDB_SUCCESS) return ret;
1945 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1946 if (ret != LDB_SUCCESS) return ret;
1948 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1949 if (dnstring == NULL) {
1950 return LDB_ERR_OPERATIONS_ERROR;
1952 *v = data_blob_string_const(dnstring);
1957 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1958 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1959 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1960 uint32_t version, bool deleted);
1963 check if any links need upgrading from w2k format
1965 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1967 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1970 for (i=0; i<count; i++) {
1975 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1976 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1980 /* it's an old one that needs upgrading */
1981 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1983 if (ret != LDB_SUCCESS) {
1991 update an extended DN, including all meta data fields
1993 see replmd_build_la_val for value names
1995 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1996 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1997 uint64_t usn, uint64_t local_usn, NTTIME nttime,
1998 uint32_t version, bool deleted)
2000 struct ldb_dn *dn = dsdb_dn->dn;
2001 const char *tstring, *usn_string, *flags_string;
2002 struct ldb_val tval;
2004 struct ldb_val usnv, local_usnv;
2005 struct ldb_val vers, flagsv;
2006 const struct ldb_val *old_addtime;
2007 uint32_t old_version;
2010 const char *dnstring;
2012 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2014 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2016 return LDB_ERR_OPERATIONS_ERROR;
2018 tval = data_blob_string_const(tstring);
2020 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2022 return LDB_ERR_OPERATIONS_ERROR;
2024 usnv = data_blob_string_const(usn_string);
2026 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2028 return LDB_ERR_OPERATIONS_ERROR;
2030 local_usnv = data_blob_string_const(usn_string);
2032 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2033 if (!NT_STATUS_IS_OK(status)) {
2034 return LDB_ERR_OPERATIONS_ERROR;
2037 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2038 if (!flags_string) {
2039 return LDB_ERR_OPERATIONS_ERROR;
2041 flagsv = data_blob_string_const(flags_string);
2043 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2044 if (ret != LDB_SUCCESS) return ret;
2046 /* get the ADDTIME from the original */
2047 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
2048 if (old_addtime == NULL) {
2049 old_addtime = &tval;
2051 if (dsdb_dn != old_dsdb_dn ||
2052 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2053 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2054 if (ret != LDB_SUCCESS) return ret;
2057 /* use our invocation id */
2058 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2059 if (ret != LDB_SUCCESS) return ret;
2061 /* changetime is the current time */
2062 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2063 if (ret != LDB_SUCCESS) return ret;
2065 /* update the USN */
2066 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2067 if (ret != LDB_SUCCESS) return ret;
2069 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2070 if (ret != LDB_SUCCESS) return ret;
2072 /* increase the version by 1 */
2073 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
2074 if (NT_STATUS_IS_OK(status) && old_version >= version) {
2075 version = old_version+1;
2077 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
2078 vers = data_blob_string_const(vstring);
2079 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2080 if (ret != LDB_SUCCESS) return ret;
2082 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2083 if (dnstring == NULL) {
2084 return LDB_ERR_OPERATIONS_ERROR;
2086 *v = data_blob_string_const(dnstring);
2092 handle adding a linked attribute
2094 static int replmd_modify_la_add(struct ldb_module *module,
2095 const struct dsdb_schema *schema,
2096 struct ldb_message *msg,
2097 struct ldb_message_element *el,
2098 struct ldb_message_element *old_el,
2099 const struct dsdb_attribute *schema_attr,
2102 struct GUID *msg_guid,
2103 struct ldb_request *parent)
2106 struct parsed_dn *dns, *old_dns;
2107 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2109 struct ldb_val *new_values = NULL;
2110 unsigned int num_new_values = 0;
2111 unsigned old_num_values = old_el?old_el->num_values:0;
2112 const struct GUID *invocation_id;
2113 struct ldb_context *ldb = ldb_module_get_ctx(module);
2116 unix_to_nt_time(&now, t);
2118 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2119 if (ret != LDB_SUCCESS) {
2120 talloc_free(tmp_ctx);
2124 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2125 if (ret != LDB_SUCCESS) {
2126 talloc_free(tmp_ctx);
2130 invocation_id = samdb_ntds_invocation_id(ldb);
2131 if (!invocation_id) {
2132 talloc_free(tmp_ctx);
2133 return LDB_ERR_OPERATIONS_ERROR;
2136 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2137 if (ret != LDB_SUCCESS) {
2138 talloc_free(tmp_ctx);
2142 /* for each new value, see if it exists already with the same GUID */
2143 for (i=0; i<el->num_values; i++) {
2144 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, &dns[i].guid, NULL);
2146 /* this is a new linked attribute value */
2147 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
2148 if (new_values == NULL) {
2149 ldb_module_oom(module);
2150 talloc_free(tmp_ctx);
2151 return LDB_ERR_OPERATIONS_ERROR;
2153 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2154 invocation_id, seq_num, seq_num, now, 0, false);
2155 if (ret != LDB_SUCCESS) {
2156 talloc_free(tmp_ctx);
2161 /* this is only allowed if the GUID was
2162 previously deleted. */
2163 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2165 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2166 struct GUID_txt_buf guid_str;
2167 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2168 el->name, GUID_buf_string(&p->guid, &guid_str));
2169 talloc_free(tmp_ctx);
2170 /* error codes for 'member' need to be
2172 if (ldb_attr_cmp(el->name, "member") == 0) {
2173 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2175 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2178 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2179 invocation_id, seq_num, seq_num, now, 0, false);
2180 if (ret != LDB_SUCCESS) {
2181 talloc_free(tmp_ctx);
2186 ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, true);
2187 if (ret != LDB_SUCCESS) {
2188 talloc_free(tmp_ctx);
2193 /* add the new ones on to the end of the old values, constructing a new el->values */
2194 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2196 old_num_values+num_new_values);
2197 if (el->values == NULL) {
2198 ldb_module_oom(module);
2199 return LDB_ERR_OPERATIONS_ERROR;
2202 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2203 el->num_values = old_num_values + num_new_values;
2205 talloc_steal(msg->elements, el->values);
2206 talloc_steal(el->values, new_values);
2208 talloc_free(tmp_ctx);
2210 /* we now tell the backend to replace all existing values
2211 with the one we have constructed */
2212 el->flags = LDB_FLAG_MOD_REPLACE;
2219 handle deleting all active linked attributes
2221 static int replmd_modify_la_delete(struct ldb_module *module,
2222 const struct dsdb_schema *schema,
2223 struct ldb_message *msg,
2224 struct ldb_message_element *el,
2225 struct ldb_message_element *old_el,
2226 const struct dsdb_attribute *schema_attr,
2229 struct GUID *msg_guid,
2230 struct ldb_request *parent)
2233 struct parsed_dn *dns, *old_dns;
2234 TALLOC_CTX *tmp_ctx = NULL;
2236 const struct GUID *invocation_id;
2237 struct ldb_context *ldb = ldb_module_get_ctx(module);
2238 struct ldb_control *vanish_links_ctrl = NULL;
2239 bool vanish_links = false;
2240 unsigned int num_to_delete = el->num_values;
2243 unix_to_nt_time(&now, t);
2245 /* check if there is nothing to delete */
2246 if ((!old_el || old_el->num_values == 0) &&
2247 el->num_values == 0) {
2251 if (!old_el || old_el->num_values == 0) {
2252 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2255 tmp_ctx = talloc_new(msg);
2256 if (tmp_ctx == NULL) {
2257 return LDB_ERR_OPERATIONS_ERROR;
2260 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2261 if (ret != LDB_SUCCESS) {
2262 talloc_free(tmp_ctx);
2266 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2267 if (ret != LDB_SUCCESS) {
2268 talloc_free(tmp_ctx);
2272 invocation_id = samdb_ntds_invocation_id(ldb);
2273 if (!invocation_id) {
2274 talloc_free(tmp_ctx);
2275 return LDB_ERR_OPERATIONS_ERROR;
2278 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2279 if (ret != LDB_SUCCESS) {
2280 talloc_free(tmp_ctx);
2285 vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2286 if (vanish_links_ctrl) {
2287 vanish_links = true;
2288 vanish_links_ctrl->critical = false;
2295 /* see if we are being asked to delete any links that
2296 don't exist or are already deleted */
2297 for (i=0; i < num_to_delete; i++) {
2298 struct parsed_dn *p = &dns[i];
2299 struct parsed_dn *p2;
2302 p2 = parsed_dn_find(old_dns, old_el->num_values, &p->guid, NULL);
2304 struct GUID_txt_buf buf;
2305 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2306 el->name, GUID_buf_string(&p->guid, &buf));
2307 if (ldb_attr_cmp(el->name, "member") == 0) {
2308 talloc_free(tmp_ctx);
2309 return LDB_ERR_UNWILLING_TO_PERFORM;
2311 talloc_free(tmp_ctx);
2312 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2315 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2316 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2317 struct GUID_txt_buf buf;
2318 const char *guid_str = GUID_buf_string(&p->guid, &buf);
2320 DEBUG(0, ("Deleting deleted linked attribute %s to %s, "
2321 "because vanish_links control is set\n",
2322 el->name, guid_str));
2325 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2326 el->name, guid_str);
2327 if (ldb_attr_cmp(el->name, "member") == 0) {
2328 talloc_free(tmp_ctx);
2329 return LDB_ERR_UNWILLING_TO_PERFORM;
2331 talloc_free(tmp_ctx);
2332 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2338 if (num_to_delete == old_el->num_values || num_to_delete == 0) {
2339 el->flags = LDB_FLAG_MOD_REPLACE;
2341 for (i = 0; i < old_el->num_values; i++) {
2342 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2343 if (ret != LDB_SUCCESS) {
2344 talloc_free(tmp_ctx);
2348 talloc_free(tmp_ctx);
2351 unsigned int num_values = 0;
2353 for (i = 0; i < old_el->num_values; i++) {
2354 if (parsed_dn_find(dns, num_to_delete, &old_dns[i].guid, NULL) != NULL) {
2355 /* The element is in the delete list. mark it dead. */
2356 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2357 if (ret != LDB_SUCCESS) {
2358 talloc_free(tmp_ctx);
2361 old_dns[i].v->length = 0;
2366 for (i = 0; i < old_el->num_values; i++) {
2367 if (old_el->values[i].length != 0) {
2368 old_el->values[j] = old_el->values[i];
2370 if (j == num_values) {
2375 old_el->num_values = num_values;
2379 /* for each new value, see if it exists already with the same GUID
2380 if it is not already deleted and matches the delete list then delete it
2382 for (i=0; i<old_el->num_values; i++) {
2383 struct parsed_dn *p = &old_dns[i];
2386 if (num_to_delete && parsed_dn_find(dns, num_to_delete, &p->guid, NULL) == NULL) {
2390 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2391 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2393 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2394 invocation_id, seq_num, seq_num, now, 0, true);
2395 if (ret != LDB_SUCCESS) {
2396 talloc_free(tmp_ctx);
2399 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
2400 if (ret != LDB_SUCCESS) {
2401 talloc_free(tmp_ctx);
2406 el->values = talloc_steal(msg->elements, old_el->values);
2407 el->num_values = old_el->num_values;
2409 talloc_free(tmp_ctx);
2411 /* we now tell the backend to replace all existing values
2412 with the one we have constructed */
2413 el->flags = LDB_FLAG_MOD_REPLACE;
2419 handle replacing a linked attribute
2421 static int replmd_modify_la_replace(struct ldb_module *module,
2422 const struct dsdb_schema *schema,
2423 struct ldb_message *msg,
2424 struct ldb_message_element *el,
2425 struct ldb_message_element *old_el,
2426 const struct dsdb_attribute *schema_attr,
2429 struct GUID *msg_guid,
2430 struct ldb_request *parent)
2433 struct parsed_dn *dns, *old_dns;
2434 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2436 const struct GUID *invocation_id;
2437 struct ldb_context *ldb = ldb_module_get_ctx(module);
2438 struct ldb_val *new_values = NULL;
2439 unsigned int num_new_values = 0;
2440 unsigned int old_num_values = old_el?old_el->num_values:0;
2443 unix_to_nt_time(&now, t);
2445 /* check if there is nothing to replace */
2446 if ((!old_el || old_el->num_values == 0) &&
2447 el->num_values == 0) {
2451 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2452 if (ret != LDB_SUCCESS) {
2453 talloc_free(tmp_ctx);
2457 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2458 if (ret != LDB_SUCCESS) {
2459 talloc_free(tmp_ctx);
2463 invocation_id = samdb_ntds_invocation_id(ldb);
2464 if (!invocation_id) {
2465 return LDB_ERR_OPERATIONS_ERROR;
2468 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2469 if (ret != LDB_SUCCESS) {
2470 talloc_free(tmp_ctx);
2474 /* mark all the old ones as deleted */
2475 for (i=0; i<old_num_values; i++) {
2476 struct parsed_dn *old_p = &old_dns[i];
2477 struct parsed_dn *p;
2478 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2480 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2482 ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, false);
2483 if (ret != LDB_SUCCESS) {
2484 talloc_free(tmp_ctx);
2488 p = parsed_dn_find(dns, el->num_values, &old_p->guid, NULL);
2490 /* we don't delete it if we are re-adding it */
2494 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2495 invocation_id, seq_num, seq_num, now, 0, true);
2496 if (ret != LDB_SUCCESS) {
2497 talloc_free(tmp_ctx);
2502 /* for each new value, either update its meta-data, or add it
2505 for (i=0; i<el->num_values; i++) {
2506 struct parsed_dn *p = &dns[i], *old_p;
2509 (old_p = parsed_dn_find(old_dns,
2510 old_num_values, &p->guid, NULL)) != NULL) {
2511 /* update in place */
2512 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2513 old_p->dsdb_dn, invocation_id,
2514 seq_num, seq_num, now, 0, false);
2515 if (ret != LDB_SUCCESS) {
2516 talloc_free(tmp_ctx);
2521 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2523 if (new_values == NULL) {
2524 ldb_module_oom(module);
2525 talloc_free(tmp_ctx);
2526 return LDB_ERR_OPERATIONS_ERROR;
2528 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2529 invocation_id, seq_num, seq_num, now, 0, false);
2530 if (ret != LDB_SUCCESS) {
2531 talloc_free(tmp_ctx);
2537 ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, false);
2538 if (ret != LDB_SUCCESS) {
2539 talloc_free(tmp_ctx);
2544 /* add the new values to the end of old_el */
2545 if (num_new_values != 0) {
2546 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2547 struct ldb_val, old_num_values+num_new_values);
2548 if (el->values == NULL) {
2549 ldb_module_oom(module);
2550 return LDB_ERR_OPERATIONS_ERROR;
2552 memcpy(&el->values[old_num_values], &new_values[0],
2553 sizeof(struct ldb_val)*num_new_values);
2554 el->num_values = old_num_values + num_new_values;
2555 talloc_steal(msg->elements, new_values);
2557 el->values = old_el->values;
2558 el->num_values = old_el->num_values;
2559 talloc_steal(msg->elements, el->values);
2562 talloc_free(tmp_ctx);
2564 /* we now tell the backend to replace all existing values
2565 with the one we have constructed */
2566 el->flags = LDB_FLAG_MOD_REPLACE;
2573 handle linked attributes in modify requests
2575 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2576 struct ldb_message *msg,
2577 uint64_t seq_num, time_t t,
2578 struct ldb_request *parent)
2580 struct ldb_result *res;
2583 struct ldb_context *ldb = ldb_module_get_ctx(module);
2584 struct ldb_message *old_msg;
2586 const struct dsdb_schema *schema;
2587 struct GUID old_guid;
2589 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2591 * Nothing special is required for modifying or vanishing links
2592 * in fl2000 since they are just strings in a multi-valued
2595 struct ldb_control *ctrl = ldb_request_get_control(parent,
2596 DSDB_CONTROL_REPLMD_VANISH_LINKS);
2598 ctrl->critical = false;
2603 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2604 DSDB_FLAG_NEXT_MODULE |
2605 DSDB_SEARCH_SHOW_RECYCLED |
2606 DSDB_SEARCH_REVEAL_INTERNALS |
2607 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2609 if (ret != LDB_SUCCESS) {
2612 schema = dsdb_get_schema(ldb, res);
2614 return LDB_ERR_OPERATIONS_ERROR;
2617 old_msg = res->msgs[0];
2619 old_guid = samdb_result_guid(old_msg, "objectGUID");
2621 for (i=0; i<msg->num_elements; i++) {
2622 struct ldb_message_element *el = &msg->elements[i];
2623 struct ldb_message_element *old_el, *new_el;
2624 const struct dsdb_attribute *schema_attr
2625 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2627 ldb_asprintf_errstring(ldb,
2628 "%s: attribute %s is not a valid attribute in schema",
2629 __FUNCTION__, el->name);
2630 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2632 if (schema_attr->linkID == 0) {
2635 if ((schema_attr->linkID & 1) == 1) {
2636 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2639 /* Odd is for the target. Illegal to modify */
2640 ldb_asprintf_errstring(ldb,
2641 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2642 return LDB_ERR_UNWILLING_TO_PERFORM;
2644 old_el = ldb_msg_find_element(old_msg, el->name);
2645 switch (el->flags & LDB_FLAG_MOD_MASK) {
2646 case LDB_FLAG_MOD_REPLACE:
2647 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2649 case LDB_FLAG_MOD_DELETE:
2650 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2652 case LDB_FLAG_MOD_ADD:
2653 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2656 ldb_asprintf_errstring(ldb,
2657 "invalid flags 0x%x for %s linked attribute",
2658 el->flags, el->name);
2659 return LDB_ERR_UNWILLING_TO_PERFORM;
2661 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2662 ldb_asprintf_errstring(ldb,
2663 "Attribute %s is single valued but more than one value has been supplied",
2665 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2667 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2672 if (ret != LDB_SUCCESS) {
2676 ldb_msg_remove_attr(old_msg, el->name);
2678 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2679 new_el->num_values = el->num_values;
2680 new_el->values = talloc_steal(msg->elements, el->values);
2682 /* TODO: this relises a bit too heavily on the exact
2683 behaviour of ldb_msg_find_element and
2684 ldb_msg_remove_element */
2685 old_el = ldb_msg_find_element(msg, el->name);
2687 ldb_msg_remove_element(msg, old_el);
2698 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2700 struct ldb_context *ldb;
2701 struct replmd_replicated_request *ac;
2702 struct ldb_request *down_req;
2703 struct ldb_message *msg;
2704 time_t t = time(NULL);
2706 bool is_urgent = false, rodc = false;
2707 bool is_schema_nc = false;
2708 unsigned int functional_level;
2709 const struct ldb_message_element *guid_el = NULL;
2710 struct ldb_control *sd_propagation_control;
2711 struct replmd_private *replmd_private =
2712 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2714 /* do not manipulate our control entries */
2715 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2716 return ldb_next_request(module, req);
2719 sd_propagation_control = ldb_request_get_control(req,
2720 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2721 if (sd_propagation_control != NULL) {
2722 if (req->op.mod.message->num_elements != 1) {
2723 return ldb_module_operr(module);
2725 ret = strcmp(req->op.mod.message->elements[0].name,
2726 "nTSecurityDescriptor");
2728 return ldb_module_operr(module);
2731 return ldb_next_request(module, req);
2734 ldb = ldb_module_get_ctx(module);
2736 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2738 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2739 if (guid_el != NULL) {
2740 ldb_set_errstring(ldb,
2741 "replmd_modify: it's not allowed to change the objectGUID!");
2742 return LDB_ERR_CONSTRAINT_VIOLATION;
2745 ac = replmd_ctx_init(module, req);
2747 return ldb_module_oom(module);
2750 functional_level = dsdb_functional_level(ldb);
2752 /* we have to copy the message as the caller might have it as a const */
2753 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2757 return LDB_ERR_OPERATIONS_ERROR;
2760 ldb_msg_remove_attr(msg, "whenChanged");
2761 ldb_msg_remove_attr(msg, "uSNChanged");
2763 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2765 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2766 msg, &ac->seq_num, t, is_schema_nc,
2768 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2769 struct loadparm_context *lp_ctx;
2772 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2773 struct loadparm_context);
2775 referral = talloc_asprintf(req,
2777 lpcfg_dnsdomain(lp_ctx),
2778 ldb_dn_get_linearized(msg->dn));
2779 ret = ldb_module_send_referral(req, referral);
2784 if (ret != LDB_SUCCESS) {
2789 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2790 if (ret != LDB_SUCCESS) {
2796 * - replace the old object with the newly constructed one
2799 ac->is_urgent = is_urgent;
2801 ret = ldb_build_mod_req(&down_req, ldb, ac,
2804 ac, replmd_op_callback,
2806 LDB_REQ_SET_LOCATION(down_req);
2807 if (ret != LDB_SUCCESS) {
2812 /* current partition control is needed by "replmd_op_callback" */
2813 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2814 ret = ldb_request_add_control(down_req,
2815 DSDB_CONTROL_CURRENT_PARTITION_OID,
2817 if (ret != LDB_SUCCESS) {
2823 /* If we are in functional level 2000, then
2824 * replmd_modify_handle_linked_attribs will have done
2826 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2827 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2828 if (ret != LDB_SUCCESS) {
2834 talloc_steal(down_req, msg);
2836 /* we only change whenChanged and uSNChanged if the seq_num
2838 if (ac->seq_num != 0) {
2839 ret = add_time_element(msg, "whenChanged", t);
2840 if (ret != LDB_SUCCESS) {
2846 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2847 if (ret != LDB_SUCCESS) {
2854 /* go on with the call chain */
2855 return ldb_next_request(module, down_req);
2858 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2861 handle a rename request
2863 On a rename we need to do an extra ldb_modify which sets the
2864 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2866 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2868 struct ldb_context *ldb;
2869 struct replmd_replicated_request *ac;
2871 struct ldb_request *down_req;
2873 /* do not manipulate our control entries */
2874 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2875 return ldb_next_request(module, req);
2878 ldb = ldb_module_get_ctx(module);
2880 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2882 ac = replmd_ctx_init(module, req);
2884 return ldb_module_oom(module);
2887 ret = ldb_build_rename_req(&down_req, ldb, ac,
2888 ac->req->op.rename.olddn,
2889 ac->req->op.rename.newdn,
2891 ac, replmd_rename_callback,
2893 LDB_REQ_SET_LOCATION(down_req);
2894 if (ret != LDB_SUCCESS) {
2899 /* go on with the call chain */
2900 return ldb_next_request(module, down_req);
2903 /* After the rename is compleated, update the whenchanged etc */
2904 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2906 struct ldb_context *ldb;
2907 struct ldb_request *down_req;
2908 struct ldb_message *msg;
2909 const struct dsdb_attribute *rdn_attr;
2910 const char *rdn_name;
2911 const struct ldb_val *rdn_val;
2912 const char *attrs[5] = { NULL, };
2913 time_t t = time(NULL);
2915 bool is_urgent = false, rodc = false;
2917 struct replmd_replicated_request *ac =
2918 talloc_get_type(req->context, struct replmd_replicated_request);
2919 struct replmd_private *replmd_private =
2920 talloc_get_type(ldb_module_get_private(ac->module),
2921 struct replmd_private);
2923 ldb = ldb_module_get_ctx(ac->module);
2925 if (ares->error != LDB_SUCCESS) {
2926 return ldb_module_done(ac->req, ares->controls,
2927 ares->response, ares->error);
2930 if (ares->type != LDB_REPLY_DONE) {
2931 ldb_set_errstring(ldb,
2932 "invalid ldb_reply_type in callback");
2934 return ldb_module_done(ac->req, NULL, NULL,
2935 LDB_ERR_OPERATIONS_ERROR);
2939 * - replace the old object with the newly constructed one
2942 msg = ldb_msg_new(ac);
2945 return LDB_ERR_OPERATIONS_ERROR;
2948 msg->dn = ac->req->op.rename.newdn;
2950 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2952 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2953 if (rdn_name == NULL) {
2955 return ldb_module_done(ac->req, NULL, NULL,
2959 /* normalize the rdn attribute name */
2960 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2961 if (rdn_attr == NULL) {
2963 return ldb_module_done(ac->req, NULL, NULL,
2966 rdn_name = rdn_attr->lDAPDisplayName;
2968 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2969 if (rdn_val == NULL) {
2971 return ldb_module_done(ac->req, NULL, NULL,
2975 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2977 return ldb_module_done(ac->req, NULL, NULL,
2980 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2982 return ldb_module_done(ac->req, NULL, NULL,
2985 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2987 return ldb_module_done(ac->req, NULL, NULL,
2990 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2992 return ldb_module_done(ac->req, NULL, NULL,
2997 * here we let replmd_update_rpmd() only search for
2998 * the existing "replPropertyMetaData" and rdn_name attributes.
3000 * We do not want the existing "name" attribute as
3001 * the "name" attribute needs to get the version
3002 * updated on rename even if the rdn value hasn't changed.
3004 * This is the diff of the meta data, for a moved user
3005 * on a w2k8r2 server:
3008 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3009 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3010 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3011 * version : 0x00000001 (1)
3012 * reserved : 0x00000000 (0)
3013 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3014 * local_usn : 0x00000000000037a5 (14245)
3015 * array: struct replPropertyMetaData1
3016 * attid : DRSUAPI_ATTID_name (0x90001)
3017 * - version : 0x00000001 (1)
3018 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3019 * + version : 0x00000002 (2)
3020 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3021 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3022 * - originating_usn : 0x00000000000037a5 (14245)
3023 * - local_usn : 0x00000000000037a5 (14245)
3024 * + originating_usn : 0x0000000000003834 (14388)
3025 * + local_usn : 0x0000000000003834 (14388)
3026 * array: struct replPropertyMetaData1
3027 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3028 * version : 0x00000004 (4)
3030 attrs[0] = "replPropertyMetaData";
3031 attrs[1] = "objectClass";
3032 attrs[2] = "instanceType";
3033 attrs[3] = rdn_name;
3036 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3037 msg, &ac->seq_num, t,
3038 is_schema_nc, &is_urgent, &rodc);
3039 if (rodc && (ret == LDB_ERR_REFERRAL)) {
3040 struct ldb_dn *olddn = ac->req->op.rename.olddn;
3041 struct loadparm_context *lp_ctx;
3044 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3045 struct loadparm_context);
3047 referral = talloc_asprintf(req,
3049 lpcfg_dnsdomain(lp_ctx),
3050 ldb_dn_get_linearized(olddn));
3051 ret = ldb_module_send_referral(req, referral);
3053 return ldb_module_done(req, NULL, NULL, ret);
3056 if (ret != LDB_SUCCESS) {
3058 return ldb_module_done(ac->req, NULL, NULL, ret);
3061 if (ac->seq_num == 0) {
3063 return ldb_module_done(ac->req, NULL, NULL,
3065 "internal error seq_num == 0"));
3067 ac->is_urgent = is_urgent;
3069 ret = ldb_build_mod_req(&down_req, ldb, ac,
3072 ac, replmd_op_callback,
3074 LDB_REQ_SET_LOCATION(down_req);
3075 if (ret != LDB_SUCCESS) {
3080 /* current partition control is needed by "replmd_op_callback" */
3081 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3082 ret = ldb_request_add_control(down_req,
3083 DSDB_CONTROL_CURRENT_PARTITION_OID,
3085 if (ret != LDB_SUCCESS) {
3091 talloc_steal(down_req, msg);
3093 ret = add_time_element(msg, "whenChanged", t);
3094 if (ret != LDB_SUCCESS) {
3100 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3101 if (ret != LDB_SUCCESS) {
3107 /* go on with the call chain - do the modify after the rename */
3108 return ldb_next_request(ac->module, down_req);
3112 * remove links from objects that point at this object when an object
3113 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
3114 * RemoveObj which states that link removal due to the object being
3115 * deleted is NOT an originating update - they just go away!
3118 static int replmd_delete_remove_link(struct ldb_module *module,
3119 const struct dsdb_schema *schema,
3121 struct ldb_message_element *el,
3122 const struct dsdb_attribute *sa,
3123 struct ldb_request *parent)
3126 TALLOC_CTX *tmp_ctx = talloc_new(module);
3127 struct ldb_context *ldb = ldb_module_get_ctx(module);
3129 for (i=0; i<el->num_values; i++) {
3130 struct dsdb_dn *dsdb_dn;
3134 struct ldb_message *msg;
3135 const struct dsdb_attribute *target_attr;
3136 struct ldb_message_element *el2;
3137 struct ldb_val dn_val;
3138 uint32_t dsdb_flags = 0;
3140 if (dsdb_dn_is_deleted_val(&el->values[i])) {
3144 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
3146 talloc_free(tmp_ctx);
3147 return LDB_ERR_OPERATIONS_ERROR;
3150 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
3151 if (!NT_STATUS_IS_OK(status)) {
3152 talloc_free(tmp_ctx);
3153 return LDB_ERR_OPERATIONS_ERROR;
3156 /* remove the link */
3157 msg = ldb_msg_new(tmp_ctx);
3159 ldb_module_oom(module);
3160 talloc_free(tmp_ctx);
3161 return LDB_ERR_OPERATIONS_ERROR;
3165 msg->dn = dsdb_dn->dn;
3167 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
3168 if (target_attr == NULL) {
3172 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
3173 if (ret != LDB_SUCCESS) {
3174 ldb_module_oom(module);
3175 talloc_free(tmp_ctx);
3176 return LDB_ERR_OPERATIONS_ERROR;
3178 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
3179 el2->values = &dn_val;
3180 el2->num_values = 1;
3183 * Ensure that we tell the modification to vanish any linked
3184 * attributes (not simply mark them as isDeleted = TRUE)
3186 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3188 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
3189 if (ret != LDB_SUCCESS) {
3190 talloc_free(tmp_ctx);
3194 talloc_free(tmp_ctx);
3200 handle update of replication meta data for deletion of objects
3202 This also handles the mapping of delete to a rename operation
3203 to allow deletes to be replicated.
3205 It also handles the incoming deleted objects, to ensure they are
3206 fully deleted here. In that case re_delete is true, and we do not
3207 use this as a signal to change the deleted state, just reinforce it.
3210 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3212 int ret = LDB_ERR_OTHER;
3213 bool retb, disallow_move_on_delete;
3214 struct ldb_dn *old_dn, *new_dn;
3215 const char *rdn_name;
3216 const struct ldb_val *rdn_value, *new_rdn_value;
3218 struct ldb_context *ldb = ldb_module_get_ctx(module);
3219 const struct dsdb_schema *schema;
3220 struct ldb_message *msg, *old_msg;
3221 struct ldb_message_element *el;
3222 TALLOC_CTX *tmp_ctx;
3223 struct ldb_result *res, *parent_res;
3224 static const char * const preserved_attrs[] = {
3225 /* yes, this really is a hard coded list. See MS-ADTS
3226 section 3.1.1.5.5.1.1 */
3229 "dNReferenceUpdate",
3240 "msDS-LastKnownRDN",
3246 "distinguishedName",
3250 "proxiedObjectName",
3252 "nTSecurityDescriptor",
3253 "replPropertyMetaData",
3255 "securityIdentifier",
3263 "userAccountControl",
3270 static const char * const all_attrs[] = {
3271 DSDB_SECRET_ATTRIBUTES,
3275 unsigned int i, el_count = 0;
3276 uint32_t dsdb_flags = 0;
3277 enum deletion_state deletion_state, next_deletion_state;
3279 if (ldb_dn_is_special(req->op.del.dn)) {
3280 return ldb_next_request(module, req);
3284 * We have to allow dbcheck to remove an object that
3285 * is beyond repair, and to do so totally. This could
3286 * mean we we can get a partial object from the other
3287 * DC, causing havoc, so dbcheck suggests
3288 * re-replication first. dbcheck sets both DBCHECK
3289 * and RELAX in this situation.
3291 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3292 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3293 /* really, really remove it */
3294 return ldb_next_request(module, req);
3297 tmp_ctx = talloc_new(ldb);
3300 return LDB_ERR_OPERATIONS_ERROR;
3303 schema = dsdb_get_schema(ldb, tmp_ctx);
3305 talloc_free(tmp_ctx);
3306 return LDB_ERR_OPERATIONS_ERROR;
3309 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3311 /* we need the complete msg off disk, so we can work out which
3312 attributes need to be removed */
3313 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
3314 DSDB_FLAG_NEXT_MODULE |
3315 DSDB_SEARCH_SHOW_RECYCLED |
3316 DSDB_SEARCH_REVEAL_INTERNALS |
3317 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3318 if (ret != LDB_SUCCESS) {
3319 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3320 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3321 re_delete ? "re-delete" : "delete",
3322 ldb_dn_get_linearized(old_dn),
3323 ldb_errstring(ldb_module_get_ctx(module)));
3324 talloc_free(tmp_ctx);
3327 old_msg = res->msgs[0];
3329 replmd_deletion_state(module, old_msg,
3331 &next_deletion_state);
3333 /* This supports us noticing an incoming isDeleted and acting on it */
3335 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3336 next_deletion_state = deletion_state;
3339 if (next_deletion_state == OBJECT_REMOVED) {
3341 * We have to prevent objects being deleted, even if
3342 * the administrator really wants them gone, as
3343 * without the tombstone, we can get a partial object
3344 * from the other DC, causing havoc.
3346 * The only other valid case is when the 180 day
3347 * timeout has expired, when relax is specified.
3349 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3350 /* it is already deleted - really remove it this time */
3351 talloc_free(tmp_ctx);
3352 return ldb_next_request(module, req);
3355 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3356 "This check is to prevent corruption of the replicated state.",
3357 ldb_dn_get_linearized(old_msg->dn));
3358 return LDB_ERR_UNWILLING_TO_PERFORM;
3361 rdn_name = ldb_dn_get_rdn_name(old_dn);
3362 rdn_value = ldb_dn_get_rdn_val(old_dn);
3363 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3364 talloc_free(tmp_ctx);
3365 return ldb_operr(ldb);
3368 msg = ldb_msg_new(tmp_ctx);
3370 ldb_module_oom(module);
3371 talloc_free(tmp_ctx);
3372 return LDB_ERR_OPERATIONS_ERROR;
3377 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3378 disallow_move_on_delete =
3379 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3380 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3382 /* work out where we will be renaming this object to */
3383 if (!disallow_move_on_delete) {
3384 struct ldb_dn *deleted_objects_dn;
3385 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3386 &deleted_objects_dn);
3389 * We should not move objects if we can't find the
3390 * deleted objects DN. Not moving (or otherwise
3391 * harming) the Deleted Objects DN itself is handled
3394 if (re_delete && (ret != LDB_SUCCESS)) {
3395 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3396 if (new_dn == NULL) {
3397 ldb_module_oom(module);
3398 talloc_free(tmp_ctx);
3399 return LDB_ERR_OPERATIONS_ERROR;
3401 } else if (ret != LDB_SUCCESS) {
3402 /* this is probably an attempted delete on a partition
3403 * that doesn't allow delete operations, such as the
3404 * schema partition */
3405 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3406 ldb_dn_get_linearized(old_dn));
3407 talloc_free(tmp_ctx);
3408 return LDB_ERR_UNWILLING_TO_PERFORM;
3410 new_dn = deleted_objects_dn;
3413 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3414 if (new_dn == NULL) {
3415 ldb_module_oom(module);
3416 talloc_free(tmp_ctx);
3417 return LDB_ERR_OPERATIONS_ERROR;
3421 if (deletion_state == OBJECT_NOT_DELETED) {
3422 /* get the objects GUID from the search we just did */
3423 guid = samdb_result_guid(old_msg, "objectGUID");
3425 /* Add a formatted child */
3426 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3428 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3429 GUID_string(tmp_ctx, &guid));
3431 ldb_asprintf_errstring(ldb, __location__
3432 ": Unable to add a formatted child to dn: %s",
3433 ldb_dn_get_linearized(new_dn));
3434 talloc_free(tmp_ctx);
3435 return LDB_ERR_OPERATIONS_ERROR;
3438 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3439 if (ret != LDB_SUCCESS) {
3440 ldb_asprintf_errstring(ldb, __location__
3441 ": Failed to add isDeleted string to the msg");
3442 talloc_free(tmp_ctx);
3445 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3448 * No matter what has happened with other renames etc, try again to
3449 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3452 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3453 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3455 ldb_asprintf_errstring(ldb, __location__
3456 ": Unable to add a prepare rdn of %s",
3457 ldb_dn_get_linearized(rdn));
3458 talloc_free(tmp_ctx);
3459 return LDB_ERR_OPERATIONS_ERROR;
3461 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3463 retb = ldb_dn_add_child(new_dn, rdn);
3465 ldb_asprintf_errstring(ldb, __location__
3466 ": Unable to add rdn %s to base dn: %s",
3467 ldb_dn_get_linearized(rdn),
3468 ldb_dn_get_linearized(new_dn));
3469 talloc_free(tmp_ctx);
3470 return LDB_ERR_OPERATIONS_ERROR;
3475 now we need to modify the object in the following ways:
3477 - add isDeleted=TRUE
3478 - update rDN and name, with new rDN
3479 - remove linked attributes
3480 - remove objectCategory and sAMAccountType
3481 - remove attribs not on the preserved list
3482 - preserved if in above list, or is rDN
3483 - remove all linked attribs from this object
3484 - remove all links from other objects to this object
3485 - add lastKnownParent
3486 - update replPropertyMetaData?
3488 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3491 if (deletion_state == OBJECT_NOT_DELETED) {
3492 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3493 char *parent_dn_str = NULL;
3495 /* we need the storage form of the parent GUID */
3496 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3498 DSDB_FLAG_NEXT_MODULE |
3499 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3500 DSDB_SEARCH_REVEAL_INTERNALS|
3501 DSDB_SEARCH_SHOW_RECYCLED, req);
3502 if (ret != LDB_SUCCESS) {
3503 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3504 "repmd_delete: Failed to %s %s, "
3505 "because we failed to find it's parent (%s): %s",
3506 re_delete ? "re-delete" : "delete",
3507 ldb_dn_get_linearized(old_dn),
3508 ldb_dn_get_linearized(parent_dn),
3509 ldb_errstring(ldb_module_get_ctx(module)));
3510 talloc_free(tmp_ctx);
3515 * Now we can use the DB version,
3516 * it will have the extended DN info in it
3518 parent_dn = parent_res->msgs[0]->dn;
3519 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3522 if (parent_dn_str == NULL) {
3523 talloc_free(tmp_ctx);
3524 return ldb_module_oom(module);
3527 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3529 if (ret != LDB_SUCCESS) {
3530 ldb_asprintf_errstring(ldb, __location__
3531 ": Failed to add lastKnownParent "
3532 "string when deleting %s",
3533 ldb_dn_get_linearized(old_dn));
3534 talloc_free(tmp_ctx);
3537 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3539 if (next_deletion_state == OBJECT_DELETED) {
3540 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3541 if (ret != LDB_SUCCESS) {
3542 ldb_asprintf_errstring(ldb, __location__
3543 ": Failed to add msDS-LastKnownRDN "
3544 "string when deleting %s",
3545 ldb_dn_get_linearized(old_dn));
3546 talloc_free(tmp_ctx);
3549 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3553 switch (next_deletion_state) {
3555 case OBJECT_RECYCLED:
3556 case OBJECT_TOMBSTONE:
3559 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3560 * describes what must be removed from a tombstone
3563 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3564 * describes what must be removed from a recycled
3570 * we also mark it as recycled, meaning this object can't be
3571 * recovered (we are stripping its attributes).
3572 * This is done only if we have this schema object of course ...
3573 * This behavior is identical to the one of Windows 2008R2 which
3574 * always set the isRecycled attribute, even if the recycle-bin is
3575 * not activated and what ever the forest level is.
3577 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3578 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3579 if (ret != LDB_SUCCESS) {
3580 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3581 ldb_module_oom(module);
3582 talloc_free(tmp_ctx);
3585 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3588 /* work out which of the old attributes we will be removing */
3589 for (i=0; i<old_msg->num_elements; i++) {
3590 const struct dsdb_attribute *sa;
3591 el = &old_msg->elements[i];
3592 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3594 talloc_free(tmp_ctx);
3595 return LDB_ERR_OPERATIONS_ERROR;
3597 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3598 /* don't remove the rDN */
3601 if (sa->linkID && (sa->linkID & 1)) {
3603 we have a backlink in this object
3604 that needs to be removed. We're not
3605 allowed to remove it directly
3606 however, so we instead setup a
3607 modify to delete the corresponding
3610 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3611 if (ret != LDB_SUCCESS) {
3612 const char *old_dn_str
3613 = ldb_dn_get_linearized(old_dn);
3614 ldb_asprintf_errstring(ldb,
3616 ": Failed to remove backlink of "
3617 "%s when deleting %s: %s",
3620 ldb_errstring(ldb));
3621 talloc_free(tmp_ctx);
3622 return LDB_ERR_OPERATIONS_ERROR;
3624 /* now we continue, which means we
3625 won't remove this backlink
3631 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3634 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3639 * Ensure that we tell the modification to vanish any linked
3640 * attributes (not simply mark them as isDeleted = TRUE)
3642 dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
3644 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3645 if (ret != LDB_SUCCESS) {
3646 talloc_free(tmp_ctx);
3647 ldb_module_oom(module);
3654 case OBJECT_DELETED:
3656 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3657 * describes what must be removed from a deleted
3661 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3662 if (ret != LDB_SUCCESS) {
3663 talloc_free(tmp_ctx);
3664 ldb_module_oom(module);
3668 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3669 if (ret != LDB_SUCCESS) {
3670 talloc_free(tmp_ctx);
3671 ldb_module_oom(module);
3681 if (deletion_state == OBJECT_NOT_DELETED) {
3682 const struct dsdb_attribute *sa;
3684 /* work out what the new rdn value is, for updating the
3685 rDN and name fields */
3686 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3687 if (new_rdn_value == NULL) {
3688 talloc_free(tmp_ctx);
3689 return ldb_operr(ldb);
3692 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3694 talloc_free(tmp_ctx);
3695 return LDB_ERR_OPERATIONS_ERROR;
3698 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3700 if (ret != LDB_SUCCESS) {
3701 talloc_free(tmp_ctx);
3704 el->flags = LDB_FLAG_MOD_REPLACE;
3706 el = ldb_msg_find_element(old_msg, "name");
3708 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3709 if (ret != LDB_SUCCESS) {
3710 talloc_free(tmp_ctx);
3713 el->flags = LDB_FLAG_MOD_REPLACE;
3718 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3723 * No matter what has happned with other renames, try again to
3724 * get this to be under the deleted DN.
3726 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3727 /* now rename onto the new DN */
3728 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3729 if (ret != LDB_SUCCESS){
3730 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3731 ldb_dn_get_linearized(old_dn),
3732 ldb_dn_get_linearized(new_dn),
3733 ldb_errstring(ldb)));
3734 talloc_free(tmp_ctx);
3740 ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
3741 if (ret != LDB_SUCCESS) {
3742 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3743 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3744 talloc_free(tmp_ctx);
3748 talloc_free(tmp_ctx);
3750 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3753 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3755 return replmd_delete_internals(module, req, false);
3759 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3764 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3766 int ret = LDB_ERR_OTHER;
3767 /* TODO: do some error mapping */
3769 /* Let the caller know the full WERROR */
3770 ar->objs->error = status;
3776 static struct replPropertyMetaData1 *
3777 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3778 enum drsuapi_DsAttributeId attid)
3781 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3783 for (i = 0; i < rpmd_ctr->count; i++) {
3784 if (rpmd_ctr->array[i].attid == attid) {
3785 return &rpmd_ctr->array[i];
3793 return true if an update is newer than an existing entry
3794 see section 5.11 of MS-ADTS
3796 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3797 const struct GUID *update_invocation_id,
3798 uint32_t current_version,
3799 uint32_t update_version,
3800 NTTIME current_change_time,
3801 NTTIME update_change_time)
3803 if (update_version != current_version) {
3804 return update_version > current_version;
3806 if (update_change_time != current_change_time) {
3807 return update_change_time > current_change_time;
3809 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3812 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3813 struct replPropertyMetaData1 *new_m)
3815 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3816 &new_m->originating_invocation_id,
3819 cur_m->originating_change_time,
3820 new_m->originating_change_time);
3823 static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
3824 struct replPropertyMetaData1 *cur_m,
3825 struct replPropertyMetaData1 *new_m)
3830 * If the new replPropertyMetaData entry for this attribute is
3831 * not provided (this happens in the case where we look for
3832 * ATTID_name, but the name was not changed), then the local
3833 * state is clearly still current, as the remote
3834 * server didn't send it due to being older the high watermark
3837 if (new_m == NULL) {
3841 if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3843 * if we compare equal then do an
3844 * update. This is used when a client
3845 * asks for a FULL_SYNC, and can be
3846 * used to recover a corrupt
3849 * This call is a bit tricky, what we
3850 * are doing it turning the 'is_newer'
3851 * call into a 'not is older' by
3852 * swapping cur_m and new_m, and negating the
3855 cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
3858 cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
3868 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3870 const struct ldb_val *rdn_val;
3871 const char *rdn_name;
3872 struct ldb_dn *new_dn;
3874 rdn_val = ldb_dn_get_rdn_val(dn);
3875 rdn_name = ldb_dn_get_rdn_name(dn);
3876 if (!rdn_val || !rdn_name) {
3880 new_dn = ldb_dn_copy(mem_ctx, dn);
3885 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3889 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3891 ldb_dn_escape_value(new_dn, *rdn_val),
3892 GUID_string(new_dn, guid))) {
3901 perform a modify operation which sets the rDN and name attributes to
3902 their current values. This has the effect of changing these
3903 attributes to have been last updated by the current DC. This is
3904 needed to ensure that renames performed as part of conflict
3905 resolution are propogated to other DCs
3907 static int replmd_name_modify(struct replmd_replicated_request *ar,
3908 struct ldb_request *req, struct ldb_dn *dn)
3910 struct ldb_message *msg;
3911 const char *rdn_name;
3912 const struct ldb_val *rdn_val;
3913 const struct dsdb_attribute *rdn_attr;
3916 msg = ldb_msg_new(req);
3922 rdn_name = ldb_dn_get_rdn_name(dn);
3923 if (rdn_name == NULL) {
3927 /* normalize the rdn attribute name */
3928 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3929 if (rdn_attr == NULL) {
3932 rdn_name = rdn_attr->lDAPDisplayName;
3934 rdn_val = ldb_dn_get_rdn_val(dn);
3935 if (rdn_val == NULL) {
3939 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3942 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3945 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3948 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3952 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3953 if (ret != LDB_SUCCESS) {
3954 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3955 ldb_dn_get_linearized(dn),
3956 ldb_errstring(ldb_module_get_ctx(ar->module))));
3966 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3967 ldb_dn_get_linearized(dn)));
3968 return LDB_ERR_OPERATIONS_ERROR;
3973 callback for conflict DN handling where we have renamed the incoming
3974 record. After renaming it, we need to ensure the change of name and
3975 rDN for the incoming record is seen as an originating update by this DC.
3977 This also handles updating lastKnownParent for entries sent to lostAndFound
3979 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3981 struct replmd_replicated_request *ar =
3982 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3983 struct ldb_dn *conflict_dn = NULL;
3986 if (ares->error != LDB_SUCCESS) {
3987 /* call the normal callback for everything except success */
3988 return replmd_op_callback(req, ares);
3991 switch (req->operation) {
3993 conflict_dn = req->op.add.message->dn;
3996 conflict_dn = req->op.mod.message->dn;
3999 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
4002 /* perform a modify of the rDN and name of the record */
4003 ret = replmd_name_modify(ar, req, conflict_dn);
4004 if (ret != LDB_SUCCESS) {
4006 return replmd_op_callback(req, ares);
4009 if (ar->objs->objects[ar->index_current].last_known_parent) {
4010 struct ldb_message *msg = ldb_msg_new(req);
4012 ldb_module_oom(ar->module);
4013 return LDB_ERR_OPERATIONS_ERROR;
4016 msg->dn = req->op.add.message->dn;
4018 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4019 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
4020 if (ret != LDB_SUCCESS) {
4021 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
4022 ldb_module_oom(ar->module);
4025 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
4027 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
4028 if (ret != LDB_SUCCESS) {
4029 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
4030 ldb_dn_get_linearized(msg->dn),
4031 ldb_errstring(ldb_module_get_ctx(ar->module))));
4037 return replmd_op_callback(req, ares);
4041 callback for replmd_replicated_apply_add()
4042 This copes with the creation of conflict records in the case where
4043 the DN exists, but with a different objectGUID
4045 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))
4047 struct ldb_dn *conflict_dn;
4048 struct replmd_replicated_request *ar =
4049 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4050 struct ldb_result *res;
4051 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4053 const struct ldb_val *omd_value;
4054 struct replPropertyMetaDataBlob omd, *rmd;
4055 enum ndr_err_code ndr_err;
4056 bool rename_incoming_record, rodc;
4057 struct replPropertyMetaData1 *rmd_name, *omd_name;
4058 struct ldb_message *msg;
4059 struct ldb_request *down_req = NULL;
4061 /* call the normal callback for success */
4062 if (ares->error == LDB_SUCCESS) {
4063 return callback(req, ares);
4067 * we have a conflict, and need to decide if we will keep the
4068 * new record or the old record
4071 msg = ar->objs->objects[ar->index_current].msg;
4072 conflict_dn = msg->dn;
4074 /* For failures other than conflicts, fail the whole operation here */
4075 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4076 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
4077 ldb_dn_get_linearized(conflict_dn),
4078 ldb_errstring(ldb_module_get_ctx(ar->module)));
4080 return ldb_module_done(ar->req, NULL, NULL,
4081 LDB_ERR_OPERATIONS_ERROR);
4084 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4085 if (ret != LDB_SUCCESS) {
4086 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)));
4087 return ldb_module_done(ar->req, NULL, NULL,
4088 LDB_ERR_OPERATIONS_ERROR);
4094 * We are on an RODC, or were a GC for this
4095 * partition, so we have to fail this until
4096 * someone who owns the partition sorts it
4099 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4100 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
4101 " - We must fail the operation until a master for this partition resolves the conflict",
4102 ldb_dn_get_linearized(conflict_dn));
4107 * first we need the replPropertyMetaData attribute from the
4108 * local, conflicting record
4110 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
4112 DSDB_FLAG_NEXT_MODULE |
4113 DSDB_SEARCH_SHOW_DELETED |
4114 DSDB_SEARCH_SHOW_RECYCLED, req);
4115 if (ret != LDB_SUCCESS) {
4116 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4117 ldb_dn_get_linearized(conflict_dn)));
4121 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4122 if (omd_value == NULL) {
4123 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4124 ldb_dn_get_linearized(conflict_dn)));
4128 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4129 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4130 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4131 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4132 ldb_dn_get_linearized(conflict_dn)));
4136 rmd = ar->objs->objects[ar->index_current].meta_data;
4139 * we decide which is newer based on the RPMD on the name
4140 * attribute. See [MS-DRSR] ResolveNameConflict.
4142 * We expect omd_name to be present, as this is from a local
4143 * search, but while rmd_name should have been given to us by
4144 * the remote server, if it is missing we just prefer the
4146 * replmd_replPropertyMetaData1_new_should_be_taken()
4148 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4149 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4151 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4152 ldb_dn_get_linearized(conflict_dn)));
4157 * Should we preserve the current record, and so rename the
4158 * incoming record to be a conflict?
4160 rename_incoming_record
4161 = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4162 omd_name, rmd_name);
4164 if (rename_incoming_record) {
4166 struct ldb_dn *new_dn;
4168 guid = samdb_result_guid(msg, "objectGUID");
4169 if (GUID_all_zero(&guid)) {
4170 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
4171 ldb_dn_get_linearized(conflict_dn)));
4174 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4175 if (new_dn == NULL) {
4176 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4177 ldb_dn_get_linearized(conflict_dn)));
4181 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
4182 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4184 /* re-submit the request, but with the new DN */
4185 callback = replmd_op_name_modify_callback;
4188 /* we are renaming the existing record */
4190 struct ldb_dn *new_dn;
4192 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4193 if (GUID_all_zero(&guid)) {
4194 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4195 ldb_dn_get_linearized(conflict_dn)));
4199 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
4200 if (new_dn == NULL) {
4201 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4202 ldb_dn_get_linearized(conflict_dn)));
4206 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4207 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4209 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4210 DSDB_FLAG_OWN_MODULE, req);
4211 if (ret != LDB_SUCCESS) {
4212 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4213 ldb_dn_get_linearized(conflict_dn),
4214 ldb_dn_get_linearized(new_dn),
4215 ldb_errstring(ldb_module_get_ctx(ar->module))));
4220 * now we need to ensure that the rename is seen as an
4221 * originating update. We do that with a modify.
4223 ret = replmd_name_modify(ar, req, new_dn);
4224 if (ret != LDB_SUCCESS) {
4228 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
4229 ldb_dn_get_linearized(req->op.add.message->dn)));
4232 ret = ldb_build_add_req(&down_req,
4233 ldb_module_get_ctx(ar->module),
4240 if (ret != LDB_SUCCESS) {
4243 LDB_REQ_SET_LOCATION(down_req);
4245 /* current partition control needed by "repmd_op_callback" */
4246 ret = ldb_request_add_control(down_req,
4247 DSDB_CONTROL_CURRENT_PARTITION_OID,
4249 if (ret != LDB_SUCCESS) {
4250 return replmd_replicated_request_error(ar, ret);
4253 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4254 /* this tells the partition module to make it a
4255 partial replica if creating an NC */
4256 ret = ldb_request_add_control(down_req,
4257 DSDB_CONTROL_PARTIAL_REPLICA,
4259 if (ret != LDB_SUCCESS) {
4260 return replmd_replicated_request_error(ar, ret);
4265 * Finally we re-run the add, otherwise the new record won't
4266 * exist, as we are here because of that exact failure!
4268 return ldb_next_request(ar->module, down_req);
4271 /* on failure make the caller get the error. This means
4272 * replication will stop with an error, but there is not much
4275 return ldb_module_done(ar->req, NULL, NULL,
4280 callback for replmd_replicated_apply_add()
4281 This copes with the creation of conflict records in the case where
4282 the DN exists, but with a different objectGUID
4284 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
4286 struct replmd_replicated_request *ar =
4287 talloc_get_type_abort(req->context, struct replmd_replicated_request);
4289 if (ar->objs->objects[ar->index_current].last_known_parent) {
4290 /* This is like a conflict DN, where we put the object in LostAndFound
4291 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
4292 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
4295 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
4299 this is called when a new object comes in over DRS
4301 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4303 struct ldb_context *ldb;
4304 struct ldb_request *change_req;
4305 enum ndr_err_code ndr_err;
4306 struct ldb_message *msg;
4307 struct replPropertyMetaDataBlob *md;
4308 struct ldb_val md_value;
4311 bool remote_isDeleted = false;
4314 time_t t = time(NULL);
4315 const struct ldb_val *rdn_val;
4316 struct replmd_private *replmd_private =
4317 talloc_get_type(ldb_module_get_private(ar->module),
4318 struct replmd_private);
4319 unix_to_nt_time(&now, t);
4321 ldb = ldb_module_get_ctx(ar->module);
4322 msg = ar->objs->objects[ar->index_current].msg;
4323 md = ar->objs->objects[ar->index_current].meta_data;
4324 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4326 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4327 if (ret != LDB_SUCCESS) {
4328 return replmd_replicated_request_error(ar, ret);
4331 ret = dsdb_msg_add_guid(msg,
4332 &ar->objs->objects[ar->index_current].object_guid,
4334 if (ret != LDB_SUCCESS) {
4335 return replmd_replicated_request_error(ar, ret);
4338 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4339 if (ret != LDB_SUCCESS) {
4340 return replmd_replicated_request_error(ar, ret);
4343 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4344 if (ret != LDB_SUCCESS) {
4345 return replmd_replicated_request_error(ar, ret);
4348 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4349 if (ret != LDB_SUCCESS) {
4350 return replmd_replicated_request_error(ar, ret);
4353 /* remove any message elements that have zero values */
4354 for (i=0; i<msg->num_elements; i++) {
4355 struct ldb_message_element *el = &msg->elements[i];
4357 if (el->num_values == 0) {
4358 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4359 ldb_asprintf_errstring(ldb, __location__
4360 ": empty objectClass sent on %s, aborting replication\n",
4361 ldb_dn_get_linearized(msg->dn));
4362 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4365 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4367 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4368 msg->num_elements--;
4375 struct GUID_txt_buf guid_txt;
4377 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4378 DEBUG(4, ("DRS replication add message of %s:\n%s\n",
4379 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4384 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4385 "isDeleted", false);
4388 * the meta data array is already sorted by the caller, except
4389 * for the RDN, which needs to be added.
4393 rdn_val = ldb_dn_get_rdn_val(msg->dn);
4394 ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
4395 md, ar, now, is_schema_nc);
4396 if (ret != LDB_SUCCESS) {
4397 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4398 return replmd_replicated_request_error(ar, ret);
4401 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
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 for (i=0; i < md->ctr.ctr1.count; i++) {
4408 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4410 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4411 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4412 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4413 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4414 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4416 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4417 if (ret != LDB_SUCCESS) {
4418 return replmd_replicated_request_error(ar, ret);
4421 replmd_ldb_message_sort(msg, ar->schema);
4423 if (!remote_isDeleted) {
4424 ret = dsdb_module_schedule_sd_propagation(ar->module,
4425 ar->objs->partition_dn,
4427 if (ret != LDB_SUCCESS) {
4428 return replmd_replicated_request_error(ar, ret);
4432 ar->isDeleted = remote_isDeleted;
4434 ret = ldb_build_add_req(&change_req,
4440 replmd_op_add_callback,
4442 LDB_REQ_SET_LOCATION(change_req);
4443 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4445 /* current partition control needed by "repmd_op_callback" */
4446 ret = ldb_request_add_control(change_req,
4447 DSDB_CONTROL_CURRENT_PARTITION_OID,
4449 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4451 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4452 /* this tells the partition module to make it a
4453 partial replica if creating an NC */
4454 ret = ldb_request_add_control(change_req,
4455 DSDB_CONTROL_PARTIAL_REPLICA,
4457 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4460 return ldb_next_request(ar->module, change_req);
4463 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4464 struct ldb_reply *ares)
4466 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4467 struct replmd_replicated_request);
4471 return ldb_module_done(ar->req, NULL, NULL,
4472 LDB_ERR_OPERATIONS_ERROR);
4476 * The error NO_SUCH_OBJECT is not expected, unless the search
4477 * base is the partition DN, and that case doesn't happen here
4478 * because then we wouldn't get a parent_guid_value in any
4481 if (ares->error != LDB_SUCCESS) {
4482 return ldb_module_done(ar->req, ares->controls,
4483 ares->response, ares->error);
4486 switch (ares->type) {
4487 case LDB_REPLY_ENTRY:
4489 struct ldb_message *parent_msg = ares->message;
4490 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4491 struct ldb_dn *parent_dn;
4494 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4495 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4496 /* Per MS-DRSR 4.1.10.6.10
4497 * FindBestParentObject we need to move this
4498 * new object under a deleted object to
4500 struct ldb_dn *nc_root;
4502 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4503 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4504 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4505 "No suitable NC root found for %s. "
4506 "We need to move this object because parent object %s "
4507 "is deleted, but this object is not.",
4508 ldb_dn_get_linearized(msg->dn),
4509 ldb_dn_get_linearized(parent_msg->dn));
4510 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4511 } else if (ret != LDB_SUCCESS) {
4512 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4513 "Unable to find NC root for %s: %s. "
4514 "We need to move this object because parent object %s "
4515 "is deleted, but this object is not.",
4516 ldb_dn_get_linearized(msg->dn),
4517 ldb_errstring(ldb_module_get_ctx(ar->module)),
4518 ldb_dn_get_linearized(parent_msg->dn));
4519 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4522 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4524 DS_GUID_LOSTANDFOUND_CONTAINER,
4526 if (ret != LDB_SUCCESS) {
4527 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4528 "Unable to find LostAndFound Container for %s "
4529 "in partition %s: %s. "
4530 "We need to move this object because parent object %s "
4531 "is deleted, but this object is not.",
4532 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4533 ldb_errstring(ldb_module_get_ctx(ar->module)),
4534 ldb_dn_get_linearized(parent_msg->dn));
4535 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4537 ar->objs->objects[ar->index_current].last_known_parent
4538 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4542 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4545 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4547 comp_num = ldb_dn_get_comp_num(msg->dn);
4549 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4551 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4554 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4556 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4560 case LDB_REPLY_REFERRAL:
4561 /* we ignore referrals */
4564 case LDB_REPLY_DONE:
4566 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4567 struct GUID_txt_buf str_buf;
4568 if (ar->search_msg != NULL) {
4569 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4570 "No parent with GUID %s found for object locally known as %s",
4571 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4572 ldb_dn_get_linearized(ar->search_msg->dn));
4574 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4575 "No parent with GUID %s found for object remotely known as %s",
4576 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4577 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4581 * This error code is really important, as it
4582 * is the flag back to the callers to retry
4583 * this with DRSUAPI_DRS_GET_ANC, and so get
4584 * the parent objects before the child
4587 return ldb_module_done(ar->req, NULL, NULL,
4588 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4591 if (ar->search_msg != NULL) {
4592 ret = replmd_replicated_apply_merge(ar);
4594 ret = replmd_replicated_apply_add(ar);
4596 if (ret != LDB_SUCCESS) {
4597 return ldb_module_done(ar->req, NULL, NULL, ret);
4606 * Look for the parent object, so we put the new object in the right
4607 * place This is akin to NameObject in MS-DRSR - this routine and the
4608 * callbacks find the right parent name, and correct name for this
4612 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4614 struct ldb_context *ldb;
4618 struct ldb_request *search_req;
4619 static const char *attrs[] = {"isDeleted", NULL};
4620 struct GUID_txt_buf guid_str_buf;
4622 ldb = ldb_module_get_ctx(ar->module);
4624 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4625 if (ar->search_msg != NULL) {
4626 return replmd_replicated_apply_merge(ar);
4628 return replmd_replicated_apply_add(ar);
4632 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4635 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4636 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4638 ret = ldb_build_search_req(&search_req,
4641 ar->objs->partition_dn,
4647 replmd_replicated_apply_search_for_parent_callback,
4649 LDB_REQ_SET_LOCATION(search_req);
4651 ret = dsdb_request_add_controls(search_req,
4652 DSDB_SEARCH_SHOW_RECYCLED|
4653 DSDB_SEARCH_SHOW_DELETED|
4654 DSDB_SEARCH_SHOW_EXTENDED_DN);
4655 if (ret != LDB_SUCCESS) {
4659 return ldb_next_request(ar->module, search_req);
4663 handle renames that come in over DRS replication
4665 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4666 struct ldb_message *msg,
4667 struct ldb_request *parent,
4671 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4672 struct ldb_result *res;
4673 struct ldb_dn *conflict_dn;
4674 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
4675 const struct ldb_val *omd_value;
4676 struct replPropertyMetaDataBlob omd, *rmd;
4677 enum ndr_err_code ndr_err;
4678 bool rename_incoming_record, rodc;
4679 struct replPropertyMetaData1 *rmd_name, *omd_name;
4680 struct ldb_dn *new_dn;
4683 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4684 ldb_dn_get_linearized(ar->search_msg->dn),
4685 ldb_dn_get_linearized(msg->dn)));
4688 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4689 DSDB_FLAG_NEXT_MODULE, ar->req);
4690 if (ret == LDB_SUCCESS) {
4691 talloc_free(tmp_ctx);
4696 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
4697 talloc_free(tmp_ctx);
4698 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
4699 ldb_dn_get_linearized(ar->search_msg->dn),
4700 ldb_dn_get_linearized(msg->dn),
4701 ldb_errstring(ldb_module_get_ctx(ar->module)));
4705 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
4706 if (ret != LDB_SUCCESS) {
4707 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4708 "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
4709 ldb_errstring(ldb_module_get_ctx(ar->module)));
4710 return LDB_ERR_OPERATIONS_ERROR;
4713 * we have a conflict, and need to decide if we will keep the
4714 * new record or the old record
4717 conflict_dn = msg->dn;
4721 * We are on an RODC, or were a GC for this
4722 * partition, so we have to fail this until
4723 * someone who owns the partition sorts it
4726 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4727 "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n"
4728 " - We must fail the operation until a master for this partition resolves the conflict",
4729 ldb_dn_get_linearized(conflict_dn));
4734 * first we need the replPropertyMetaData attribute from the
4737 ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
4739 DSDB_FLAG_NEXT_MODULE |
4740 DSDB_SEARCH_SHOW_DELETED |
4741 DSDB_SEARCH_SHOW_RECYCLED, ar->req);
4742 if (ret != LDB_SUCCESS) {
4743 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
4744 ldb_dn_get_linearized(conflict_dn)));
4748 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
4749 if (omd_value == NULL) {
4750 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
4751 ldb_dn_get_linearized(conflict_dn)));
4755 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
4756 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4757 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4758 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
4759 ldb_dn_get_linearized(conflict_dn)));
4763 rmd = ar->objs->objects[ar->index_current].meta_data;
4766 * we decide which is newer based on the RPMD on the name
4767 * attribute. See [MS-DRSR] ResolveNameConflict.
4769 * We expect omd_name to be present, as this is from a local
4770 * search, but while rmd_name should have been given to us by
4771 * the remote server, if it is missing we just prefer the
4773 * replmd_replPropertyMetaData1_new_should_be_taken()
4775 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4776 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4778 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
4779 ldb_dn_get_linearized(conflict_dn)));
4784 * Should we preserve the current record, and so rename the
4785 * incoming record to be a conflict?
4787 rename_incoming_record =
4788 !replmd_replPropertyMetaData1_new_should_be_taken(
4789 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
4790 omd_name, rmd_name);
4792 if (rename_incoming_record) {
4794 new_dn = replmd_conflict_dn(msg, msg->dn,
4795 &ar->objs->objects[ar->index_current].object_guid);
4796 if (new_dn == NULL) {
4797 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4798 "Failed to form conflict DN for %s\n",
4799 ldb_dn_get_linearized(msg->dn));
4801 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4804 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4805 DSDB_FLAG_NEXT_MODULE, ar->req);
4806 if (ret != LDB_SUCCESS) {
4807 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4808 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4809 ldb_dn_get_linearized(conflict_dn),
4810 ldb_dn_get_linearized(ar->search_msg->dn),
4811 ldb_dn_get_linearized(new_dn),
4812 ldb_errstring(ldb_module_get_ctx(ar->module)));
4813 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4821 /* we are renaming the existing record */
4823 guid = samdb_result_guid(res->msgs[0], "objectGUID");
4824 if (GUID_all_zero(&guid)) {
4825 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
4826 ldb_dn_get_linearized(conflict_dn)));
4830 new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
4831 if (new_dn == NULL) {
4832 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
4833 ldb_dn_get_linearized(conflict_dn)));
4837 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
4838 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
4840 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
4841 DSDB_FLAG_OWN_MODULE, ar->req);
4842 if (ret != LDB_SUCCESS) {
4843 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
4844 ldb_dn_get_linearized(conflict_dn),
4845 ldb_dn_get_linearized(new_dn),
4846 ldb_errstring(ldb_module_get_ctx(ar->module))));
4851 * now we need to ensure that the rename is seen as an
4852 * originating update. We do that with a modify.
4854 ret = replmd_name_modify(ar, ar->req, new_dn);
4855 if (ret != LDB_SUCCESS) {
4859 DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
4860 ldb_dn_get_linearized(ar->search_msg->dn),
4861 ldb_dn_get_linearized(msg->dn)));
4864 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
4865 DSDB_FLAG_NEXT_MODULE, ar->req);
4866 if (ret != LDB_SUCCESS) {
4867 DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
4868 ldb_dn_get_linearized(ar->search_msg->dn),
4869 ldb_dn_get_linearized(msg->dn),
4870 ldb_errstring(ldb_module_get_ctx(ar->module))));
4876 * On failure make the caller get the error
4877 * This means replication will stop with an error,
4878 * but there is not much else we can do. In the
4879 * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
4883 talloc_free(tmp_ctx);
4888 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4890 struct ldb_context *ldb;
4891 struct ldb_request *change_req;
4892 enum ndr_err_code ndr_err;
4893 struct ldb_message *msg;
4894 struct replPropertyMetaDataBlob *rmd;
4895 struct replPropertyMetaDataBlob omd;
4896 const struct ldb_val *omd_value;
4897 struct replPropertyMetaDataBlob nmd;
4898 struct ldb_val nmd_value;
4899 struct GUID remote_parent_guid;
4902 unsigned int removed_attrs = 0;
4904 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4905 bool isDeleted = false;
4906 bool local_isDeleted = false;
4907 bool remote_isDeleted = false;
4908 bool take_remote_isDeleted = false;
4909 bool sd_updated = false;
4910 bool renamed = false;
4911 bool is_schema_nc = false;
4913 const struct ldb_val *old_rdn, *new_rdn;
4914 struct replmd_private *replmd_private =
4915 talloc_get_type(ldb_module_get_private(ar->module),
4916 struct replmd_private);
4918 time_t t = time(NULL);
4919 unix_to_nt_time(&now, t);
4921 ldb = ldb_module_get_ctx(ar->module);
4922 msg = ar->objs->objects[ar->index_current].msg;
4924 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
4926 rmd = ar->objs->objects[ar->index_current].meta_data;
4930 /* find existing meta data */
4931 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4933 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4934 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4935 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4936 nt_status = ndr_map_error2ntstatus(ndr_err);
4937 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4940 if (omd.version != 1) {
4941 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4946 struct GUID_txt_buf guid_txt;
4948 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4949 DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
4952 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
4954 ndr_print_struct_string(s,
4955 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4956 "existing replPropertyMetaData",
4958 ndr_print_struct_string(s,
4959 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
4960 "incoming replPropertyMetaData",
4965 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4966 "isDeleted", false);
4967 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4968 "isDeleted", false);
4971 * Fill in the remote_parent_guid with the GUID or an all-zero
4974 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4975 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4977 remote_parent_guid = GUID_zero();
4981 * To ensure we follow a complex rename chain around, we have
4982 * to confirm that the DN is the same (mostly to confirm the
4983 * RDN) and the parentGUID is the same.
4985 * This ensures we keep things under the correct parent, which
4986 * replmd_replicated_handle_rename() will do.
4989 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
4990 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
4994 * handle renames, even just by case that come in over
4995 * DRS. Changes in the parent DN don't hit us here,
4996 * because the search for a parent will clean up those
4999 * We also have already filtered out the case where
5000 * the peer has an older name to what we have (see
5001 * replmd_replicated_apply_search_callback())
5003 ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
5006 if (ret != LDB_SUCCESS) {
5007 ldb_debug(ldb, LDB_DEBUG_FATAL,
5008 "replmd_replicated_request rename %s => %s failed - %s\n",
5009 ldb_dn_get_linearized(ar->search_msg->dn),
5010 ldb_dn_get_linearized(msg->dn),
5011 ldb_errstring(ldb));
5012 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5015 if (renamed == true) {
5017 * Set the callback to one that will fix up the name
5018 * metadata on the new conflict DN
5020 callback = replmd_op_name_modify_callback;
5025 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
5026 nmd.ctr.ctr1.array = talloc_array(ar,
5027 struct replPropertyMetaData1,
5028 nmd.ctr.ctr1.count);
5029 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5031 /* first copy the old meta data */
5032 for (i=0; i < omd.ctr.ctr1.count; i++) {
5033 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
5038 /* now merge in the new meta data */
5039 for (i=0; i < rmd->ctr.ctr1.count; i++) {
5042 for (j=0; j < ni; j++) {
5045 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
5049 cmp = replmd_replPropertyMetaData1_new_should_be_taken(
5050 ar->objs->dsdb_repl_flags,
5051 &nmd.ctr.ctr1.array[j],
5052 &rmd->ctr.ctr1.array[i]);
5054 /* replace the entry */
5055 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
5056 if (ar->seq_num == 0) {
5057 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5058 if (ret != LDB_SUCCESS) {
5059 return replmd_replicated_request_error(ar, ret);
5062 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
5063 switch (nmd.ctr.ctr1.array[j].attid) {
5064 case DRSUAPI_ATTID_ntSecurityDescriptor:
5067 case DRSUAPI_ATTID_isDeleted:
5068 take_remote_isDeleted = true;
5077 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
5078 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
5079 msg->elements[i-removed_attrs].name,
5080 ldb_dn_get_linearized(msg->dn),
5081 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
5084 /* we don't want to apply this change so remove the attribute */
5085 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
5092 if (found) continue;
5094 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
5095 if (ar->seq_num == 0) {
5096 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5097 if (ret != LDB_SUCCESS) {
5098 return replmd_replicated_request_error(ar, ret);
5101 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
5102 switch (nmd.ctr.ctr1.array[ni].attid) {
5103 case DRSUAPI_ATTID_ntSecurityDescriptor:
5106 case DRSUAPI_ATTID_isDeleted:
5107 take_remote_isDeleted = true;
5116 * finally correct the size of the meta_data array
5118 nmd.ctr.ctr1.count = ni;
5120 new_rdn = ldb_dn_get_rdn_val(msg->dn);
5121 old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
5124 ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
5125 &nmd, ar, now, is_schema_nc);
5126 if (ret != LDB_SUCCESS) {
5127 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5128 return replmd_replicated_request_error(ar, ret);
5132 * sort the new meta data array
5134 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
5135 if (ret != LDB_SUCCESS) {
5136 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
5141 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
5144 * This also controls SD propagation below
5146 if (take_remote_isDeleted) {
5147 isDeleted = remote_isDeleted;
5149 isDeleted = local_isDeleted;
5152 ar->isDeleted = isDeleted;
5155 * check if some replicated attributes left, otherwise skip the ldb_modify() call
5157 if (msg->num_elements == 0) {
5158 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
5161 return replmd_replicated_apply_isDeleted(ar);
5164 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
5165 ar->index_current, msg->num_elements);
5171 if (sd_updated && !isDeleted) {
5172 ret = dsdb_module_schedule_sd_propagation(ar->module,
5173 ar->objs->partition_dn,
5175 if (ret != LDB_SUCCESS) {
5176 return ldb_operr(ldb);
5180 /* create the meta data value */
5181 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
5182 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5183 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5184 nt_status = ndr_map_error2ntstatus(ndr_err);
5185 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5189 * when we know that we'll modify the record, add the whenChanged, uSNChanged
5190 * and replPopertyMetaData attributes
5192 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5193 if (ret != LDB_SUCCESS) {
5194 return replmd_replicated_request_error(ar, ret);
5196 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5197 if (ret != LDB_SUCCESS) {
5198 return replmd_replicated_request_error(ar, ret);
5200 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
5201 if (ret != LDB_SUCCESS) {
5202 return replmd_replicated_request_error(ar, ret);
5205 replmd_ldb_message_sort(msg, ar->schema);
5207 /* we want to replace the old values */
5208 for (i=0; i < msg->num_elements; i++) {
5209 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
5210 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5211 if (msg->elements[i].num_values == 0) {
5212 ldb_asprintf_errstring(ldb, __location__
5213 ": objectClass removed on %s, aborting replication\n",
5214 ldb_dn_get_linearized(msg->dn));
5215 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5221 struct GUID_txt_buf guid_txt;
5223 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5224 DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
5225 GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5230 ret = ldb_build_mod_req(&change_req,
5238 LDB_REQ_SET_LOCATION(change_req);
5239 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5241 /* current partition control needed by "repmd_op_callback" */
5242 ret = ldb_request_add_control(change_req,
5243 DSDB_CONTROL_CURRENT_PARTITION_OID,
5245 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5247 return ldb_next_request(ar->module, change_req);
5250 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
5251 struct ldb_reply *ares)
5253 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5254 struct replmd_replicated_request);
5258 return ldb_module_done(ar->req, NULL, NULL,
5259 LDB_ERR_OPERATIONS_ERROR);
5261 if (ares->error != LDB_SUCCESS &&
5262 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5263 return ldb_module_done(ar->req, ares->controls,
5264 ares->response, ares->error);
5267 switch (ares->type) {
5268 case LDB_REPLY_ENTRY:
5269 ar->search_msg = talloc_steal(ar, ares->message);
5272 case LDB_REPLY_REFERRAL:
5273 /* we ignore referrals */
5276 case LDB_REPLY_DONE:
5278 struct replPropertyMetaData1 *md_remote;
5279 struct replPropertyMetaData1 *md_local;
5281 struct replPropertyMetaDataBlob omd;
5282 const struct ldb_val *omd_value;
5283 struct replPropertyMetaDataBlob *rmd;
5284 struct ldb_message *msg;
5286 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
5287 ar->objs->objects[ar->index_current].last_known_parent = NULL;
5290 * This is the ADD case, find the appropriate parent,
5291 * as this object doesn't exist locally:
5293 if (ar->search_msg == NULL) {
5294 ret = replmd_replicated_apply_search_for_parent(ar);
5295 if (ret != LDB_SUCCESS) {
5296 return ldb_module_done(ar->req, NULL, NULL, ret);
5303 * Otherwise, in the MERGE case, work out if we are
5304 * attempting a rename, and if so find the parent the
5305 * newly renamed object wants to belong under (which
5306 * may not be the parent in it's attached string DN
5308 rmd = ar->objs->objects[ar->index_current].meta_data;
5312 /* find existing meta data */
5313 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
5315 enum ndr_err_code ndr_err;
5316 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
5317 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5318 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5319 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5320 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5323 if (omd.version != 1) {
5324 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5328 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
5330 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
5331 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
5332 && GUID_all_zero(&ar->local_parent_guid)) {
5333 DEBUG(0, ("Refusing to replicate new version of %s "
5334 "as local object has an all-zero parentGUID attribute, "
5335 "despite not being an NC root\n",
5336 ldb_dn_get_linearized(ar->search_msg->dn)));
5337 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5341 * now we need to check for double renames. We could have a
5342 * local rename pending which our replication partner hasn't
5343 * received yet. We choose which one wins by looking at the
5344 * attribute stamps on the two objects, the newer one wins.
5346 * This also simply applies the correct algorithms for
5347 * determining if a change was made to name at all, or
5348 * if the object has just been renamed under the same
5351 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
5352 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
5354 DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
5355 ldb_dn_get_linearized(ar->search_msg->dn)));
5356 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
5360 * if there is no name attribute given then we have to assume the
5361 * object we've received has the older name
5363 if (replmd_replPropertyMetaData1_new_should_be_taken(
5364 ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
5365 md_local, md_remote)) {
5366 struct GUID_txt_buf p_guid_local;
5367 struct GUID_txt_buf p_guid_remote;
5368 msg = ar->objs->objects[ar->index_current].msg;
5370 /* Merge on the existing object, with rename */
5372 DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
5373 "as incoming object changing to %s under %s\n",
5374 ldb_dn_get_linearized(ar->search_msg->dn),
5375 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5376 ldb_dn_get_linearized(msg->dn),
5377 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5379 ret = replmd_replicated_apply_search_for_parent(ar);
5381 struct GUID_txt_buf p_guid_local;
5382 struct GUID_txt_buf p_guid_remote;
5383 msg = ar->objs->objects[ar->index_current].msg;
5386 * Merge on the existing object, force no
5387 * rename (code below just to explain why in
5391 if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
5392 ldb_dn_get_linearized(msg->dn)) == 0) {
5393 if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
5394 GUID_equal(&ar->local_parent_guid,
5395 ar->objs->objects[ar->index_current].parent_guid)
5397 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5398 "despite incoming object changing parent to %s\n",
5399 ldb_dn_get_linearized(ar->search_msg->dn),
5400 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5401 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5405 DEBUG(4,(__location__ ": Keeping object %s at under %s "
5406 " and rejecting older rename to %s under %s\n",
5407 ldb_dn_get_linearized(ar->search_msg->dn),
5408 GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
5409 ldb_dn_get_linearized(msg->dn),
5410 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5414 * This assignment ensures that the strcmp()
5415 * and GUID_equal() calls in
5416 * replmd_replicated_apply_merge() avoids the
5419 ar->objs->objects[ar->index_current].parent_guid =
5420 &ar->local_parent_guid;
5422 msg->dn = ar->search_msg->dn;
5423 ret = replmd_replicated_apply_merge(ar);
5425 if (ret != LDB_SUCCESS) {
5426 return ldb_module_done(ar->req, NULL, NULL, ret);
5435 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
5437 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
5439 struct ldb_context *ldb;
5443 struct ldb_request *search_req;
5444 static const char *attrs[] = { "repsFrom", "replUpToDateVector",
5445 "parentGUID", "instanceType",
5446 "replPropertyMetaData", "nTSecurityDescriptor",
5447 "isDeleted", NULL };
5448 struct GUID_txt_buf guid_str_buf;
5450 if (ar->index_current >= ar->objs->num_objects) {
5451 /* done with it, go to next stage */
5452 return replmd_replicated_uptodate_vector(ar);
5455 ldb = ldb_module_get_ctx(ar->module);
5456 ar->search_msg = NULL;
5457 ar->isDeleted = false;
5459 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
5462 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5463 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5465 ret = ldb_build_search_req(&search_req,
5468 ar->objs->partition_dn,
5474 replmd_replicated_apply_search_callback,
5476 LDB_REQ_SET_LOCATION(search_req);
5478 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
5480 if (ret != LDB_SUCCESS) {
5484 return ldb_next_request(ar->module, search_req);
5488 * This is essentially a wrapper for replmd_replicated_apply_next()
5490 * This is needed to ensure that both codepaths call this handler.
5492 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
5494 struct ldb_dn *deleted_objects_dn;
5495 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5496 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
5497 &deleted_objects_dn);
5498 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
5500 * Do a delete here again, so that if there is
5501 * anything local that conflicts with this
5502 * object being deleted, it is removed. This
5503 * includes links. See MS-DRSR 4.1.10.6.9
5506 * If the object is already deleted, and there
5507 * is no more work required, it doesn't do
5511 /* This has been updated to point to the DN we eventually did the modify on */
5513 struct ldb_request *del_req;
5514 struct ldb_result *res;
5516 TALLOC_CTX *tmp_ctx = talloc_new(ar);
5518 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5522 res = talloc_zero(tmp_ctx, struct ldb_result);
5524 ret = ldb_oom(ldb_module_get_ctx(ar->module));
5525 talloc_free(tmp_ctx);
5529 /* Build a delete request, which hopefully will artually turn into nothing */
5530 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5534 ldb_modify_default_callback,
5536 LDB_REQ_SET_LOCATION(del_req);
5537 if (ret != LDB_SUCCESS) {
5538 talloc_free(tmp_ctx);
5543 * This is the guts of the call, call back
5544 * into our delete code, but setting the
5545 * re_delete flag so we delete anything that
5546 * shouldn't be there on a deleted or recycled
5549 ret = replmd_delete_internals(ar->module, del_req, true);
5550 if (ret == LDB_SUCCESS) {
5551 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5554 talloc_free(tmp_ctx);
5555 if (ret != LDB_SUCCESS) {
5560 ar->index_current++;
5561 return replmd_replicated_apply_next(ar);
5564 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5565 struct ldb_reply *ares)
5567 struct ldb_context *ldb;
5568 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5569 struct replmd_replicated_request);
5570 ldb = ldb_module_get_ctx(ar->module);
5573 return ldb_module_done(ar->req, NULL, NULL,
5574 LDB_ERR_OPERATIONS_ERROR);
5576 if (ares->error != LDB_SUCCESS) {
5577 return ldb_module_done(ar->req, ares->controls,
5578 ares->response, ares->error);
5581 if (ares->type != LDB_REPLY_DONE) {
5582 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5583 return ldb_module_done(ar->req, NULL, NULL,
5584 LDB_ERR_OPERATIONS_ERROR);
5589 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5592 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5594 struct ldb_context *ldb;
5595 struct ldb_request *change_req;
5596 enum ndr_err_code ndr_err;
5597 struct ldb_message *msg;
5598 struct replUpToDateVectorBlob ouv;
5599 const struct ldb_val *ouv_value;
5600 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5601 struct replUpToDateVectorBlob nuv;
5602 struct ldb_val nuv_value;
5603 struct ldb_message_element *nuv_el = NULL;
5604 struct ldb_message_element *orf_el = NULL;
5605 struct repsFromToBlob nrf;
5606 struct ldb_val *nrf_value = NULL;
5607 struct ldb_message_element *nrf_el = NULL;
5611 time_t t = time(NULL);
5614 uint32_t instanceType;
5616 ldb = ldb_module_get_ctx(ar->module);
5617 ruv = ar->objs->uptodateness_vector;
5623 unix_to_nt_time(&now, t);
5625 if (ar->search_msg == NULL) {
5626 /* this happens for a REPL_OBJ call where we are
5627 creating the target object by replicating it. The
5628 subdomain join code does this for the partition DN
5630 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5631 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5634 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5635 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5636 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5637 ldb_dn_get_linearized(ar->search_msg->dn)));
5638 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5642 * first create the new replUpToDateVector
5644 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5646 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5647 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5648 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5649 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5650 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5653 if (ouv.version != 2) {
5654 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5659 * the new uptodateness vector will at least
5660 * contain 1 entry, one for the source_dsa
5662 * plus optional values from our old vector and the one from the source_dsa
5664 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5665 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5666 nuv.ctr.ctr2.cursors = talloc_array(ar,
5667 struct drsuapi_DsReplicaCursor2,
5668 nuv.ctr.ctr2.count);
5669 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5671 /* first copy the old vector */
5672 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5673 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5677 /* merge in the source_dsa vector is available */
5678 for (i=0; (ruv && i < ruv->count); i++) {
5681 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5682 &ar->our_invocation_id)) {
5686 for (j=0; j < ni; j++) {
5687 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5688 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5694 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5695 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5700 if (found) continue;
5702 /* if it's not there yet, add it */
5703 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5708 * finally correct the size of the cursors array
5710 nuv.ctr.ctr2.count = ni;
5715 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5718 * create the change ldb_message
5720 msg = ldb_msg_new(ar);
5721 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5722 msg->dn = ar->search_msg->dn;
5724 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5725 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5726 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5727 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5728 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5730 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5731 if (ret != LDB_SUCCESS) {
5732 return replmd_replicated_request_error(ar, ret);
5734 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5737 * now create the new repsFrom value from the given repsFromTo1 structure
5741 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5742 nrf.ctr.ctr1.last_attempt = now;
5743 nrf.ctr.ctr1.last_success = now;
5744 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5747 * first see if we already have a repsFrom value for the current source dsa
5748 * if so we'll later replace this value
5750 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5752 for (i=0; i < orf_el->num_values; i++) {
5753 struct repsFromToBlob *trf;
5755 trf = talloc(ar, struct repsFromToBlob);
5756 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5758 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5759 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5760 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5761 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5762 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5765 if (trf->version != 1) {
5766 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5770 * we compare the source dsa objectGUID not the invocation_id
5771 * because we want only one repsFrom value per source dsa
5772 * and when the invocation_id of the source dsa has changed we don't need
5773 * the old repsFrom with the old invocation_id
5775 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5776 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5782 nrf_value = &orf_el->values[i];
5787 * copy over all old values to the new ldb_message
5789 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5790 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5795 * if we haven't found an old repsFrom value for the current source dsa
5796 * we'll add a new value
5799 struct ldb_val zero_value;
5800 ZERO_STRUCT(zero_value);
5801 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5802 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5804 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5807 /* we now fill the value which is already attached to ldb_message */
5808 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5810 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5811 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5812 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5813 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5817 * the ldb_message_element for the attribute, has all the old values and the new one
5818 * so we'll replace the whole attribute with all values
5820 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5822 if (CHECK_DEBUGLVL(4)) {
5823 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5824 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5828 /* prepare the ldb_modify() request */
5829 ret = ldb_build_mod_req(&change_req,
5835 replmd_replicated_uptodate_modify_callback,
5837 LDB_REQ_SET_LOCATION(change_req);
5838 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5840 return ldb_next_request(ar->module, change_req);
5843 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5844 struct ldb_reply *ares)
5846 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5847 struct replmd_replicated_request);
5851 return ldb_module_done(ar->req, NULL, NULL,
5852 LDB_ERR_OPERATIONS_ERROR);
5854 if (ares->error != LDB_SUCCESS &&
5855 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5856 return ldb_module_done(ar->req, ares->controls,
5857 ares->response, ares->error);
5860 switch (ares->type) {
5861 case LDB_REPLY_ENTRY:
5862 ar->search_msg = talloc_steal(ar, ares->message);
5865 case LDB_REPLY_REFERRAL:
5866 /* we ignore referrals */
5869 case LDB_REPLY_DONE:
5870 ret = replmd_replicated_uptodate_modify(ar);
5871 if (ret != LDB_SUCCESS) {
5872 return ldb_module_done(ar->req, NULL, NULL, ret);
5881 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5883 struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5884 struct replmd_private *replmd_private =
5885 talloc_get_type_abort(ldb_module_get_private(ar->module),
5886 struct replmd_private);
5888 static const char *attrs[] = {
5889 "replUpToDateVector",
5894 struct ldb_request *search_req;
5896 ar->search_msg = NULL;
5899 * Let the caller know that we did an originating updates
5901 ar->objs->originating_updates = replmd_private->originating_updates;
5903 ret = ldb_build_search_req(&search_req,
5906 ar->objs->partition_dn,
5912 replmd_replicated_uptodate_search_callback,
5914 LDB_REQ_SET_LOCATION(search_req);
5915 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5917 return ldb_next_request(ar->module, search_req);
5922 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5924 struct ldb_context *ldb;
5925 struct dsdb_extended_replicated_objects *objs;
5926 struct replmd_replicated_request *ar;
5927 struct ldb_control **ctrls;
5930 struct replmd_private *replmd_private =
5931 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5933 ldb = ldb_module_get_ctx(module);
5935 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5937 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5939 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5940 return LDB_ERR_PROTOCOL_ERROR;
5943 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5944 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5945 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5946 return LDB_ERR_PROTOCOL_ERROR;
5949 ar = replmd_ctx_init(module, req);
5951 return LDB_ERR_OPERATIONS_ERROR;
5953 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5954 ar->apply_mode = true;
5956 ar->schema = dsdb_get_schema(ldb, ar);
5958 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5960 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5961 return LDB_ERR_CONSTRAINT_VIOLATION;
5964 ctrls = req->controls;
5966 if (req->controls) {
5967 req->controls = talloc_memdup(ar, req->controls,
5968 talloc_get_size(req->controls));
5969 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5972 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
5973 if (ret != LDB_SUCCESS) {
5977 /* If this change contained linked attributes in the body
5978 * (rather than in the links section) we need to update
5979 * backlinks in linked_attributes */
5980 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5981 if (ret != LDB_SUCCESS) {
5985 ar->controls = req->controls;
5986 req->controls = ctrls;
5988 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5990 /* save away the linked attributes for the end of the
5992 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5993 struct la_entry *la_entry;
5995 if (replmd_private->la_ctx == NULL) {
5996 replmd_private->la_ctx = talloc_new(replmd_private);
5998 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5999 if (la_entry == NULL) {
6001 return LDB_ERR_OPERATIONS_ERROR;
6003 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
6004 if (la_entry->la == NULL) {
6005 talloc_free(la_entry);
6007 return LDB_ERR_OPERATIONS_ERROR;
6009 *la_entry->la = ar->objs->linked_attributes[i];
6011 /* we need to steal the non-scalars so they stay
6012 around until the end of the transaction */
6013 talloc_steal(la_entry->la, la_entry->la->identifier);
6014 talloc_steal(la_entry->la, la_entry->la->value.blob);
6016 DLIST_ADD(replmd_private->la_list, la_entry);
6019 return replmd_replicated_apply_next(ar);
6023 process one linked attribute structure
6025 static int replmd_process_linked_attribute(struct ldb_module *module,
6026 struct la_entry *la_entry,
6027 struct ldb_request *parent)
6029 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
6030 struct ldb_context *ldb = ldb_module_get_ctx(module);
6031 struct ldb_message *msg;
6032 struct ldb_message *target_msg = NULL;
6033 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
6034 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
6036 const struct dsdb_attribute *attr;
6037 struct dsdb_dn *dsdb_dn;
6038 uint64_t seq_num = 0;
6039 struct ldb_message_element *old_el;
6041 time_t t = time(NULL);
6042 struct ldb_result *res;
6043 struct ldb_result *target_res;
6044 const char *attrs[4];
6045 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
6046 struct parsed_dn *pdn_list, *pdn;
6047 struct GUID guid = GUID_zero();
6049 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
6050 const struct GUID *our_invocation_id;
6052 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
6053 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
6056 linked_attributes[0]:
6057 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
6059 identifier: struct drsuapi_DsReplicaObjectIdentifier
6060 __ndr_size : 0x0000003a (58)
6061 __ndr_size_sid : 0x00000000 (0)
6062 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
6064 __ndr_size_dn : 0x00000000 (0)
6066 attid : DRSUAPI_ATTID_member (0x1F)
6067 value: struct drsuapi_DsAttributeValue
6068 __ndr_size : 0x0000007e (126)
6070 blob : DATA_BLOB length=126
6071 flags : 0x00000001 (1)
6072 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
6073 originating_add_time : Wed Sep 2 22:20:01 2009 EST
6074 meta_data: struct drsuapi_DsReplicaMetaData
6075 version : 0x00000015 (21)
6076 originating_change_time : Wed Sep 2 23:39:07 2009 EST
6077 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
6078 originating_usn : 0x000000000001e19c (123292)
6080 (for cases where the link is to a normal DN)
6081 &target: struct drsuapi_DsReplicaObjectIdentifier3
6082 __ndr_size : 0x0000007e (126)
6083 __ndr_size_sid : 0x0000001c (28)
6084 guid : 7639e594-db75-4086-b0d4-67890ae46031
6085 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
6086 __ndr_size_dn : 0x00000022 (34)
6087 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
6090 /* find the attribute being modified */
6091 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
6093 struct GUID_txt_buf guid_str;
6094 ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
6096 GUID_buf_string(&la->identifier->guid,
6098 talloc_free(tmp_ctx);
6099 return LDB_ERR_OPERATIONS_ERROR;
6102 attrs[0] = attr->lDAPDisplayName;
6103 attrs[1] = "isDeleted";
6104 attrs[2] = "isRecycled";
6107 /* get the existing message from the db for the object with
6108 this GUID, returning attribute being modified. We will then
6109 use this msg as the basis for a modify call */
6110 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
6111 DSDB_FLAG_NEXT_MODULE |
6112 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6113 DSDB_SEARCH_SHOW_RECYCLED |
6114 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
6115 DSDB_SEARCH_REVEAL_INTERNALS,
6117 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
6118 if (ret != LDB_SUCCESS) {
6119 talloc_free(tmp_ctx);
6122 if (res->count != 1) {
6123 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
6124 GUID_string(tmp_ctx, &la->identifier->guid));
6125 talloc_free(tmp_ctx);
6126 return LDB_ERR_NO_SUCH_OBJECT;
6131 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6132 * ProcessLinkValue, because link updates are not applied to
6133 * recycled and tombstone objects. We don't have to delete
6134 * any existing link, that should have happened when the
6135 * object deletion was replicated or initiated.
6138 replmd_deletion_state(module, msg, &deletion_state, NULL);
6140 if (deletion_state >= OBJECT_RECYCLED) {
6141 talloc_free(tmp_ctx);
6145 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6146 if (old_el == NULL) {
6147 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
6148 if (ret != LDB_SUCCESS) {
6149 ldb_module_oom(module);
6150 talloc_free(tmp_ctx);
6151 return LDB_ERR_OPERATIONS_ERROR;
6154 old_el->flags = LDB_FLAG_MOD_REPLACE;
6157 /* parse the existing links */
6158 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
6159 if (ret != LDB_SUCCESS) {
6160 talloc_free(tmp_ctx);
6164 /* get our invocationId */
6165 our_invocation_id = samdb_ntds_invocation_id(ldb);
6166 if (!our_invocation_id) {
6167 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
6168 talloc_free(tmp_ctx);
6169 return LDB_ERR_OPERATIONS_ERROR;
6172 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
6173 if (ret != LDB_SUCCESS) {
6174 talloc_free(tmp_ctx);
6178 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
6179 if (!W_ERROR_IS_OK(status)) {
6180 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
6181 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
6182 talloc_free(tmp_ctx);
6183 return LDB_ERR_OPERATIONS_ERROR;
6186 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
6187 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
6189 * This strange behaviour (allowing a NULL/missing
6190 * GUID) originally comes from:
6192 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
6193 * Author: Andrew Tridgell <tridge@samba.org>
6194 * Date: Mon Dec 21 21:21:55 2009 +1100
6196 * s4-drs: cope better with NULL GUIDS from DRS
6198 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
6199 * need to match by DN if possible when seeing if we should update an
6202 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
6205 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
6206 dsdb_dn->dn, attrs2,
6207 DSDB_FLAG_NEXT_MODULE |
6208 DSDB_SEARCH_SHOW_RECYCLED |
6209 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6210 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6212 } else if (!NT_STATUS_IS_OK(ntstatus)) {
6213 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
6215 ldb_dn_get_linearized(dsdb_dn->dn),
6216 ldb_dn_get_linearized(msg->dn));
6217 talloc_free(tmp_ctx);
6218 return LDB_ERR_OPERATIONS_ERROR;
6220 ret = dsdb_module_search(module, tmp_ctx, &target_res,
6221 NULL, LDB_SCOPE_SUBTREE,
6223 DSDB_FLAG_NEXT_MODULE |
6224 DSDB_SEARCH_SHOW_RECYCLED |
6225 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
6226 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
6229 GUID_string(tmp_ctx, &guid));
6232 if (ret != LDB_SUCCESS) {
6233 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
6234 GUID_string(tmp_ctx, &guid),
6235 ldb_errstring(ldb_module_get_ctx(module)));
6236 talloc_free(tmp_ctx);
6240 if (target_res->count == 0) {
6241 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
6242 GUID_string(tmp_ctx, &guid),
6243 ldb_dn_get_linearized(dsdb_dn->dn)));
6244 } else if (target_res->count != 1) {
6245 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
6246 GUID_string(tmp_ctx, &guid));
6247 talloc_free(tmp_ctx);
6248 return LDB_ERR_OPERATIONS_ERROR;
6250 target_msg = target_res->msgs[0];
6251 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
6255 * Check for deleted objects per MS-DRSR 4.1.10.6.13
6256 * ProcessLinkValue, because link updates are not applied to
6257 * recycled and tombstone objects. We don't have to delete
6258 * any existing link, that should have happened when the
6259 * object deletion was replicated or initiated.
6261 replmd_deletion_state(module, target_msg,
6262 &target_deletion_state, NULL);
6264 if (target_deletion_state >= OBJECT_RECYCLED) {
6265 talloc_free(tmp_ctx);
6269 /* see if this link already exists */
6270 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
6272 /* see if this update is newer than what we have already */
6273 struct GUID invocation_id = GUID_zero();
6274 uint32_t version = 0;
6275 uint32_t originating_usn = 0;
6276 NTTIME change_time = 0;
6277 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
6279 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
6280 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
6281 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
6282 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
6284 if (!replmd_update_is_newer(&invocation_id,
6285 &la->meta_data.originating_invocation_id,
6287 la->meta_data.version,
6289 la->meta_data.originating_change_time)) {
6290 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
6291 old_el->name, ldb_dn_get_linearized(msg->dn),
6292 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
6293 talloc_free(tmp_ctx);
6297 /* get a seq_num for this change */
6298 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6299 if (ret != LDB_SUCCESS) {
6300 talloc_free(tmp_ctx);
6304 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
6305 /* remove the existing backlink */
6306 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, true);
6307 if (ret != LDB_SUCCESS) {
6308 talloc_free(tmp_ctx);
6313 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
6314 &la->meta_data.originating_invocation_id,
6315 la->meta_data.originating_usn, seq_num,
6316 la->meta_data.originating_change_time,
6317 la->meta_data.version,
6319 if (ret != LDB_SUCCESS) {
6320 talloc_free(tmp_ctx);
6325 /* add the new backlink */
6326 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, true);
6327 if (ret != LDB_SUCCESS) {
6328 talloc_free(tmp_ctx);
6333 /* get a seq_num for this change */
6334 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
6335 if (ret != LDB_SUCCESS) {
6336 talloc_free(tmp_ctx);
6340 old_el->values = talloc_realloc(msg->elements, old_el->values,
6341 struct ldb_val, old_el->num_values+1);
6342 if (!old_el->values) {
6343 ldb_module_oom(module);
6344 return LDB_ERR_OPERATIONS_ERROR;
6346 old_el->num_values++;
6348 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
6349 &la->meta_data.originating_invocation_id,
6350 la->meta_data.originating_usn, seq_num,
6351 la->meta_data.originating_change_time,
6352 la->meta_data.version,
6354 if (ret != LDB_SUCCESS) {
6355 talloc_free(tmp_ctx);
6360 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
6362 if (ret != LDB_SUCCESS) {
6363 talloc_free(tmp_ctx);
6369 /* we only change whenChanged and uSNChanged if the seq_num
6371 ret = add_time_element(msg, "whenChanged", t);
6372 if (ret != LDB_SUCCESS) {
6373 talloc_free(tmp_ctx);
6378 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
6379 if (ret != LDB_SUCCESS) {
6380 talloc_free(tmp_ctx);
6385 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
6386 if (old_el == NULL) {
6387 talloc_free(tmp_ctx);
6388 return ldb_operr(ldb);
6391 ret = dsdb_check_single_valued_link(attr, old_el);
6392 if (ret != LDB_SUCCESS) {
6393 talloc_free(tmp_ctx);
6397 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
6399 ret = linked_attr_modify(module, msg, parent);
6400 if (ret != LDB_SUCCESS) {
6401 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
6403 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
6404 talloc_free(tmp_ctx);
6408 talloc_free(tmp_ctx);
6413 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
6415 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
6416 return replmd_extended_replicated_objects(module, req);
6419 return ldb_next_request(module, req);
6424 we hook into the transaction operations to allow us to
6425 perform the linked attribute updates at the end of the whole
6426 transaction. This allows a forward linked attribute to be created
6427 before the object is created. During a vampire, w2k8 sends us linked
6428 attributes before the objects they are part of.
6430 static int replmd_start_transaction(struct ldb_module *module)
6432 /* create our private structure for this transaction */
6433 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
6434 struct replmd_private);
6435 replmd_txn_cleanup(replmd_private);
6437 /* free any leftover mod_usn records from cancelled
6439 while (replmd_private->ncs) {
6440 struct nc_entry *e = replmd_private->ncs;
6441 DLIST_REMOVE(replmd_private->ncs, e);
6445 replmd_private->originating_updates = false;
6447 return ldb_next_start_trans(module);
6451 on prepare commit we loop over our queued la_context structures and
6454 static int replmd_prepare_commit(struct ldb_module *module)
6456 struct replmd_private *replmd_private =
6457 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6458 struct la_entry *la, *prev;
6459 struct la_backlink *bl;
6462 /* walk the list backwards, to do the first entry first, as we
6463 * added the entries with DLIST_ADD() which puts them at the
6464 * start of the list */
6465 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
6466 prev = DLIST_PREV(la);
6467 DLIST_REMOVE(replmd_private->la_list, la);
6468 ret = replmd_process_linked_attribute(module, la, NULL);
6469 if (ret != LDB_SUCCESS) {
6470 replmd_txn_cleanup(replmd_private);
6475 /* process our backlink list, creating and deleting backlinks
6477 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
6478 ret = replmd_process_backlink(module, bl, NULL);
6479 if (ret != LDB_SUCCESS) {
6480 replmd_txn_cleanup(replmd_private);
6485 replmd_txn_cleanup(replmd_private);
6487 /* possibly change @REPLCHANGED */
6488 ret = replmd_notify_store(module, NULL);
6489 if (ret != LDB_SUCCESS) {
6493 return ldb_next_prepare_commit(module);
6496 static int replmd_del_transaction(struct ldb_module *module)
6498 struct replmd_private *replmd_private =
6499 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6500 replmd_txn_cleanup(replmd_private);
6502 return ldb_next_del_trans(module);
6506 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
6507 .name = "repl_meta_data",
6508 .init_context = replmd_init,
6510 .modify = replmd_modify,
6511 .rename = replmd_rename,
6512 .del = replmd_delete,
6513 .extended = replmd_extended,
6514 .start_transaction = replmd_start_transaction,
6515 .prepare_commit = replmd_prepare_commit,
6516 .del_transaction = replmd_del_transaction,
6519 int ldb_repl_meta_data_module_init(const char *version)
6521 LDB_MODULE_CHECK_VERSION(version);
6522 return ldb_register_module(&ldb_repl_meta_data_module_ops);