4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 Copyright (C) Andrew Tridgell 2005-2009
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
54 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
55 * Deleted Objects Container
57 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
59 struct replmd_private {
61 struct la_entry *la_list;
63 struct la_backlink *la_backlinks;
65 struct nc_entry *prev, *next;
68 uint64_t mod_usn_urgent;
70 struct ldb_dn *schema_dn;
74 struct la_entry *next, *prev;
75 struct drsuapi_DsReplicaLinkedAttribute *la;
78 struct replmd_replicated_request {
79 struct ldb_module *module;
80 struct ldb_request *req;
82 const struct dsdb_schema *schema;
83 struct GUID our_invocation_id;
85 /* the controls we pass down */
86 struct ldb_control **controls;
88 /* details for the mode where we apply a bunch of inbound replication meessages */
90 uint32_t index_current;
91 struct dsdb_extended_replicated_objects *objs;
93 struct ldb_message *search_msg;
94 struct GUID local_parent_guid;
102 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
103 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
105 enum urgent_situation {
106 REPL_URGENT_ON_CREATE = 1,
107 REPL_URGENT_ON_UPDATE = 2,
108 REPL_URGENT_ON_DELETE = 4
111 enum deletion_state {
112 OBJECT_NOT_DELETED=1,
119 static void replmd_deletion_state(struct ldb_module *module,
120 const struct ldb_message *msg,
121 enum deletion_state *current_state,
122 enum deletion_state *next_state)
125 bool enabled = false;
128 *current_state = OBJECT_REMOVED;
129 if (next_state != NULL) {
130 *next_state = OBJECT_REMOVED;
135 ret = dsdb_recyclebin_enabled(module, &enabled);
136 if (ret != LDB_SUCCESS) {
140 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
142 *current_state = OBJECT_TOMBSTONE;
143 if (next_state != NULL) {
144 *next_state = OBJECT_REMOVED;
149 if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
150 *current_state = OBJECT_RECYCLED;
151 if (next_state != NULL) {
152 *next_state = OBJECT_REMOVED;
157 *current_state = OBJECT_DELETED;
158 if (next_state != NULL) {
159 *next_state = OBJECT_RECYCLED;
164 *current_state = OBJECT_NOT_DELETED;
165 if (next_state == NULL) {
170 *next_state = OBJECT_DELETED;
172 *next_state = OBJECT_TOMBSTONE;
176 static const struct {
177 const char *update_name;
178 enum urgent_situation repl_situation;
179 } urgent_objects[] = {
180 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
181 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
182 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
183 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
184 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
185 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
189 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
190 static const char *urgent_attrs[] = {
193 "userAccountControl",
198 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
199 enum urgent_situation situation)
202 for (i=0; urgent_objects[i].update_name; i++) {
204 if ((situation & urgent_objects[i].repl_situation) == 0) {
208 for (j=0; j<objectclass_el->num_values; j++) {
209 const struct ldb_val *v = &objectclass_el->values[j];
210 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
218 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
220 if (ldb_attr_in_list(urgent_attrs, el->name)) {
227 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
230 initialise the module
231 allocate the private structure and build the list
232 of partition DNs for use by replmd_notify()
234 static int replmd_init(struct ldb_module *module)
236 struct replmd_private *replmd_private;
237 struct ldb_context *ldb = ldb_module_get_ctx(module);
239 replmd_private = talloc_zero(module, struct replmd_private);
240 if (replmd_private == NULL) {
242 return LDB_ERR_OPERATIONS_ERROR;
244 ldb_module_set_private(module, replmd_private);
246 replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
248 return ldb_next_init(module);
252 cleanup our per-transaction contexts
254 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
256 talloc_free(replmd_private->la_ctx);
257 replmd_private->la_list = NULL;
258 replmd_private->la_ctx = NULL;
260 talloc_free(replmd_private->bl_ctx);
261 replmd_private->la_backlinks = NULL;
262 replmd_private->bl_ctx = NULL;
267 struct la_backlink *next, *prev;
268 const char *attr_name;
269 struct GUID forward_guid, target_guid;
274 process a backlinks we accumulated during a transaction, adding and
275 deleting the backlinks from the target objects
277 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
279 struct ldb_dn *target_dn, *source_dn;
281 struct ldb_context *ldb = ldb_module_get_ctx(module);
282 struct ldb_message *msg;
283 TALLOC_CTX *tmp_ctx = talloc_new(bl);
289 - construct ldb_message
290 - either an add or a delete
292 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
293 if (ret != LDB_SUCCESS) {
294 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
295 GUID_string(bl, &bl->target_guid)));
299 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
300 if (ret != LDB_SUCCESS) {
301 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
302 GUID_string(bl, &bl->forward_guid));
303 talloc_free(tmp_ctx);
307 msg = ldb_msg_new(tmp_ctx);
309 ldb_module_oom(module);
310 talloc_free(tmp_ctx);
311 return LDB_ERR_OPERATIONS_ERROR;
314 /* construct a ldb_message for adding/deleting the backlink */
316 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
318 ldb_module_oom(module);
319 talloc_free(tmp_ctx);
320 return LDB_ERR_OPERATIONS_ERROR;
322 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
323 if (ret != LDB_SUCCESS) {
324 talloc_free(tmp_ctx);
327 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
329 /* a backlink should never be single valued. Unfortunately the
330 exchange schema has a attribute
331 msExchBridgeheadedLocalConnectorsDNBL which is single
332 valued and a backlink. We need to cope with that by
333 ignoring the single value flag */
334 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
336 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
337 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
338 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
339 cope with possible corruption where the backlink has
340 already been removed */
341 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
342 ldb_dn_get_linearized(target_dn),
343 ldb_dn_get_linearized(source_dn),
344 ldb_errstring(ldb)));
346 } else if (ret != LDB_SUCCESS) {
347 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
348 bl->active?"add":"remove",
349 ldb_dn_get_linearized(source_dn),
350 ldb_dn_get_linearized(target_dn),
352 talloc_free(tmp_ctx);
355 talloc_free(tmp_ctx);
360 add a backlink to the list of backlinks to add/delete in the prepare
363 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
364 struct GUID *forward_guid, struct GUID *target_guid,
365 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
367 const struct dsdb_attribute *target_attr;
368 struct la_backlink *bl;
369 struct replmd_private *replmd_private =
370 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
372 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
375 * windows 2003 has a broken schema where the
376 * definition of msDS-IsDomainFor is missing (which is
377 * supposed to be the backlink of the
378 * msDS-HasDomainNCs attribute
383 /* see if its already in the list */
384 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
385 if (GUID_equal(forward_guid, &bl->forward_guid) &&
386 GUID_equal(target_guid, &bl->target_guid) &&
387 (target_attr->lDAPDisplayName == bl->attr_name ||
388 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
394 /* we found an existing one */
395 if (bl->active == active) {
398 DLIST_REMOVE(replmd_private->la_backlinks, bl);
403 if (replmd_private->bl_ctx == NULL) {
404 replmd_private->bl_ctx = talloc_new(replmd_private);
405 if (replmd_private->bl_ctx == NULL) {
406 ldb_module_oom(module);
407 return LDB_ERR_OPERATIONS_ERROR;
412 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
414 ldb_module_oom(module);
415 return LDB_ERR_OPERATIONS_ERROR;
418 /* Ensure the schema does not go away before the bl->attr_name is used */
419 if (!talloc_reference(bl, schema)) {
421 ldb_module_oom(module);
422 return LDB_ERR_OPERATIONS_ERROR;
425 bl->attr_name = target_attr->lDAPDisplayName;
426 bl->forward_guid = *forward_guid;
427 bl->target_guid = *target_guid;
430 /* the caller may ask for this backlink to be processed
433 int ret = replmd_process_backlink(module, bl, NULL);
438 DLIST_ADD(replmd_private->la_backlinks, bl);
445 * Callback for most write operations in this module:
447 * notify the repl task that a object has changed. The notifies are
448 * gathered up in the replmd_private structure then written to the
449 * @REPLCHANGED object in each partition during the prepare_commit
451 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
454 struct replmd_replicated_request *ac =
455 talloc_get_type_abort(req->context, struct replmd_replicated_request);
456 struct replmd_private *replmd_private =
457 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
458 struct nc_entry *modified_partition;
459 struct ldb_control *partition_ctrl;
460 const struct dsdb_control_current_partition *partition;
462 struct ldb_control **controls;
464 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
466 controls = ares->controls;
467 if (ldb_request_get_control(ac->req,
468 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
470 * Remove the current partition control from what we pass up
471 * the chain if it hasn't been requested manually.
473 controls = ldb_controls_except_specified(ares->controls, ares,
477 if (ares->error != LDB_SUCCESS) {
478 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
479 return ldb_module_done(ac->req, controls,
480 ares->response, ares->error);
483 if (ares->type != LDB_REPLY_DONE) {
484 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
485 return ldb_module_done(ac->req, NULL,
486 NULL, LDB_ERR_OPERATIONS_ERROR);
489 if (!partition_ctrl) {
490 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
491 return ldb_module_done(ac->req, NULL,
492 NULL, LDB_ERR_OPERATIONS_ERROR);
495 partition = talloc_get_type_abort(partition_ctrl->data,
496 struct dsdb_control_current_partition);
498 if (ac->seq_num > 0) {
499 for (modified_partition = replmd_private->ncs; modified_partition;
500 modified_partition = modified_partition->next) {
501 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
506 if (modified_partition == NULL) {
507 modified_partition = talloc_zero(replmd_private, struct nc_entry);
508 if (!modified_partition) {
509 ldb_oom(ldb_module_get_ctx(ac->module));
510 return ldb_module_done(ac->req, NULL,
511 NULL, LDB_ERR_OPERATIONS_ERROR);
513 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
514 if (!modified_partition->dn) {
515 ldb_oom(ldb_module_get_ctx(ac->module));
516 return ldb_module_done(ac->req, NULL,
517 NULL, LDB_ERR_OPERATIONS_ERROR);
519 DLIST_ADD(replmd_private->ncs, modified_partition);
522 if (ac->seq_num > modified_partition->mod_usn) {
523 modified_partition->mod_usn = ac->seq_num;
525 modified_partition->mod_usn_urgent = ac->seq_num;
530 if (ac->apply_mode) {
531 ret = replmd_replicated_apply_isDeleted(ac);
532 if (ret != LDB_SUCCESS) {
533 return ldb_module_done(ac->req, NULL, NULL, ret);
537 /* free the partition control container here, for the
538 * common path. Other cases will have it cleaned up
539 * eventually with the ares */
540 talloc_free(partition_ctrl);
541 return ldb_module_done(ac->req, controls,
542 ares->response, LDB_SUCCESS);
548 * update a @REPLCHANGED record in each partition if there have been
549 * any writes of replicated data in the partition
551 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
553 struct replmd_private *replmd_private =
554 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
556 while (replmd_private->ncs) {
558 struct nc_entry *modified_partition = replmd_private->ncs;
560 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
561 modified_partition->mod_usn,
562 modified_partition->mod_usn_urgent, parent);
563 if (ret != LDB_SUCCESS) {
564 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
565 ldb_dn_get_linearized(modified_partition->dn)));
568 DLIST_REMOVE(replmd_private->ncs, modified_partition);
569 talloc_free(modified_partition);
577 created a replmd_replicated_request context
579 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
580 struct ldb_request *req)
582 struct ldb_context *ldb;
583 struct replmd_replicated_request *ac;
584 const struct GUID *our_invocation_id;
586 ldb = ldb_module_get_ctx(module);
588 ac = talloc_zero(req, struct replmd_replicated_request);
597 ac->schema = dsdb_get_schema(ldb, ac);
599 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
600 "replmd_modify: no dsdb_schema loaded");
601 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
606 /* get our invocationId */
607 our_invocation_id = samdb_ntds_invocation_id(ldb);
608 if (!our_invocation_id) {
609 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
610 "replmd_add: unable to find invocationId\n");
614 ac->our_invocation_id = *our_invocation_id;
620 add a time element to a record
622 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
624 struct ldb_message_element *el;
628 if (ldb_msg_find_element(msg, attr) != NULL) {
632 s = ldb_timestring(msg, t);
634 return LDB_ERR_OPERATIONS_ERROR;
637 ret = ldb_msg_add_string(msg, attr, s);
638 if (ret != LDB_SUCCESS) {
642 el = ldb_msg_find_element(msg, attr);
643 /* always set as replace. This works because on add ops, the flag
645 el->flags = LDB_FLAG_MOD_REPLACE;
651 add a uint64_t element to a record
653 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
654 const char *attr, uint64_t v)
656 struct ldb_message_element *el;
659 if (ldb_msg_find_element(msg, attr) != NULL) {
663 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
664 if (ret != LDB_SUCCESS) {
668 el = ldb_msg_find_element(msg, attr);
669 /* always set as replace. This works because on add ops, the flag
671 el->flags = LDB_FLAG_MOD_REPLACE;
676 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
677 const struct replPropertyMetaData1 *m2,
678 const uint32_t *rdn_attid)
681 * This assignment seems inoccous, but it is critical for the
682 * system, as we need to do the comparisons as a unsigned
683 * quantity, not signed (enums are signed integers)
685 uint32_t attid_1 = m1->attid;
686 uint32_t attid_2 = m2->attid;
688 if (attid_1 == attid_2) {
693 * the rdn attribute should be at the end!
694 * so we need to return a value greater than zero
695 * which means m1 is greater than m2
697 if (attid_1 == *rdn_attid) {
702 * the rdn attribute should be at the end!
703 * so we need to return a value less than zero
704 * which means m2 is greater than m1
706 if (attid_2 == *rdn_attid) {
711 * See above regarding this being an unsigned comparison.
712 * Otherwise when the high bit is set on non-standard
713 * attributes, they would end up first, before objectClass
716 return attid_1 > attid_2 ? 1 : -1;
719 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
720 struct replPropertyMetaDataCtr1 *ctr1,
721 const struct dsdb_attribute *rdn_sa,
724 if (ctr1->count == 0) {
725 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
726 "No elements found in replPropertyMetaData for %s!\n",
727 ldb_dn_get_linearized(dn));
728 return LDB_ERR_CONSTRAINT_VIOLATION;
731 /* the objectClass attribute is value 0x00000000, so must be first */
732 if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
733 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
734 "No objectClass found in replPropertyMetaData for %s!\n",
735 ldb_dn_get_linearized(dn));
736 return LDB_ERR_OBJECT_CLASS_VIOLATION;
742 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
743 struct replPropertyMetaDataCtr1 *ctr1,
744 const struct dsdb_schema *schema,
747 const char *rdn_name;
748 const struct dsdb_attribute *rdn_sa;
750 rdn_name = ldb_dn_get_rdn_name(dn);
752 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
753 __location__ ": No rDN for %s?\n",
754 ldb_dn_get_linearized(dn));
755 return LDB_ERR_INVALID_DN_SYNTAX;
758 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
759 if (rdn_sa == NULL) {
760 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
761 __location__ ": No sa found for rDN %s for %s\n",
762 rdn_name, ldb_dn_get_linearized(dn));
763 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
766 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
767 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
769 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
770 replmd_replPropertyMetaData1_attid_sort);
771 return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
774 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
775 const struct ldb_message_element *e2,
776 const struct dsdb_schema *schema)
778 const struct dsdb_attribute *a1;
779 const struct dsdb_attribute *a2;
782 * TODO: make this faster by caching the dsdb_attribute pointer
783 * on the ldb_messag_element
786 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
787 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
790 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
794 return strcasecmp(e1->name, e2->name);
796 if (a1->attributeID_id == a2->attributeID_id) {
799 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
802 static void replmd_ldb_message_sort(struct ldb_message *msg,
803 const struct dsdb_schema *schema)
805 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
808 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
809 const struct GUID *invocation_id, uint64_t seq_num,
810 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
814 fix up linked attributes in replmd_add.
815 This involves setting up the right meta-data in extended DN
816 components, and creating backlinks to the object
818 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
819 uint64_t seq_num, const struct GUID *invocationId, time_t t,
820 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
823 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
824 struct ldb_context *ldb = ldb_module_get_ctx(module);
826 /* We will take a reference to the schema in replmd_add_backlink */
827 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
830 unix_to_nt_time(&now, t);
832 for (i=0; i<el->num_values; i++) {
833 struct ldb_val *v = &el->values[i];
834 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
835 struct GUID target_guid;
839 /* note that the DN already has the extended
840 components from the extended_dn_store module */
841 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
842 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
843 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
844 if (ret != LDB_SUCCESS) {
845 talloc_free(tmp_ctx);
848 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
849 if (ret != LDB_SUCCESS) {
850 talloc_free(tmp_ctx);
855 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
856 seq_num, seq_num, now, 0, false);
857 if (ret != LDB_SUCCESS) {
858 talloc_free(tmp_ctx);
862 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
863 if (ret != LDB_SUCCESS) {
864 talloc_free(tmp_ctx);
869 talloc_free(tmp_ctx);
875 intercept add requests
877 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
879 struct samldb_msds_intid_persistant *msds_intid_struct;
880 struct ldb_context *ldb;
881 struct ldb_control *control;
882 struct replmd_replicated_request *ac;
883 enum ndr_err_code ndr_err;
884 struct ldb_request *down_req;
885 struct ldb_message *msg;
886 const DATA_BLOB *guid_blob;
888 struct replPropertyMetaDataBlob nmd;
889 struct ldb_val nmd_value;
892 * The use of a time_t here seems odd, but as the NTTIME
893 * elements are actually declared as NTTIME_1sec in the IDL,
894 * getting a higher resolution timestamp is not required.
896 time_t t = time(NULL);
901 unsigned int functional_level;
903 bool allow_add_guid = false;
904 bool remove_current_guid = false;
905 bool is_urgent = false;
906 bool is_schema_nc = false;
907 struct ldb_message_element *objectclass_el;
908 struct replmd_private *replmd_private =
909 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
911 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
912 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
914 allow_add_guid = true;
917 /* do not manipulate our control entries */
918 if (ldb_dn_is_special(req->op.add.message->dn)) {
919 return ldb_next_request(module, req);
922 ldb = ldb_module_get_ctx(module);
924 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
926 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
927 if (guid_blob != NULL) {
928 if (!allow_add_guid) {
929 ldb_set_errstring(ldb,
930 "replmd_add: it's not allowed to add an object with objectGUID!");
931 return LDB_ERR_UNWILLING_TO_PERFORM;
933 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
934 if (!NT_STATUS_IS_OK(status)) {
935 ldb_set_errstring(ldb,
936 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
937 return LDB_ERR_UNWILLING_TO_PERFORM;
939 /* we remove this attribute as it can be a string and
940 * will not be treated correctly and then we will re-add
941 * it later on in the good format */
942 remove_current_guid = true;
946 guid = GUID_random();
949 ac = replmd_ctx_init(module, req);
951 return ldb_module_oom(module);
954 functional_level = dsdb_functional_level(ldb);
956 /* Get a sequence number from the backend */
957 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
958 if (ret != LDB_SUCCESS) {
963 /* we have to copy the message as the caller might have it as a const */
964 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
968 return LDB_ERR_OPERATIONS_ERROR;
971 /* generated times */
972 unix_to_nt_time(&now, t);
973 time_str = ldb_timestring(msg, t);
977 return LDB_ERR_OPERATIONS_ERROR;
979 if (remove_current_guid) {
980 ldb_msg_remove_attr(msg,"objectGUID");
984 * remove autogenerated attributes
986 ldb_msg_remove_attr(msg, "whenCreated");
987 ldb_msg_remove_attr(msg, "whenChanged");
988 ldb_msg_remove_attr(msg, "uSNCreated");
989 ldb_msg_remove_attr(msg, "uSNChanged");
990 ldb_msg_remove_attr(msg, "replPropertyMetaData");
993 * readd replicated attributes
995 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
996 if (ret != LDB_SUCCESS) {
1002 /* build the replication meta_data */
1005 nmd.ctr.ctr1.count = msg->num_elements;
1006 nmd.ctr.ctr1.array = talloc_array(msg,
1007 struct replPropertyMetaData1,
1008 nmd.ctr.ctr1.count);
1009 if (!nmd.ctr.ctr1.array) {
1012 return LDB_ERR_OPERATIONS_ERROR;
1015 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1017 for (i=0; i < msg->num_elements; i++) {
1018 struct ldb_message_element *e = &msg->elements[i];
1019 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1020 const struct dsdb_attribute *sa;
1022 if (e->name[0] == '@') continue;
1024 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1026 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1027 "replmd_add: attribute '%s' not defined in schema\n",
1030 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1033 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1034 /* if the attribute is not replicated (0x00000001)
1035 * or constructed (0x00000004) it has no metadata
1040 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1041 ret = replmd_add_fix_la(module, e, ac->seq_num, &ac->our_invocation_id, t, &guid, sa, req);
1042 if (ret != LDB_SUCCESS) {
1046 /* linked attributes are not stored in
1047 replPropertyMetaData in FL above w2k */
1051 m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1053 if (m->attid == DRSUAPI_ATTID_isDeleted) {
1054 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1057 if (rdn_val == NULL) {
1060 return LDB_ERR_OPERATIONS_ERROR;
1063 rdn = (const char*)rdn_val->data;
1064 if (strcmp(rdn, "Deleted Objects") == 0) {
1066 * Set the originating_change_time to 29/12/9999 at 23:59:59
1067 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1069 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1071 m->originating_change_time = now;
1074 m->originating_change_time = now;
1076 m->originating_invocation_id = ac->our_invocation_id;
1077 m->originating_usn = ac->seq_num;
1078 m->local_usn = ac->seq_num;
1082 /* fix meta data count */
1083 nmd.ctr.ctr1.count = ni;
1086 * sort meta data array, and move the rdn attribute entry to the end
1088 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, msg->dn);
1089 if (ret != LDB_SUCCESS) {
1090 ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1095 /* generated NDR encoded values */
1096 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1098 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1099 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1102 return LDB_ERR_OPERATIONS_ERROR;
1106 * add the autogenerated values
1108 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1109 if (ret != LDB_SUCCESS) {
1114 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1115 if (ret != LDB_SUCCESS) {
1120 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1121 if (ret != LDB_SUCCESS) {
1126 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1127 if (ret != LDB_SUCCESS) {
1132 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1133 if (ret != LDB_SUCCESS) {
1140 * sort the attributes by attid before storing the object
1142 replmd_ldb_message_sort(msg, ac->schema);
1145 * Assert that we do have an objectClass
1147 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1148 if (objectclass_el == NULL) {
1149 ldb_asprintf_errstring(ldb, __location__
1150 ": objectClass missing on %s\n",
1151 ldb_dn_get_linearized(msg->dn));
1153 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1155 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1156 REPL_URGENT_ON_CREATE);
1158 ac->is_urgent = is_urgent;
1159 ret = ldb_build_add_req(&down_req, ldb, ac,
1162 ac, replmd_op_callback,
1165 LDB_REQ_SET_LOCATION(down_req);
1166 if (ret != LDB_SUCCESS) {
1171 /* current partition control is needed by "replmd_op_callback" */
1172 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1173 ret = ldb_request_add_control(down_req,
1174 DSDB_CONTROL_CURRENT_PARTITION_OID,
1176 if (ret != LDB_SUCCESS) {
1182 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1183 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1184 if (ret != LDB_SUCCESS) {
1190 /* mark the control done */
1192 control->critical = 0;
1194 if (ldb_dn_compare_base(replmd_private->schema_dn, req->op.add.message->dn) != 0) {
1196 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1197 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1198 if (msds_intid_struct) {
1199 msds_intid_struct->usn = ac->seq_num;
1202 /* go on with the call chain */
1203 return ldb_next_request(module, down_req);
1208 * update the replPropertyMetaData for one element
1210 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1211 struct ldb_message *msg,
1212 struct ldb_message_element *el,
1213 struct ldb_message_element *old_el,
1214 struct replPropertyMetaDataBlob *omd,
1215 const struct dsdb_schema *schema,
1217 const struct GUID *our_invocation_id,
1220 struct ldb_request *req)
1223 const struct dsdb_attribute *a;
1224 struct replPropertyMetaData1 *md1;
1225 bool may_skip = false;
1228 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1230 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1231 /* allow this to make it possible for dbcheck
1232 to remove bad attributes */
1236 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1238 return LDB_ERR_OPERATIONS_ERROR;
1241 attid = dsdb_attribute_get_attid(a, is_schema_nc);
1243 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1248 * if the attribute's value haven't changed, and this isn't
1249 * just a delete of everything then return LDB_SUCCESS Unless
1250 * we have the provision control or if the attribute is
1251 * interSiteTopologyGenerator as this page explain:
1252 * http://support.microsoft.com/kb/224815 this attribute is
1253 * periodicaly written by the DC responsible for the intersite
1254 * generation in a given site
1256 * Unchanged could be deleting or replacing an already-gone
1257 * thing with an unconstrained delete/empty replace or a
1258 * replace with the same value, but not an add with the same
1259 * value because that could be about adding a duplicate (which
1260 * is for someone else to error out on).
1262 if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1263 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1266 } else if (old_el == NULL && el->num_values == 0) {
1267 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1269 } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1275 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1276 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1278 * allow this to make it possible for dbcheck
1279 * to rebuild broken metadata
1285 for (i=0; i<omd->ctr.ctr1.count; i++) {
1287 * First check if we find it under the msDS-IntID,
1288 * then check if we find it under the OID and
1291 * This allows the administrator to simply re-write
1292 * the attributes and so restore replication, which is
1293 * likely what they will try to do.
1295 if (attid == omd->ctr.ctr1.array[i].attid) {
1299 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1304 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1305 /* linked attributes are not stored in
1306 replPropertyMetaData in FL above w2k, but we do
1307 raise the seqnum for the object */
1308 if (*seq_num == 0 &&
1309 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1310 return LDB_ERR_OPERATIONS_ERROR;
1315 if (i == omd->ctr.ctr1.count) {
1316 /* we need to add a new one */
1317 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1318 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1319 if (omd->ctr.ctr1.array == NULL) {
1321 return LDB_ERR_OPERATIONS_ERROR;
1323 omd->ctr.ctr1.count++;
1324 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1327 /* Get a new sequence number from the backend. We only do this
1328 * if we have a change that requires a new
1329 * replPropertyMetaData element
1331 if (*seq_num == 0) {
1332 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1333 if (ret != LDB_SUCCESS) {
1334 return LDB_ERR_OPERATIONS_ERROR;
1338 md1 = &omd->ctr.ctr1.array[i];
1341 if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1342 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1345 if (rdn_val == NULL) {
1347 return LDB_ERR_OPERATIONS_ERROR;
1350 rdn = (const char*)rdn_val->data;
1351 if (strcmp(rdn, "Deleted Objects") == 0) {
1353 * Set the originating_change_time to 29/12/9999 at 23:59:59
1354 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1356 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1358 md1->originating_change_time = now;
1361 md1->originating_change_time = now;
1363 md1->originating_invocation_id = *our_invocation_id;
1364 md1->originating_usn = *seq_num;
1365 md1->local_usn = *seq_num;
1370 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1372 uint32_t count = omd.ctr.ctr1.count;
1375 for (i=0; i < count; i++) {
1376 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1377 if (max < m.local_usn) {
1385 * update the replPropertyMetaData object each time we modify an
1386 * object. This is needed for DRS replication, as the merge on the
1387 * client is based on this object
1389 static int replmd_update_rpmd(struct ldb_module *module,
1390 const struct dsdb_schema *schema,
1391 struct ldb_request *req,
1392 const char * const *rename_attrs,
1393 struct ldb_message *msg, uint64_t *seq_num,
1394 time_t t, bool is_schema_nc,
1395 bool *is_urgent, bool *rodc)
1397 const struct ldb_val *omd_value;
1398 enum ndr_err_code ndr_err;
1399 struct replPropertyMetaDataBlob omd;
1402 const struct GUID *our_invocation_id;
1404 const char * const *attrs = NULL;
1405 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1406 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1407 struct ldb_result *res;
1408 struct ldb_context *ldb;
1409 struct ldb_message_element *objectclass_el;
1410 enum urgent_situation situation;
1411 bool rmd_is_provided;
1412 bool rmd_is_just_resorted = false;
1415 attrs = rename_attrs;
1420 ldb = ldb_module_get_ctx(module);
1422 our_invocation_id = samdb_ntds_invocation_id(ldb);
1423 if (!our_invocation_id) {
1424 /* this happens during an initial vampire while
1425 updating the schema */
1426 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1430 unix_to_nt_time(&now, t);
1432 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1433 rmd_is_provided = true;
1434 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1435 rmd_is_just_resorted = true;
1438 rmd_is_provided = false;
1441 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1442 * otherwise we consider we are updating */
1443 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1444 situation = REPL_URGENT_ON_DELETE;
1445 } else if (rename_attrs) {
1446 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1448 situation = REPL_URGENT_ON_UPDATE;
1451 if (rmd_is_provided) {
1452 /* In this case the change_replmetadata control was supplied */
1453 /* We check that it's the only attribute that is provided
1454 * (it's a rare case so it's better to keep the code simplier)
1455 * We also check that the highest local_usn is bigger or the same as
1458 if( msg->num_elements != 1 ||
1459 strncmp(msg->elements[0].name,
1460 "replPropertyMetaData", 20) ) {
1461 DEBUG(0,(__location__ ": changereplmetada control called without "\
1462 "a specified replPropertyMetaData attribute or with others\n"));
1463 return LDB_ERR_OPERATIONS_ERROR;
1465 if (situation != REPL_URGENT_ON_UPDATE) {
1466 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1467 return LDB_ERR_OPERATIONS_ERROR;
1469 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1471 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1472 ldb_dn_get_linearized(msg->dn)));
1473 return LDB_ERR_OPERATIONS_ERROR;
1475 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1476 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1477 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1478 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1479 ldb_dn_get_linearized(msg->dn)));
1480 return LDB_ERR_OPERATIONS_ERROR;
1483 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1484 DSDB_FLAG_NEXT_MODULE |
1485 DSDB_SEARCH_SHOW_RECYCLED |
1486 DSDB_SEARCH_SHOW_EXTENDED_DN |
1487 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1488 DSDB_SEARCH_REVEAL_INTERNALS, req);
1490 if (ret != LDB_SUCCESS) {
1494 if (rmd_is_just_resorted == false) {
1495 *seq_num = find_max_local_usn(omd);
1497 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1500 * The test here now allows for a new
1501 * replPropertyMetaData with no change, if was
1502 * just dbcheck re-sorting the values.
1504 if (*seq_num <= db_seq) {
1505 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1506 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1507 (long long)*seq_num, (long long)db_seq));
1508 return LDB_ERR_OPERATIONS_ERROR;
1513 /* search for the existing replPropertyMetaDataBlob. We need
1514 * to use REVEAL and ask for DNs in storage format to support
1515 * the check for values being the same in
1516 * replmd_update_rpmd_element()
1518 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1519 DSDB_FLAG_NEXT_MODULE |
1520 DSDB_SEARCH_SHOW_RECYCLED |
1521 DSDB_SEARCH_SHOW_EXTENDED_DN |
1522 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1523 DSDB_SEARCH_REVEAL_INTERNALS, req);
1524 if (ret != LDB_SUCCESS) {
1528 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1530 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1531 ldb_dn_get_linearized(msg->dn)));
1532 return LDB_ERR_OPERATIONS_ERROR;
1535 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1536 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1537 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1538 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1539 ldb_dn_get_linearized(msg->dn)));
1540 return LDB_ERR_OPERATIONS_ERROR;
1543 if (omd.version != 1) {
1544 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1545 omd.version, ldb_dn_get_linearized(msg->dn)));
1546 return LDB_ERR_OPERATIONS_ERROR;
1549 for (i=0; i<msg->num_elements; i++) {
1550 struct ldb_message_element *old_el;
1551 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1552 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1556 if (ret != LDB_SUCCESS) {
1560 if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1561 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1568 * Assert that we have an objectClass attribute - this is major
1569 * corruption if we don't have this!
1571 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1572 if (objectclass_el != NULL) {
1574 * Now check if this objectClass means we need to do urgent replication
1576 if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1580 } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1581 ldb_asprintf_errstring(ldb, __location__
1582 ": objectClass missing on %s\n",
1583 ldb_dn_get_linearized(msg->dn));
1584 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1588 * replmd_update_rpmd_element has done an update if the
1591 if (*seq_num != 0 || rmd_is_just_resorted == true) {
1592 struct ldb_val *md_value;
1593 struct ldb_message_element *el;
1595 /*if we are RODC and this is a DRSR update then its ok*/
1596 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1597 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1598 unsigned instanceType;
1600 ret = samdb_rodc(ldb, rodc);
1601 if (ret != LDB_SUCCESS) {
1602 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1604 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1605 return LDB_ERR_REFERRAL;
1608 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1609 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1610 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1611 "cannot change replicated attribute on partial replica");
1615 md_value = talloc(msg, struct ldb_val);
1616 if (md_value == NULL) {
1618 return LDB_ERR_OPERATIONS_ERROR;
1621 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, schema, msg->dn);
1622 if (ret != LDB_SUCCESS) {
1623 ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
1627 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1628 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1629 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1630 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1631 ldb_dn_get_linearized(msg->dn)));
1632 return LDB_ERR_OPERATIONS_ERROR;
1635 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1636 if (ret != LDB_SUCCESS) {
1637 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1638 ldb_dn_get_linearized(msg->dn)));
1643 el->values = md_value;
1650 struct dsdb_dn *dsdb_dn;
1655 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1657 return GUID_compare(pdn1->guid, pdn2->guid);
1660 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1661 unsigned int count, struct GUID *guid,
1664 struct parsed_dn *ret;
1666 if (dn && GUID_all_zero(guid)) {
1667 /* when updating a link using DRS, we sometimes get a
1668 NULL GUID. We then need to try and match by DN */
1669 for (i=0; i<count; i++) {
1670 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1671 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1677 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1682 get a series of message element values as an array of DNs and GUIDs
1683 the result is sorted by GUID
1685 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1686 struct ldb_message_element *el, struct parsed_dn **pdn,
1687 const char *ldap_oid, struct ldb_request *parent)
1690 struct ldb_context *ldb = ldb_module_get_ctx(module);
1697 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1699 ldb_module_oom(module);
1700 return LDB_ERR_OPERATIONS_ERROR;
1703 for (i=0; i<el->num_values; i++) {
1704 struct ldb_val *v = &el->values[i];
1707 struct parsed_dn *p;
1711 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1712 if (p->dsdb_dn == NULL) {
1713 return LDB_ERR_INVALID_DN_SYNTAX;
1716 dn = p->dsdb_dn->dn;
1718 p->guid = talloc(*pdn, struct GUID);
1719 if (p->guid == NULL) {
1720 ldb_module_oom(module);
1721 return LDB_ERR_OPERATIONS_ERROR;
1724 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1725 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1726 /* we got a DN without a GUID - go find the GUID */
1727 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1728 if (ret != LDB_SUCCESS) {
1729 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1730 ldb_dn_get_linearized(dn));
1731 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1732 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1733 ldb_attr_cmp(el->name, "member") == 0) {
1734 return LDB_ERR_UNWILLING_TO_PERFORM;
1738 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1739 if (ret != LDB_SUCCESS) {
1742 } else if (!NT_STATUS_IS_OK(status)) {
1743 return LDB_ERR_OPERATIONS_ERROR;
1746 /* keep a pointer to the original ldb_val */
1750 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1756 build a new extended DN, including all meta data fields
1758 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1759 RMD_ADDTIME = originating_add_time
1760 RMD_INVOCID = originating_invocation_id
1761 RMD_CHANGETIME = originating_change_time
1762 RMD_ORIGINATING_USN = originating_usn
1763 RMD_LOCAL_USN = local_usn
1764 RMD_VERSION = version
1766 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1767 const struct GUID *invocation_id, uint64_t seq_num,
1768 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1770 struct ldb_dn *dn = dsdb_dn->dn;
1771 const char *tstring, *usn_string, *flags_string;
1772 struct ldb_val tval;
1774 struct ldb_val usnv, local_usnv;
1775 struct ldb_val vers, flagsv;
1778 const char *dnstring;
1780 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1782 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1784 return LDB_ERR_OPERATIONS_ERROR;
1786 tval = data_blob_string_const(tstring);
1788 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1790 return LDB_ERR_OPERATIONS_ERROR;
1792 usnv = data_blob_string_const(usn_string);
1794 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1796 return LDB_ERR_OPERATIONS_ERROR;
1798 local_usnv = data_blob_string_const(usn_string);
1800 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1802 return LDB_ERR_OPERATIONS_ERROR;
1804 vers = data_blob_string_const(vstring);
1806 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1807 if (!NT_STATUS_IS_OK(status)) {
1808 return LDB_ERR_OPERATIONS_ERROR;
1811 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1812 if (!flags_string) {
1813 return LDB_ERR_OPERATIONS_ERROR;
1815 flagsv = data_blob_string_const(flags_string);
1817 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1818 if (ret != LDB_SUCCESS) return ret;
1819 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1820 if (ret != LDB_SUCCESS) return ret;
1821 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1822 if (ret != LDB_SUCCESS) return ret;
1823 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1824 if (ret != LDB_SUCCESS) return ret;
1825 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1826 if (ret != LDB_SUCCESS) return ret;
1827 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1828 if (ret != LDB_SUCCESS) return ret;
1829 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1830 if (ret != LDB_SUCCESS) return ret;
1832 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1833 if (dnstring == NULL) {
1834 return LDB_ERR_OPERATIONS_ERROR;
1836 *v = data_blob_string_const(dnstring);
1841 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1842 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1843 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1844 uint32_t version, bool deleted);
1847 check if any links need upgrading from w2k format
1849 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.
1851 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1854 for (i=0; i<count; i++) {
1859 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1860 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1864 /* it's an old one that needs upgrading */
1865 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1867 if (ret != LDB_SUCCESS) {
1875 update an extended DN, including all meta data fields
1877 see replmd_build_la_val for value names
1879 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1880 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1881 uint64_t usn, uint64_t local_usn, NTTIME nttime,
1882 uint32_t version, bool deleted)
1884 struct ldb_dn *dn = dsdb_dn->dn;
1885 const char *tstring, *usn_string, *flags_string;
1886 struct ldb_val tval;
1888 struct ldb_val usnv, local_usnv;
1889 struct ldb_val vers, flagsv;
1890 const struct ldb_val *old_addtime;
1891 uint32_t old_version;
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)usn);
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 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1917 if (!NT_STATUS_IS_OK(status)) {
1918 return LDB_ERR_OPERATIONS_ERROR;
1921 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1922 if (!flags_string) {
1923 return LDB_ERR_OPERATIONS_ERROR;
1925 flagsv = data_blob_string_const(flags_string);
1927 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1928 if (ret != LDB_SUCCESS) return ret;
1930 /* get the ADDTIME from the original */
1931 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1932 if (old_addtime == NULL) {
1933 old_addtime = &tval;
1935 if (dsdb_dn != old_dsdb_dn ||
1936 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1937 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1938 if (ret != LDB_SUCCESS) return ret;
1941 /* use our invocation id */
1942 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1943 if (ret != LDB_SUCCESS) return ret;
1945 /* changetime is the current time */
1946 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1947 if (ret != LDB_SUCCESS) return ret;
1949 /* update the USN */
1950 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1951 if (ret != LDB_SUCCESS) return ret;
1953 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1954 if (ret != LDB_SUCCESS) return ret;
1956 /* increase the version by 1 */
1957 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1958 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1959 version = old_version+1;
1961 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1962 vers = data_blob_string_const(vstring);
1963 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1964 if (ret != LDB_SUCCESS) return ret;
1966 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1967 if (dnstring == NULL) {
1968 return LDB_ERR_OPERATIONS_ERROR;
1970 *v = data_blob_string_const(dnstring);
1976 handle adding a linked attribute
1978 static int replmd_modify_la_add(struct ldb_module *module,
1979 const struct dsdb_schema *schema,
1980 struct ldb_message *msg,
1981 struct ldb_message_element *el,
1982 struct ldb_message_element *old_el,
1983 const struct dsdb_attribute *schema_attr,
1986 struct GUID *msg_guid,
1987 struct ldb_request *parent)
1990 struct parsed_dn *dns, *old_dns;
1991 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1993 struct ldb_val *new_values = NULL;
1994 unsigned int num_new_values = 0;
1995 unsigned old_num_values = old_el?old_el->num_values:0;
1996 const struct GUID *invocation_id;
1997 struct ldb_context *ldb = ldb_module_get_ctx(module);
2000 unix_to_nt_time(&now, t);
2002 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2003 if (ret != LDB_SUCCESS) {
2004 talloc_free(tmp_ctx);
2008 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2009 if (ret != LDB_SUCCESS) {
2010 talloc_free(tmp_ctx);
2014 invocation_id = samdb_ntds_invocation_id(ldb);
2015 if (!invocation_id) {
2016 talloc_free(tmp_ctx);
2017 return LDB_ERR_OPERATIONS_ERROR;
2020 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2021 if (ret != LDB_SUCCESS) {
2022 talloc_free(tmp_ctx);
2026 /* for each new value, see if it exists already with the same GUID */
2027 for (i=0; i<el->num_values; i++) {
2028 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
2030 /* this is a new linked attribute value */
2031 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
2032 if (new_values == NULL) {
2033 ldb_module_oom(module);
2034 talloc_free(tmp_ctx);
2035 return LDB_ERR_OPERATIONS_ERROR;
2037 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2038 invocation_id, seq_num, seq_num, now, 0, false);
2039 if (ret != LDB_SUCCESS) {
2040 talloc_free(tmp_ctx);
2045 /* this is only allowed if the GUID was
2046 previously deleted. */
2047 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2049 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2050 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
2051 el->name, GUID_string(tmp_ctx, p->guid));
2052 talloc_free(tmp_ctx);
2053 /* error codes for 'member' need to be
2055 if (ldb_attr_cmp(el->name, "member") == 0) {
2056 return LDB_ERR_ENTRY_ALREADY_EXISTS;
2058 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2061 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
2062 invocation_id, seq_num, seq_num, now, 0, false);
2063 if (ret != LDB_SUCCESS) {
2064 talloc_free(tmp_ctx);
2069 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
2070 if (ret != LDB_SUCCESS) {
2071 talloc_free(tmp_ctx);
2076 /* add the new ones on to the end of the old values, constructing a new el->values */
2077 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2079 old_num_values+num_new_values);
2080 if (el->values == NULL) {
2081 ldb_module_oom(module);
2082 return LDB_ERR_OPERATIONS_ERROR;
2085 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
2086 el->num_values = old_num_values + num_new_values;
2088 talloc_steal(msg->elements, el->values);
2089 talloc_steal(el->values, new_values);
2091 talloc_free(tmp_ctx);
2093 /* we now tell the backend to replace all existing values
2094 with the one we have constructed */
2095 el->flags = LDB_FLAG_MOD_REPLACE;
2102 handle deleting all active linked attributes
2104 static int replmd_modify_la_delete(struct ldb_module *module,
2105 const struct dsdb_schema *schema,
2106 struct ldb_message *msg,
2107 struct ldb_message_element *el,
2108 struct ldb_message_element *old_el,
2109 const struct dsdb_attribute *schema_attr,
2112 struct GUID *msg_guid,
2113 struct ldb_request *parent)
2116 struct parsed_dn *dns, *old_dns;
2117 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2119 const struct GUID *invocation_id;
2120 struct ldb_context *ldb = ldb_module_get_ctx(module);
2123 unix_to_nt_time(&now, t);
2125 /* check if there is nothing to delete */
2126 if ((!old_el || old_el->num_values == 0) &&
2127 el->num_values == 0) {
2131 if (!old_el || old_el->num_values == 0) {
2132 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2135 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2136 if (ret != LDB_SUCCESS) {
2137 talloc_free(tmp_ctx);
2141 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2142 if (ret != LDB_SUCCESS) {
2143 talloc_free(tmp_ctx);
2147 invocation_id = samdb_ntds_invocation_id(ldb);
2148 if (!invocation_id) {
2149 return LDB_ERR_OPERATIONS_ERROR;
2152 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
2153 if (ret != LDB_SUCCESS) {
2154 talloc_free(tmp_ctx);
2160 /* see if we are being asked to delete any links that
2161 don't exist or are already deleted */
2162 for (i=0; i<el->num_values; i++) {
2163 struct parsed_dn *p = &dns[i];
2164 struct parsed_dn *p2;
2167 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
2169 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
2170 el->name, GUID_string(tmp_ctx, p->guid));
2171 if (ldb_attr_cmp(el->name, "member") == 0) {
2172 return LDB_ERR_UNWILLING_TO_PERFORM;
2174 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2177 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
2178 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2179 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
2180 el->name, GUID_string(tmp_ctx, p->guid));
2181 if (ldb_attr_cmp(el->name, "member") == 0) {
2182 return LDB_ERR_UNWILLING_TO_PERFORM;
2184 return LDB_ERR_NO_SUCH_ATTRIBUTE;
2189 /* for each new value, see if it exists already with the same GUID
2190 if it is not already deleted and matches the delete list then delete it
2192 for (i=0; i<old_el->num_values; i++) {
2193 struct parsed_dn *p = &old_dns[i];
2196 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
2200 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2201 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2203 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2204 invocation_id, seq_num, seq_num, now, 0, true);
2205 if (ret != LDB_SUCCESS) {
2206 talloc_free(tmp_ctx);
2210 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2211 if (ret != LDB_SUCCESS) {
2212 talloc_free(tmp_ctx);
2217 el->values = talloc_steal(msg->elements, old_el->values);
2218 el->num_values = old_el->num_values;
2220 talloc_free(tmp_ctx);
2222 /* we now tell the backend to replace all existing values
2223 with the one we have constructed */
2224 el->flags = LDB_FLAG_MOD_REPLACE;
2230 handle replacing a linked attribute
2232 static int replmd_modify_la_replace(struct ldb_module *module,
2233 const struct dsdb_schema *schema,
2234 struct ldb_message *msg,
2235 struct ldb_message_element *el,
2236 struct ldb_message_element *old_el,
2237 const struct dsdb_attribute *schema_attr,
2240 struct GUID *msg_guid,
2241 struct ldb_request *parent)
2244 struct parsed_dn *dns, *old_dns;
2245 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2247 const struct GUID *invocation_id;
2248 struct ldb_context *ldb = ldb_module_get_ctx(module);
2249 struct ldb_val *new_values = NULL;
2250 unsigned int num_new_values = 0;
2251 unsigned int old_num_values = old_el?old_el->num_values:0;
2254 unix_to_nt_time(&now, t);
2256 /* check if there is nothing to replace */
2257 if ((!old_el || old_el->num_values == 0) &&
2258 el->num_values == 0) {
2262 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2263 if (ret != LDB_SUCCESS) {
2264 talloc_free(tmp_ctx);
2268 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2269 if (ret != LDB_SUCCESS) {
2270 talloc_free(tmp_ctx);
2274 invocation_id = samdb_ntds_invocation_id(ldb);
2275 if (!invocation_id) {
2276 return LDB_ERR_OPERATIONS_ERROR;
2279 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2280 if (ret != LDB_SUCCESS) {
2281 talloc_free(tmp_ctx);
2285 /* mark all the old ones as deleted */
2286 for (i=0; i<old_num_values; i++) {
2287 struct parsed_dn *old_p = &old_dns[i];
2288 struct parsed_dn *p;
2289 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2291 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2293 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2294 if (ret != LDB_SUCCESS) {
2295 talloc_free(tmp_ctx);
2299 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2301 /* we don't delete it if we are re-adding it */
2305 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2306 invocation_id, seq_num, seq_num, now, 0, true);
2307 if (ret != LDB_SUCCESS) {
2308 talloc_free(tmp_ctx);
2313 /* for each new value, either update its meta-data, or add it
2316 for (i=0; i<el->num_values; i++) {
2317 struct parsed_dn *p = &dns[i], *old_p;
2320 (old_p = parsed_dn_find(old_dns,
2321 old_num_values, p->guid, NULL)) != NULL) {
2322 /* update in place */
2323 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2324 old_p->dsdb_dn, invocation_id,
2325 seq_num, seq_num, now, 0, false);
2326 if (ret != LDB_SUCCESS) {
2327 talloc_free(tmp_ctx);
2332 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2334 if (new_values == NULL) {
2335 ldb_module_oom(module);
2336 talloc_free(tmp_ctx);
2337 return LDB_ERR_OPERATIONS_ERROR;
2339 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2340 invocation_id, seq_num, seq_num, now, 0, false);
2341 if (ret != LDB_SUCCESS) {
2342 talloc_free(tmp_ctx);
2348 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2349 if (ret != LDB_SUCCESS) {
2350 talloc_free(tmp_ctx);
2355 /* add the new values to the end of old_el */
2356 if (num_new_values != 0) {
2357 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2358 struct ldb_val, old_num_values+num_new_values);
2359 if (el->values == NULL) {
2360 ldb_module_oom(module);
2361 return LDB_ERR_OPERATIONS_ERROR;
2363 memcpy(&el->values[old_num_values], &new_values[0],
2364 sizeof(struct ldb_val)*num_new_values);
2365 el->num_values = old_num_values + num_new_values;
2366 talloc_steal(msg->elements, new_values);
2368 el->values = old_el->values;
2369 el->num_values = old_el->num_values;
2370 talloc_steal(msg->elements, el->values);
2373 talloc_free(tmp_ctx);
2375 /* we now tell the backend to replace all existing values
2376 with the one we have constructed */
2377 el->flags = LDB_FLAG_MOD_REPLACE;
2384 handle linked attributes in modify requests
2386 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2387 struct ldb_message *msg,
2388 uint64_t seq_num, time_t t,
2389 struct ldb_request *parent)
2391 struct ldb_result *res;
2394 struct ldb_context *ldb = ldb_module_get_ctx(module);
2395 struct ldb_message *old_msg;
2397 const struct dsdb_schema *schema;
2398 struct GUID old_guid;
2401 /* there the replmd_update_rpmd code has already
2402 * checked and saw that there are no linked
2407 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2408 /* don't do anything special for linked attributes */
2412 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2413 DSDB_FLAG_NEXT_MODULE |
2414 DSDB_SEARCH_SHOW_RECYCLED |
2415 DSDB_SEARCH_REVEAL_INTERNALS |
2416 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2418 if (ret != LDB_SUCCESS) {
2421 schema = dsdb_get_schema(ldb, res);
2423 return LDB_ERR_OPERATIONS_ERROR;
2426 old_msg = res->msgs[0];
2428 old_guid = samdb_result_guid(old_msg, "objectGUID");
2430 for (i=0; i<msg->num_elements; i++) {
2431 struct ldb_message_element *el = &msg->elements[i];
2432 struct ldb_message_element *old_el, *new_el;
2433 const struct dsdb_attribute *schema_attr
2434 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2436 ldb_asprintf_errstring(ldb,
2437 "%s: attribute %s is not a valid attribute in schema",
2438 __FUNCTION__, el->name);
2439 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2441 if (schema_attr->linkID == 0) {
2444 if ((schema_attr->linkID & 1) == 1) {
2445 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2448 /* Odd is for the target. Illegal to modify */
2449 ldb_asprintf_errstring(ldb,
2450 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2451 return LDB_ERR_UNWILLING_TO_PERFORM;
2453 old_el = ldb_msg_find_element(old_msg, el->name);
2454 switch (el->flags & LDB_FLAG_MOD_MASK) {
2455 case LDB_FLAG_MOD_REPLACE:
2456 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2458 case LDB_FLAG_MOD_DELETE:
2459 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2461 case LDB_FLAG_MOD_ADD:
2462 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2465 ldb_asprintf_errstring(ldb,
2466 "invalid flags 0x%x for %s linked attribute",
2467 el->flags, el->name);
2468 return LDB_ERR_UNWILLING_TO_PERFORM;
2470 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2471 ldb_asprintf_errstring(ldb,
2472 "Attribute %s is single valued but more than one value has been supplied",
2474 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2476 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2481 if (ret != LDB_SUCCESS) {
2485 ldb_msg_remove_attr(old_msg, el->name);
2487 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2488 new_el->num_values = el->num_values;
2489 new_el->values = talloc_steal(msg->elements, el->values);
2491 /* TODO: this relises a bit too heavily on the exact
2492 behaviour of ldb_msg_find_element and
2493 ldb_msg_remove_element */
2494 old_el = ldb_msg_find_element(msg, el->name);
2496 ldb_msg_remove_element(msg, old_el);
2507 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2509 struct samldb_msds_intid_persistant *msds_intid_struct;
2510 struct ldb_context *ldb;
2511 struct replmd_replicated_request *ac;
2512 struct ldb_request *down_req;
2513 struct ldb_message *msg;
2514 time_t t = time(NULL);
2516 bool is_urgent = false, rodc = false;
2517 bool is_schema_nc = false;
2518 unsigned int functional_level;
2519 const struct ldb_message_element *guid_el = NULL;
2520 struct ldb_control *sd_propagation_control;
2521 struct replmd_private *replmd_private =
2522 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2524 /* do not manipulate our control entries */
2525 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2526 return ldb_next_request(module, req);
2529 sd_propagation_control = ldb_request_get_control(req,
2530 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2531 if (sd_propagation_control != NULL) {
2532 if (req->op.mod.message->num_elements != 1) {
2533 return ldb_module_operr(module);
2535 ret = strcmp(req->op.mod.message->elements[0].name,
2536 "nTSecurityDescriptor");
2538 return ldb_module_operr(module);
2541 return ldb_next_request(module, req);
2544 ldb = ldb_module_get_ctx(module);
2546 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2548 guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
2549 if (guid_el != NULL) {
2550 ldb_set_errstring(ldb,
2551 "replmd_modify: it's not allowed to change the objectGUID!");
2552 return LDB_ERR_CONSTRAINT_VIOLATION;
2555 ac = replmd_ctx_init(module, req);
2557 return ldb_module_oom(module);
2560 functional_level = dsdb_functional_level(ldb);
2562 /* we have to copy the message as the caller might have it as a const */
2563 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2567 return LDB_ERR_OPERATIONS_ERROR;
2570 ldb_msg_remove_attr(msg, "whenChanged");
2571 ldb_msg_remove_attr(msg, "uSNChanged");
2573 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2575 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2576 msg, &ac->seq_num, t, is_schema_nc,
2578 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2579 struct loadparm_context *lp_ctx;
2582 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2583 struct loadparm_context);
2585 referral = talloc_asprintf(req,
2587 lpcfg_dnsdomain(lp_ctx),
2588 ldb_dn_get_linearized(msg->dn));
2589 ret = ldb_module_send_referral(req, referral);
2594 if (ret != LDB_SUCCESS) {
2599 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2600 if (ret != LDB_SUCCESS) {
2606 * - replace the old object with the newly constructed one
2609 ac->is_urgent = is_urgent;
2611 ret = ldb_build_mod_req(&down_req, ldb, ac,
2614 ac, replmd_op_callback,
2616 LDB_REQ_SET_LOCATION(down_req);
2617 if (ret != LDB_SUCCESS) {
2622 /* current partition control is needed by "replmd_op_callback" */
2623 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2624 ret = ldb_request_add_control(down_req,
2625 DSDB_CONTROL_CURRENT_PARTITION_OID,
2627 if (ret != LDB_SUCCESS) {
2633 /* If we are in functional level 2000, then
2634 * replmd_modify_handle_linked_attribs will have done
2636 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2637 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2638 if (ret != LDB_SUCCESS) {
2644 talloc_steal(down_req, msg);
2646 /* we only change whenChanged and uSNChanged if the seq_num
2648 if (ac->seq_num != 0) {
2649 ret = add_time_element(msg, "whenChanged", t);
2650 if (ret != LDB_SUCCESS) {
2656 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2657 if (ret != LDB_SUCCESS) {
2664 if (!ldb_dn_compare_base(replmd_private->schema_dn, msg->dn)) {
2665 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2666 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2667 if (msds_intid_struct) {
2668 msds_intid_struct->usn = ac->seq_num;
2672 /* go on with the call chain */
2673 return ldb_next_request(module, down_req);
2676 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2679 handle a rename request
2681 On a rename we need to do an extra ldb_modify which sets the
2682 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2684 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2686 struct ldb_context *ldb;
2687 struct replmd_replicated_request *ac;
2689 struct ldb_request *down_req;
2691 /* do not manipulate our control entries */
2692 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2693 return ldb_next_request(module, req);
2696 ldb = ldb_module_get_ctx(module);
2698 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2700 ac = replmd_ctx_init(module, req);
2702 return ldb_module_oom(module);
2705 ret = ldb_build_rename_req(&down_req, ldb, ac,
2706 ac->req->op.rename.olddn,
2707 ac->req->op.rename.newdn,
2709 ac, replmd_rename_callback,
2711 LDB_REQ_SET_LOCATION(down_req);
2712 if (ret != LDB_SUCCESS) {
2717 /* go on with the call chain */
2718 return ldb_next_request(module, down_req);
2721 /* After the rename is compleated, update the whenchanged etc */
2722 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2724 struct ldb_context *ldb;
2725 struct ldb_request *down_req;
2726 struct ldb_message *msg;
2727 const struct dsdb_attribute *rdn_attr;
2728 const char *rdn_name;
2729 const struct ldb_val *rdn_val;
2730 const char *attrs[5] = { NULL, };
2731 time_t t = time(NULL);
2733 bool is_urgent = false, rodc = false;
2735 struct replmd_replicated_request *ac =
2736 talloc_get_type(req->context, struct replmd_replicated_request);
2737 struct replmd_private *replmd_private =
2738 talloc_get_type(ldb_module_get_private(ac->module),
2739 struct replmd_private);
2741 ldb = ldb_module_get_ctx(ac->module);
2743 if (ares->error != LDB_SUCCESS) {
2744 return ldb_module_done(ac->req, ares->controls,
2745 ares->response, ares->error);
2748 if (ares->type != LDB_REPLY_DONE) {
2749 ldb_set_errstring(ldb,
2750 "invalid ldb_reply_type in callback");
2752 return ldb_module_done(ac->req, NULL, NULL,
2753 LDB_ERR_OPERATIONS_ERROR);
2757 * - replace the old object with the newly constructed one
2760 msg = ldb_msg_new(ac);
2763 return LDB_ERR_OPERATIONS_ERROR;
2766 msg->dn = ac->req->op.rename.newdn;
2768 is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
2770 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2771 if (rdn_name == NULL) {
2773 return ldb_module_done(ac->req, NULL, NULL,
2777 /* normalize the rdn attribute name */
2778 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2779 if (rdn_attr == NULL) {
2781 return ldb_module_done(ac->req, NULL, NULL,
2784 rdn_name = rdn_attr->lDAPDisplayName;
2786 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2787 if (rdn_val == NULL) {
2789 return ldb_module_done(ac->req, NULL, NULL,
2793 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2795 return ldb_module_done(ac->req, NULL, NULL,
2798 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2800 return ldb_module_done(ac->req, NULL, NULL,
2803 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2805 return ldb_module_done(ac->req, NULL, NULL,
2808 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2810 return ldb_module_done(ac->req, NULL, NULL,
2815 * here we let replmd_update_rpmd() only search for
2816 * the existing "replPropertyMetaData" and rdn_name attributes.
2818 * We do not want the existing "name" attribute as
2819 * the "name" attribute needs to get the version
2820 * updated on rename even if the rdn value hasn't changed.
2822 * This is the diff of the meta data, for a moved user
2823 * on a w2k8r2 server:
2826 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2827 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2828 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2829 * version : 0x00000001 (1)
2830 * reserved : 0x00000000 (0)
2831 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2832 * local_usn : 0x00000000000037a5 (14245)
2833 * array: struct replPropertyMetaData1
2834 * attid : DRSUAPI_ATTID_name (0x90001)
2835 * - version : 0x00000001 (1)
2836 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2837 * + version : 0x00000002 (2)
2838 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2839 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2840 * - originating_usn : 0x00000000000037a5 (14245)
2841 * - local_usn : 0x00000000000037a5 (14245)
2842 * + originating_usn : 0x0000000000003834 (14388)
2843 * + local_usn : 0x0000000000003834 (14388)
2844 * array: struct replPropertyMetaData1
2845 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2846 * version : 0x00000004 (4)
2848 attrs[0] = "replPropertyMetaData";
2849 attrs[1] = "objectClass";
2850 attrs[2] = "instanceType";
2851 attrs[3] = rdn_name;
2854 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2855 msg, &ac->seq_num, t,
2856 is_schema_nc, &is_urgent, &rodc);
2857 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2858 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2859 struct loadparm_context *lp_ctx;
2862 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2863 struct loadparm_context);
2865 referral = talloc_asprintf(req,
2867 lpcfg_dnsdomain(lp_ctx),
2868 ldb_dn_get_linearized(olddn));
2869 ret = ldb_module_send_referral(req, referral);
2871 return ldb_module_done(req, NULL, NULL, ret);
2874 if (ret != LDB_SUCCESS) {
2876 return ldb_module_done(ac->req, NULL, NULL, ret);
2879 if (ac->seq_num == 0) {
2881 return ldb_module_done(ac->req, NULL, NULL,
2883 "internal error seq_num == 0"));
2885 ac->is_urgent = is_urgent;
2887 ret = ldb_build_mod_req(&down_req, ldb, ac,
2890 ac, replmd_op_callback,
2892 LDB_REQ_SET_LOCATION(down_req);
2893 if (ret != LDB_SUCCESS) {
2898 /* current partition control is needed by "replmd_op_callback" */
2899 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2900 ret = ldb_request_add_control(down_req,
2901 DSDB_CONTROL_CURRENT_PARTITION_OID,
2903 if (ret != LDB_SUCCESS) {
2909 talloc_steal(down_req, msg);
2911 ret = add_time_element(msg, "whenChanged", t);
2912 if (ret != LDB_SUCCESS) {
2918 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2919 if (ret != LDB_SUCCESS) {
2925 /* go on with the call chain - do the modify after the rename */
2926 return ldb_next_request(ac->module, down_req);
2930 * remove links from objects that point at this object when an object
2931 * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
2932 * RemoveObj which states that link removal due to the object being
2933 * deleted is NOT an originating update - they just go away!
2936 static int replmd_delete_remove_link(struct ldb_module *module,
2937 const struct dsdb_schema *schema,
2939 struct ldb_message_element *el,
2940 const struct dsdb_attribute *sa,
2941 struct ldb_request *parent)
2944 TALLOC_CTX *tmp_ctx = talloc_new(module);
2945 struct ldb_context *ldb = ldb_module_get_ctx(module);
2947 for (i=0; i<el->num_values; i++) {
2948 struct dsdb_dn *dsdb_dn;
2952 struct ldb_message *msg;
2953 const struct dsdb_attribute *target_attr;
2954 struct ldb_message_element *el2;
2955 struct ldb_val dn_val;
2957 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2961 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2963 talloc_free(tmp_ctx);
2964 return LDB_ERR_OPERATIONS_ERROR;
2967 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2968 if (!NT_STATUS_IS_OK(status)) {
2969 talloc_free(tmp_ctx);
2970 return LDB_ERR_OPERATIONS_ERROR;
2973 /* remove the link */
2974 msg = ldb_msg_new(tmp_ctx);
2976 ldb_module_oom(module);
2977 talloc_free(tmp_ctx);
2978 return LDB_ERR_OPERATIONS_ERROR;
2982 msg->dn = dsdb_dn->dn;
2984 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2985 if (target_attr == NULL) {
2989 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2990 if (ret != LDB_SUCCESS) {
2991 ldb_module_oom(module);
2992 talloc_free(tmp_ctx);
2993 return LDB_ERR_OPERATIONS_ERROR;
2995 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2996 el2->values = &dn_val;
2997 el2->num_values = 1;
2999 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
3000 if (ret != LDB_SUCCESS) {
3001 talloc_free(tmp_ctx);
3005 talloc_free(tmp_ctx);
3011 handle update of replication meta data for deletion of objects
3013 This also handles the mapping of delete to a rename operation
3014 to allow deletes to be replicated.
3016 It also handles the incoming deleted objects, to ensure they are
3017 fully deleted here. In that case re_delete is true, and we do not
3018 use this as a signal to change the deleted state, just reinforce it.
3021 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
3023 int ret = LDB_ERR_OTHER;
3024 bool retb, disallow_move_on_delete;
3025 struct ldb_dn *old_dn, *new_dn;
3026 const char *rdn_name;
3027 const struct ldb_val *rdn_value, *new_rdn_value;
3029 struct ldb_context *ldb = ldb_module_get_ctx(module);
3030 const struct dsdb_schema *schema;
3031 struct ldb_message *msg, *old_msg;
3032 struct ldb_message_element *el;
3033 TALLOC_CTX *tmp_ctx;
3034 struct ldb_result *res, *parent_res;
3035 const char *preserved_attrs[] = {
3036 /* yes, this really is a hard coded list. See MS-ADTS
3037 section 3.1.1.5.5.1.1 */
3038 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
3039 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
3040 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
3041 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
3042 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
3043 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
3044 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
3045 "whenChanged", NULL};
3046 unsigned int i, el_count = 0;
3047 enum deletion_state deletion_state, next_deletion_state;
3049 if (ldb_dn_is_special(req->op.del.dn)) {
3050 return ldb_next_request(module, req);
3054 * We have to allow dbcheck to remove an object that
3055 * is beyond repair, and to do so totally. This could
3056 * mean we we can get a partial object from the other
3057 * DC, causing havoc, so dbcheck suggests
3058 * re-replication first. dbcheck sets both DBCHECK
3059 * and RELAX in this situation.
3061 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
3062 && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
3063 /* really, really remove it */
3064 return ldb_next_request(module, req);
3067 tmp_ctx = talloc_new(ldb);
3070 return LDB_ERR_OPERATIONS_ERROR;
3073 schema = dsdb_get_schema(ldb, tmp_ctx);
3075 talloc_free(tmp_ctx);
3076 return LDB_ERR_OPERATIONS_ERROR;
3079 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
3081 /* we need the complete msg off disk, so we can work out which
3082 attributes need to be removed */
3083 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
3084 DSDB_FLAG_NEXT_MODULE |
3085 DSDB_SEARCH_SHOW_RECYCLED |
3086 DSDB_SEARCH_REVEAL_INTERNALS |
3087 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
3088 if (ret != LDB_SUCCESS) {
3089 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3090 "repmd_delete: Failed to %s %s, because we failed to find it: %s",
3091 re_delete ? "re-delete" : "delete",
3092 ldb_dn_get_linearized(old_dn),
3093 ldb_errstring(ldb_module_get_ctx(module)));
3094 talloc_free(tmp_ctx);
3097 old_msg = res->msgs[0];
3099 replmd_deletion_state(module, old_msg,
3101 &next_deletion_state);
3103 /* This supports us noticing an incoming isDeleted and acting on it */
3105 SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
3106 next_deletion_state = deletion_state;
3109 if (next_deletion_state == OBJECT_REMOVED) {
3111 * We have to prevent objects being deleted, even if
3112 * the administrator really wants them gone, as
3113 * without the tombstone, we can get a partial object
3114 * from the other DC, causing havoc.
3116 * The only other valid case is when the 180 day
3117 * timeout has expired, when relax is specified.
3119 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
3120 /* it is already deleted - really remove it this time */
3121 talloc_free(tmp_ctx);
3122 return ldb_next_request(module, req);
3125 ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
3126 "This check is to prevent corruption of the replicated state.",
3127 ldb_dn_get_linearized(old_msg->dn));
3128 return LDB_ERR_UNWILLING_TO_PERFORM;
3131 rdn_name = ldb_dn_get_rdn_name(old_dn);
3132 rdn_value = ldb_dn_get_rdn_val(old_dn);
3133 if ((rdn_name == NULL) || (rdn_value == NULL)) {
3134 talloc_free(tmp_ctx);
3135 return ldb_operr(ldb);
3138 msg = ldb_msg_new(tmp_ctx);
3140 ldb_module_oom(module);
3141 talloc_free(tmp_ctx);
3142 return LDB_ERR_OPERATIONS_ERROR;
3147 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
3148 disallow_move_on_delete =
3149 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
3150 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
3152 /* work out where we will be renaming this object to */
3153 if (!disallow_move_on_delete) {
3154 struct ldb_dn *deleted_objects_dn;
3155 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
3156 &deleted_objects_dn);
3159 * We should not move objects if we can't find the
3160 * deleted objects DN. Not moving (or otherwise
3161 * harming) the Deleted Objects DN itself is handled
3164 if (re_delete && (ret != LDB_SUCCESS)) {
3165 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3166 if (new_dn == NULL) {
3167 ldb_module_oom(module);
3168 talloc_free(tmp_ctx);
3169 return LDB_ERR_OPERATIONS_ERROR;
3171 } else if (ret != LDB_SUCCESS) {
3172 /* this is probably an attempted delete on a partition
3173 * that doesn't allow delete operations, such as the
3174 * schema partition */
3175 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
3176 ldb_dn_get_linearized(old_dn));
3177 talloc_free(tmp_ctx);
3178 return LDB_ERR_UNWILLING_TO_PERFORM;
3180 new_dn = deleted_objects_dn;
3183 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3184 if (new_dn == NULL) {
3185 ldb_module_oom(module);
3186 talloc_free(tmp_ctx);
3187 return LDB_ERR_OPERATIONS_ERROR;
3191 if (deletion_state == OBJECT_NOT_DELETED) {
3192 /* get the objects GUID from the search we just did */
3193 guid = samdb_result_guid(old_msg, "objectGUID");
3195 /* Add a formatted child */
3196 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
3198 ldb_dn_escape_value(tmp_ctx, *rdn_value),
3199 GUID_string(tmp_ctx, &guid));
3201 ldb_asprintf_errstring(ldb, __location__
3202 ": Unable to add a formatted child to dn: %s",
3203 ldb_dn_get_linearized(new_dn));
3204 talloc_free(tmp_ctx);
3205 return LDB_ERR_OPERATIONS_ERROR;
3208 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
3209 if (ret != LDB_SUCCESS) {
3210 ldb_asprintf_errstring(ldb, __location__
3211 ": Failed to add isDeleted string to the msg");
3212 talloc_free(tmp_ctx);
3215 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3218 * No matter what has happened with other renames etc, try again to
3219 * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
3222 struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
3223 retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
3225 ldb_asprintf_errstring(ldb, __location__
3226 ": Unable to add a prepare rdn of %s",
3227 ldb_dn_get_linearized(rdn));
3228 talloc_free(tmp_ctx);
3229 return LDB_ERR_OPERATIONS_ERROR;
3231 SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
3233 retb = ldb_dn_add_child(new_dn, rdn);
3235 ldb_asprintf_errstring(ldb, __location__
3236 ": Unable to add rdn %s to base dn: %s",
3237 ldb_dn_get_linearized(rdn),
3238 ldb_dn_get_linearized(new_dn));
3239 talloc_free(tmp_ctx);
3240 return LDB_ERR_OPERATIONS_ERROR;
3245 now we need to modify the object in the following ways:
3247 - add isDeleted=TRUE
3248 - update rDN and name, with new rDN
3249 - remove linked attributes
3250 - remove objectCategory and sAMAccountType
3251 - remove attribs not on the preserved list
3252 - preserved if in above list, or is rDN
3253 - remove all linked attribs from this object
3254 - remove all links from other objects to this object
3255 - add lastKnownParent
3256 - update replPropertyMetaData?
3258 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
3261 if (deletion_state == OBJECT_NOT_DELETED) {
3262 struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
3263 char *parent_dn_str = NULL;
3265 /* we need the storage form of the parent GUID */
3266 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
3268 DSDB_FLAG_NEXT_MODULE |
3269 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3270 DSDB_SEARCH_REVEAL_INTERNALS|
3271 DSDB_SEARCH_SHOW_RECYCLED, req);
3272 if (ret != LDB_SUCCESS) {
3273 ldb_asprintf_errstring(ldb_module_get_ctx(module),
3274 "repmd_delete: Failed to %s %s, "
3275 "because we failed to find it's parent (%s): %s",
3276 re_delete ? "re-delete" : "delete",
3277 ldb_dn_get_linearized(old_dn),
3278 ldb_dn_get_linearized(parent_dn),
3279 ldb_errstring(ldb_module_get_ctx(module)));
3280 talloc_free(tmp_ctx);
3285 * Now we can use the DB version,
3286 * it will have the extended DN info in it
3288 parent_dn = parent_res->msgs[0]->dn;
3289 parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
3292 if (parent_dn_str == NULL) {
3293 talloc_free(tmp_ctx);
3294 return ldb_module_oom(module);
3297 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3299 if (ret != LDB_SUCCESS) {
3300 ldb_asprintf_errstring(ldb, __location__
3301 ": Failed to add lastKnownParent "
3302 "string when deleting %s",
3303 ldb_dn_get_linearized(old_dn));
3304 talloc_free(tmp_ctx);
3307 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3309 if (next_deletion_state == OBJECT_DELETED) {
3310 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3311 if (ret != LDB_SUCCESS) {
3312 ldb_asprintf_errstring(ldb, __location__
3313 ": Failed to add msDS-LastKnownRDN "
3314 "string when deleting %s",
3315 ldb_dn_get_linearized(old_dn));
3316 talloc_free(tmp_ctx);
3319 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3323 switch (next_deletion_state) {
3325 case OBJECT_RECYCLED:
3326 case OBJECT_TOMBSTONE:
3329 * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
3330 * describes what must be removed from a tombstone
3333 * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
3334 * describes what must be removed from a recycled
3340 * we also mark it as recycled, meaning this object can't be
3341 * recovered (we are stripping its attributes).
3342 * This is done only if we have this schema object of course ...
3343 * This behavior is identical to the one of Windows 2008R2 which
3344 * always set the isRecycled attribute, even if the recycle-bin is
3345 * not activated and what ever the forest level is.
3347 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3348 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3349 if (ret != LDB_SUCCESS) {
3350 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3351 ldb_module_oom(module);
3352 talloc_free(tmp_ctx);
3355 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3358 /* work out which of the old attributes we will be removing */
3359 for (i=0; i<old_msg->num_elements; i++) {
3360 const struct dsdb_attribute *sa;
3361 el = &old_msg->elements[i];
3362 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3364 talloc_free(tmp_ctx);
3365 return LDB_ERR_OPERATIONS_ERROR;
3367 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3368 /* don't remove the rDN */
3371 if (sa->linkID && (sa->linkID & 1)) {
3373 we have a backlink in this object
3374 that needs to be removed. We're not
3375 allowed to remove it directly
3376 however, so we instead setup a
3377 modify to delete the corresponding
3380 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3381 if (ret != LDB_SUCCESS) {
3382 const char *old_dn_str
3383 = ldb_dn_get_linearized(old_dn);
3384 ldb_asprintf_errstring(ldb,
3386 ": Failed to remove backlink of "
3387 "%s when deleting %s",
3390 talloc_free(tmp_ctx);
3391 return LDB_ERR_OPERATIONS_ERROR;
3393 /* now we continue, which means we
3394 won't remove this backlink
3400 if (ldb_attr_in_list(preserved_attrs, el->name)) {
3403 if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
3407 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3408 if (ret != LDB_SUCCESS) {
3409 talloc_free(tmp_ctx);
3410 ldb_module_oom(module);
3417 case OBJECT_DELETED:
3419 * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
3420 * describes what must be removed from a deleted
3424 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3425 if (ret != LDB_SUCCESS) {
3426 talloc_free(tmp_ctx);
3427 ldb_module_oom(module);
3431 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3432 if (ret != LDB_SUCCESS) {
3433 talloc_free(tmp_ctx);
3434 ldb_module_oom(module);
3444 if (deletion_state == OBJECT_NOT_DELETED) {
3445 const struct dsdb_attribute *sa;
3447 /* work out what the new rdn value is, for updating the
3448 rDN and name fields */
3449 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3450 if (new_rdn_value == NULL) {
3451 talloc_free(tmp_ctx);
3452 return ldb_operr(ldb);
3455 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3457 talloc_free(tmp_ctx);
3458 return LDB_ERR_OPERATIONS_ERROR;
3461 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3463 if (ret != LDB_SUCCESS) {
3464 talloc_free(tmp_ctx);
3467 el->flags = LDB_FLAG_MOD_REPLACE;
3469 el = ldb_msg_find_element(old_msg, "name");
3471 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3472 if (ret != LDB_SUCCESS) {
3473 talloc_free(tmp_ctx);
3476 el->flags = LDB_FLAG_MOD_REPLACE;
3481 * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
3486 * No matter what has happned with other renames, try again to
3487 * get this to be under the deleted DN.
3489 if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
3490 /* now rename onto the new DN */
3491 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3492 if (ret != LDB_SUCCESS){
3493 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3494 ldb_dn_get_linearized(old_dn),
3495 ldb_dn_get_linearized(new_dn),
3496 ldb_errstring(ldb)));
3497 talloc_free(tmp_ctx);
3503 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3504 if (ret != LDB_SUCCESS) {
3505 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3506 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3507 talloc_free(tmp_ctx);
3511 talloc_free(tmp_ctx);
3513 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3516 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
3518 return replmd_delete_internals(module, req, false);
3522 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3527 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3529 int ret = LDB_ERR_OTHER;
3530 /* TODO: do some error mapping */
3532 /* Let the caller know the full WERROR */
3533 ar->objs->error = status;
3539 static struct replPropertyMetaData1 *
3540 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3541 enum drsuapi_DsAttributeId attid)
3544 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3546 for (i = 0; i < rpmd_ctr->count; i++) {
3547 if (rpmd_ctr->array[i].attid == attid) {
3548 return &rpmd_ctr->array[i];
3556 return true if an update is newer than an existing entry
3557 see section 5.11 of MS-ADTS
3559 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3560 const struct GUID *update_invocation_id,
3561 uint32_t current_version,
3562 uint32_t update_version,
3563 NTTIME current_change_time,
3564 NTTIME update_change_time)
3566 if (update_version != current_version) {
3567 return update_version > current_version;
3569 if (update_change_time != current_change_time) {
3570 return update_change_time > current_change_time;
3572 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3575 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3576 struct replPropertyMetaData1 *new_m)
3578 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3579 &new_m->originating_invocation_id,
3582 cur_m->originating_change_time,
3583 new_m->originating_change_time);
3590 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3592 const struct ldb_val *rdn_val;
3593 const char *rdn_name;
3594 struct ldb_dn *new_dn;
3596 rdn_val = ldb_dn_get_rdn_val(dn);
3597 rdn_name = ldb_dn_get_rdn_name(dn);
3598 if (!rdn_val || !rdn_name) {
3602 new_dn = ldb_dn_copy(mem_ctx, dn);
3607 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3611 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3613 ldb_dn_escape_value(new_dn, *rdn_val),
3614 GUID_string(new_dn, guid))) {
3623 perform a modify operation which sets the rDN and name attributes to
3624 their current values. This has the effect of changing these
3625 attributes to have been last updated by the current DC. This is
3626 needed to ensure that renames performed as part of conflict
3627 resolution are propogated to other DCs
3629 static int replmd_name_modify(struct replmd_replicated_request *ar,
3630 struct ldb_request *req, struct ldb_dn *dn)
3632 struct ldb_message *msg;
3633 const char *rdn_name;
3634 const struct ldb_val *rdn_val;
3635 const struct dsdb_attribute *rdn_attr;
3638 msg = ldb_msg_new(req);
3644 rdn_name = ldb_dn_get_rdn_name(dn);
3645 if (rdn_name == NULL) {
3649 /* normalize the rdn attribute name */
3650 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3651 if (rdn_attr == NULL) {
3654 rdn_name = rdn_attr->lDAPDisplayName;
3656 rdn_val = ldb_dn_get_rdn_val(dn);
3657 if (rdn_val == NULL) {
3661 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3664 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3667 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3670 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3674 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3675 if (ret != LDB_SUCCESS) {
3676 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3677 ldb_dn_get_linearized(dn),
3678 ldb_errstring(ldb_module_get_ctx(ar->module))));
3688 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3689 ldb_dn_get_linearized(dn)));
3690 return LDB_ERR_OPERATIONS_ERROR;
3695 callback for conflict DN handling where we have renamed the incoming
3696 record. After renaming it, we need to ensure the change of name and
3697 rDN for the incoming record is seen as an originating update by this DC.
3699 This also handles updating lastKnownParent for entries sent to lostAndFound
3701 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3703 struct replmd_replicated_request *ar =
3704 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3705 struct ldb_dn *conflict_dn = NULL;
3708 if (ares->error != LDB_SUCCESS) {
3709 /* call the normal callback for everything except success */
3710 return replmd_op_callback(req, ares);
3713 switch (req->operation) {
3715 conflict_dn = req->op.add.message->dn;
3718 conflict_dn = req->op.mod.message->dn;
3721 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3724 /* perform a modify of the rDN and name of the record */
3725 ret = replmd_name_modify(ar, req, conflict_dn);
3726 if (ret != LDB_SUCCESS) {
3728 return replmd_op_callback(req, ares);
3731 if (ar->objs->objects[ar->index_current].last_known_parent) {
3732 struct ldb_message *msg = ldb_msg_new(req);
3734 ldb_module_oom(ar->module);
3735 return LDB_ERR_OPERATIONS_ERROR;
3738 msg->dn = req->op.add.message->dn;
3740 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3741 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3742 if (ret != LDB_SUCCESS) {
3743 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3744 ldb_module_oom(ar->module);
3747 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3749 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3750 if (ret != LDB_SUCCESS) {
3751 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3752 ldb_dn_get_linearized(msg->dn),
3753 ldb_errstring(ldb_module_get_ctx(ar->module))));
3759 return replmd_op_callback(req, ares);
3763 callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename()
3764 This copes with the creation of conflict records in the case where
3765 the DN exists, but with a different objectGUID
3767 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))
3769 struct ldb_dn *conflict_dn;
3770 struct replmd_replicated_request *ar =
3771 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3772 struct ldb_result *res;
3773 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3775 const struct ldb_val *omd_value;
3776 struct replPropertyMetaDataBlob omd, *rmd;
3777 enum ndr_err_code ndr_err;
3778 bool rename_incoming_record, rodc;
3779 struct replPropertyMetaData1 *rmd_name, *omd_name;
3780 struct ldb_message *msg;
3782 req->callback = callback;
3784 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3785 /* call the normal callback for everything except
3787 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3790 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3791 if (ret != LDB_SUCCESS) {
3792 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)));
3793 return ldb_module_done(req, ares->controls, ares->response, LDB_ERR_OPERATIONS_ERROR);
3796 * we have a conflict, and need to decide if we will keep the
3797 * new record or the old record
3800 msg = ar->objs->objects[ar->index_current].msg;
3802 switch (req->operation) {
3804 conflict_dn = msg->dn;
3807 conflict_dn = req->op.rename.newdn;
3810 return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module));
3815 * We are on an RODC, or were a GC for this
3816 * partition, so we have to fail this until
3817 * someone who owns the partition sorts it
3820 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3821 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3822 " - We must fail the operation until a master for this partition resolves the conflict",
3823 ldb_dn_get_linearized(conflict_dn));
3828 * first we need the replPropertyMetaData attribute from the
3831 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3833 DSDB_FLAG_NEXT_MODULE |
3834 DSDB_SEARCH_SHOW_DELETED |
3835 DSDB_SEARCH_SHOW_RECYCLED, req);
3836 if (ret != LDB_SUCCESS) {
3837 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3838 ldb_dn_get_linearized(conflict_dn)));
3842 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3843 if (omd_value == NULL) {
3844 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3845 ldb_dn_get_linearized(conflict_dn)));
3849 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3850 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3851 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3852 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3853 ldb_dn_get_linearized(conflict_dn)));
3857 rmd = ar->objs->objects[ar->index_current].meta_data;
3859 /* we decide which is newer based on the RPMD on the name
3860 attribute. See [MS-DRSR] ResolveNameConflict */
3861 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3862 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3863 if (!rmd_name || !omd_name) {
3864 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3865 ldb_dn_get_linearized(conflict_dn)));
3869 rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
3870 !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3872 if (rename_incoming_record) {
3874 struct ldb_dn *new_dn;
3877 * We want to run the original callback here, which
3878 * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
3879 * caller, which will in turn know to rename the
3880 * incoming record. The error string is set in case
3881 * this isn't handled properly at some point in the
3884 if (req->operation == LDB_RENAME) {
3885 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3886 "Unable to handle incoming renames where this would "
3887 "create a conflict. Incoming record is %s (caller to handle)\n",
3888 ldb_dn_get_extended_linearized(req, conflict_dn, 1));
3893 guid = samdb_result_guid(msg, "objectGUID");
3894 if (GUID_all_zero(&guid)) {
3895 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3896 ldb_dn_get_linearized(conflict_dn)));
3899 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3900 if (new_dn == NULL) {
3901 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3902 ldb_dn_get_linearized(conflict_dn)));
3906 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3907 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3909 /* re-submit the request, but with a different
3910 callback, so we don't loop forever. */
3912 req->callback = replmd_op_name_modify_callback;
3914 return ldb_next_request(ar->module, req);
3916 /* we are renaming the existing record */
3918 struct ldb_dn *new_dn;
3920 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3921 if (GUID_all_zero(&guid)) {
3922 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3923 ldb_dn_get_linearized(conflict_dn)));
3927 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3928 if (new_dn == NULL) {
3929 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3930 ldb_dn_get_linearized(conflict_dn)));
3934 DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
3935 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3937 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3938 DSDB_FLAG_OWN_MODULE, req);
3939 if (ret != LDB_SUCCESS) {
3940 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3941 ldb_dn_get_linearized(conflict_dn),
3942 ldb_dn_get_linearized(new_dn),
3943 ldb_errstring(ldb_module_get_ctx(ar->module))));
3948 * now we need to ensure that the rename is seen as an
3949 * originating update. We do that with a modify.
3951 ret = replmd_name_modify(ar, req, new_dn);
3952 if (ret != LDB_SUCCESS) {
3956 return ldb_next_request(ar->module, req);
3960 /* on failure do the original callback. This means replication
3961 * will stop with an error, but there is not much else we can
3964 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3968 callback for replmd_replicated_apply_add()
3969 This copes with the creation of conflict records in the case where
3970 the DN exists, but with a different objectGUID
3972 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3974 struct replmd_replicated_request *ar =
3975 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3977 if (ar->objs->objects[ar->index_current].last_known_parent) {
3978 /* This is like a conflict DN, where we put the object in LostAndFound
3979 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
3980 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
3983 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
3987 callback for replmd_replicated_handle_rename()
3988 This copes with the creation of conflict records in the case where
3989 the DN exists, but with a different objectGUID
3991 static int replmd_op_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3993 return replmd_op_possible_conflict_callback(req, ares, ldb_modify_default_callback);
3997 this is called when a new object comes in over DRS
3999 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
4001 struct ldb_context *ldb;
4002 struct ldb_request *change_req;
4003 enum ndr_err_code ndr_err;
4004 struct ldb_message *msg;
4005 struct replPropertyMetaDataBlob *md;
4006 struct ldb_val md_value;
4009 bool remote_isDeleted = false;
4010 const struct dsdb_attribute *rdn_sa;
4011 const char *rdn_name;
4013 ldb = ldb_module_get_ctx(ar->module);
4014 msg = ar->objs->objects[ar->index_current].msg;
4015 md = ar->objs->objects[ar->index_current].meta_data;
4017 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4018 if (ret != LDB_SUCCESS) {
4019 return replmd_replicated_request_error(ar, ret);
4022 ret = dsdb_msg_add_guid(msg,
4023 &ar->objs->objects[ar->index_current].object_guid,
4025 if (ret != LDB_SUCCESS) {
4026 return replmd_replicated_request_error(ar, ret);
4029 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4030 if (ret != LDB_SUCCESS) {
4031 return replmd_replicated_request_error(ar, ret);
4034 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
4035 if (ret != LDB_SUCCESS) {
4036 return replmd_replicated_request_error(ar, ret);
4039 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4040 if (ret != LDB_SUCCESS) {
4041 return replmd_replicated_request_error(ar, ret);
4044 /* remove any message elements that have zero values */
4045 for (i=0; i<msg->num_elements; i++) {
4046 struct ldb_message_element *el = &msg->elements[i];
4048 if (el->num_values == 0) {
4049 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4050 ldb_asprintf_errstring(ldb, __location__
4051 ": empty objectClass sent on %s, aborting replication\n",
4052 ldb_dn_get_linearized(msg->dn));
4053 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4056 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
4058 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
4059 msg->num_elements--;
4066 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
4067 DEBUG(4, ("DRS replication add message:\n%s\n", s));
4071 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4072 "isDeleted", false);
4075 * the meta data array is already sorted by the caller
4078 rdn_name = ldb_dn_get_rdn_name(msg->dn);
4079 if (rdn_name == NULL) {
4080 ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
4081 return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
4084 rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
4085 if (rdn_sa == NULL) {
4086 ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
4087 rdn_name, ldb_dn_get_linearized(msg->dn));
4088 return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
4091 ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
4092 if (ret != LDB_SUCCESS) {
4093 ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
4094 return replmd_replicated_request_error(ar, ret);
4097 for (i=0; i < md->ctr.ctr1.count; i++) {
4098 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
4100 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
4101 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4102 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4103 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4104 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4106 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
4107 if (ret != LDB_SUCCESS) {
4108 return replmd_replicated_request_error(ar, ret);
4111 replmd_ldb_message_sort(msg, ar->schema);
4113 if (!remote_isDeleted) {
4114 ret = dsdb_module_schedule_sd_propagation(ar->module,
4115 ar->objs->partition_dn,
4117 if (ret != LDB_SUCCESS) {
4118 return replmd_replicated_request_error(ar, ret);
4122 ar->isDeleted = remote_isDeleted;
4124 ret = ldb_build_add_req(&change_req,
4130 replmd_op_add_callback,
4132 LDB_REQ_SET_LOCATION(change_req);
4133 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4135 /* current partition control needed by "repmd_op_callback" */
4136 ret = ldb_request_add_control(change_req,
4137 DSDB_CONTROL_CURRENT_PARTITION_OID,
4139 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4141 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
4142 /* this tells the partition module to make it a
4143 partial replica if creating an NC */
4144 ret = ldb_request_add_control(change_req,
4145 DSDB_CONTROL_PARTIAL_REPLICA,
4147 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4150 return ldb_next_request(ar->module, change_req);
4153 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
4154 struct ldb_reply *ares)
4156 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4157 struct replmd_replicated_request);
4161 return ldb_module_done(ar->req, NULL, NULL,
4162 LDB_ERR_OPERATIONS_ERROR);
4166 * The error NO_SUCH_OBJECT is not expected, unless the search
4167 * base is the partition DN, and that case doesn't happen here
4168 * because then we wouldn't get a parent_guid_value in any
4171 if (ares->error != LDB_SUCCESS) {
4172 return ldb_module_done(ar->req, ares->controls,
4173 ares->response, ares->error);
4176 switch (ares->type) {
4177 case LDB_REPLY_ENTRY:
4179 struct ldb_message *parent_msg = ares->message;
4180 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4181 struct ldb_dn *parent_dn;
4184 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
4185 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
4186 /* Per MS-DRSR 4.1.10.6.10
4187 * FindBestParentObject we need to move this
4188 * new object under a deleted object to
4190 struct ldb_dn *nc_root;
4192 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
4193 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4194 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4195 "No suitable NC root found for %s. "
4196 "We need to move this object because parent object %s "
4197 "is deleted, but this object is not.",
4198 ldb_dn_get_linearized(msg->dn),
4199 ldb_dn_get_linearized(parent_msg->dn));
4200 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4201 } else if (ret != LDB_SUCCESS) {
4202 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4203 "Unable to find NC root for %s: %s. "
4204 "We need to move this object because parent object %s "
4205 "is deleted, but this object is not.",
4206 ldb_dn_get_linearized(msg->dn),
4207 ldb_errstring(ldb_module_get_ctx(ar->module)),
4208 ldb_dn_get_linearized(parent_msg->dn));
4209 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4212 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
4214 DS_GUID_LOSTANDFOUND_CONTAINER,
4216 if (ret != LDB_SUCCESS) {
4217 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4218 "Unable to find LostAndFound Container for %s "
4219 "in partition %s: %s. "
4220 "We need to move this object because parent object %s "
4221 "is deleted, but this object is not.",
4222 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
4223 ldb_errstring(ldb_module_get_ctx(ar->module)),
4224 ldb_dn_get_linearized(parent_msg->dn));
4225 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
4227 ar->objs->objects[ar->index_current].last_known_parent
4228 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4232 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
4235 ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
4237 comp_num = ldb_dn_get_comp_num(msg->dn);
4239 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
4241 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4244 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
4246 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
4250 case LDB_REPLY_REFERRAL:
4251 /* we ignore referrals */
4254 case LDB_REPLY_DONE:
4256 if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
4257 struct GUID_txt_buf str_buf;
4258 if (ar->search_msg != NULL) {
4259 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4260 "No parent with GUID %s found for object locally known as %s",
4261 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4262 ldb_dn_get_linearized(ar->search_msg->dn));
4264 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4265 "No parent with GUID %s found for object remotely known as %s",
4266 GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
4267 ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
4271 * This error code is really important, as it
4272 * is the flag back to the callers to retry
4273 * this with DRSUAPI_DRS_GET_ANC, and so get
4274 * the parent objects before the child
4277 return ldb_module_done(ar->req, NULL, NULL,
4278 replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
4281 if (ar->search_msg != NULL) {
4282 ret = replmd_replicated_apply_merge(ar);
4284 ret = replmd_replicated_apply_add(ar);
4286 if (ret != LDB_SUCCESS) {
4287 return ldb_module_done(ar->req, NULL, NULL, ret);
4296 * Look for the parent object, so we put the new object in the right
4297 * place This is akin to NameObject in MS-DRSR - this routine and the
4298 * callbacks find the right parent name, and correct name for this
4302 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
4304 struct ldb_context *ldb;
4308 struct ldb_request *search_req;
4309 static const char *attrs[] = {"isDeleted", NULL};
4310 struct GUID_txt_buf guid_str_buf;
4312 ldb = ldb_module_get_ctx(ar->module);
4314 if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
4315 if (ar->search_msg != NULL) {
4316 return replmd_replicated_apply_merge(ar);
4318 return replmd_replicated_apply_add(ar);
4322 tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
4325 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4326 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4328 ret = ldb_build_search_req(&search_req,
4331 ar->objs->partition_dn,
4337 replmd_replicated_apply_search_for_parent_callback,
4339 LDB_REQ_SET_LOCATION(search_req);
4341 ret = dsdb_request_add_controls(search_req,
4342 DSDB_SEARCH_SHOW_RECYCLED|
4343 DSDB_SEARCH_SHOW_DELETED|
4344 DSDB_SEARCH_SHOW_EXTENDED_DN);
4345 if (ret != LDB_SUCCESS) {
4349 return ldb_next_request(ar->module, search_req);
4353 handle renames that come in over DRS replication
4355 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
4356 struct ldb_message *msg,
4357 struct ldb_request *parent)
4359 struct ldb_request *req;
4361 TALLOC_CTX *tmp_ctx = talloc_new(msg);
4362 struct ldb_result *res;
4364 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
4365 ldb_dn_get_linearized(ar->search_msg->dn),
4366 ldb_dn_get_linearized(msg->dn)));
4369 res = talloc_zero(tmp_ctx, struct ldb_result);
4371 talloc_free(tmp_ctx);
4372 return ldb_oom(ldb_module_get_ctx(ar->module));
4375 /* pass rename to the next module
4376 * so it doesn't appear as an originating update */
4377 ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx,
4378 ar->search_msg->dn, msg->dn,
4381 replmd_op_rename_callback,
4383 LDB_REQ_SET_LOCATION(req);
4384 if (ret != LDB_SUCCESS) {
4385 talloc_free(tmp_ctx);
4389 ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
4390 if (ret != LDB_SUCCESS) {
4391 talloc_free(tmp_ctx);
4395 ret = ldb_next_request(ar->module, req);
4397 if (ret == LDB_SUCCESS) {
4398 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
4401 talloc_free(tmp_ctx);
4406 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
4408 struct ldb_context *ldb;
4409 struct ldb_request *change_req;
4410 enum ndr_err_code ndr_err;
4411 struct ldb_message *msg;
4412 struct replPropertyMetaDataBlob *rmd;
4413 struct replPropertyMetaDataBlob omd;
4414 const struct ldb_val *omd_value;
4415 struct replPropertyMetaDataBlob nmd;
4416 struct ldb_val nmd_value;
4417 struct GUID remote_parent_guid;
4420 unsigned int removed_attrs = 0;
4422 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4423 bool isDeleted = false;
4424 bool local_isDeleted = false;
4425 bool remote_isDeleted = false;
4426 bool take_remote_isDeleted = false;
4427 bool sd_updated = false;
4428 bool renamed = false;
4431 ldb = ldb_module_get_ctx(ar->module);
4432 msg = ar->objs->objects[ar->index_current].msg;
4434 rmd = ar->objs->objects[ar->index_current].meta_data;
4438 /* find existing meta data */
4439 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4441 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4442 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4443 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4444 nt_status = ndr_map_error2ntstatus(ndr_err);
4445 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4448 if (omd.version != 1) {
4449 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4453 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4454 "isDeleted", false);
4455 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4456 "isDeleted", false);
4459 * Fill in the remote_parent_guid with the GUID or an all-zero
4462 if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
4463 remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
4465 remote_parent_guid = GUID_zero();
4469 * To ensure we follow a complex rename chain around, we have
4470 * to confirm that the DN is the same (mostly to confirm the
4471 * RDN) and the parentGUID is the same.
4473 * This ensures we keep things under the correct parent, which
4474 * replmd_replicated_handle_rename() will do.
4477 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
4478 && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
4482 * handle renames, even just by case that come in over
4483 * DRS. Changes in the parent DN don't hit us here,
4484 * because the search for a parent will clean up those
4487 * We also have already filtered out the case where
4488 * the peer has an older name to what we have (see
4489 * replmd_replicated_apply_search_callback())
4492 ret = replmd_replicated_handle_rename(ar, msg, ar->req);
4496 * This particular error code means that we already tried the
4497 * conflict algrorithm, and the existing record name was newer, so we
4498 * need to rename the incoming record
4500 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
4501 struct ldb_dn *new_dn;
4503 new_dn = replmd_conflict_dn(msg, msg->dn,
4504 &ar->objs->objects[ar->index_current].object_guid);
4505 if (new_dn == NULL) {
4506 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4507 "Failed to form conflict DN for %s\n",
4508 ldb_dn_get_linearized(msg->dn));
4510 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4513 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4514 DSDB_FLAG_NEXT_MODULE, ar->req);
4515 if (ret != LDB_SUCCESS) {
4516 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4517 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4518 ldb_dn_get_linearized(msg->dn),
4519 ldb_dn_get_linearized(ar->search_msg->dn),
4520 ldb_dn_get_linearized(new_dn),
4521 ldb_errstring(ldb_module_get_ctx(ar->module)));
4522 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4525 /* Set the callback to one that will fix up the name to be a conflict DN */
4526 callback = replmd_op_name_modify_callback;
4529 } else if (ret != LDB_SUCCESS) {
4530 ldb_debug(ldb, LDB_DEBUG_FATAL,
4531 "replmd_replicated_request rename %s => %s failed - %s\n",
4532 ldb_dn_get_linearized(ar->search_msg->dn),
4533 ldb_dn_get_linearized(msg->dn),
4534 ldb_errstring(ldb));
4535 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4540 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4541 nmd.ctr.ctr1.array = talloc_array(ar,
4542 struct replPropertyMetaData1,
4543 nmd.ctr.ctr1.count);
4544 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4546 /* first copy the old meta data */
4547 for (i=0; i < omd.ctr.ctr1.count; i++) {
4548 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4553 /* now merge in the new meta data */
4554 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4557 for (j=0; j < ni; j++) {
4560 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4564 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4566 * if we compare equal then do an
4567 * update. This is used when a client
4568 * asks for a FULL_SYNC, and can be
4569 * used to recover a corrupt
4572 * This call is a bit tricky, what we
4573 * are doing it turning the 'is_newer'
4574 * call into a 'not is older' by
4575 * swapping i and j, and negating the
4578 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
4579 &nmd.ctr.ctr1.array[j]);
4581 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
4582 &rmd->ctr.ctr1.array[i]);
4585 /* replace the entry */
4586 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4587 if (ar->seq_num == 0) {
4588 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4589 if (ret != LDB_SUCCESS) {
4590 return replmd_replicated_request_error(ar, ret);
4593 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4594 switch (nmd.ctr.ctr1.array[j].attid) {
4595 case DRSUAPI_ATTID_ntSecurityDescriptor:
4598 case DRSUAPI_ATTID_isDeleted:
4599 take_remote_isDeleted = true;
4608 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4609 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4610 msg->elements[i-removed_attrs].name,
4611 ldb_dn_get_linearized(msg->dn),
4612 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4615 /* we don't want to apply this change so remove the attribute */
4616 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4623 if (found) continue;
4625 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4626 if (ar->seq_num == 0) {
4627 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4628 if (ret != LDB_SUCCESS) {
4629 return replmd_replicated_request_error(ar, ret);
4632 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4633 switch (nmd.ctr.ctr1.array[ni].attid) {
4634 case DRSUAPI_ATTID_ntSecurityDescriptor:
4637 case DRSUAPI_ATTID_isDeleted:
4638 take_remote_isDeleted = true;
4647 * finally correct the size of the meta_data array
4649 nmd.ctr.ctr1.count = ni;
4652 * the rdn attribute (the alias for the name attribute),
4653 * 'cn' for most objects is the last entry in the meta data array
4656 * sort the new meta data array
4658 ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
4659 if (ret != LDB_SUCCESS) {
4660 ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
4665 * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
4668 * This also controls SD propagation below
4670 if (take_remote_isDeleted) {
4671 isDeleted = remote_isDeleted;
4673 isDeleted = local_isDeleted;
4676 ar->isDeleted = isDeleted;
4679 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4681 if (msg->num_elements == 0) {
4682 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4685 return replmd_replicated_apply_isDeleted(ar);
4688 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4689 ar->index_current, msg->num_elements);
4695 if (sd_updated && !isDeleted) {
4696 ret = dsdb_module_schedule_sd_propagation(ar->module,
4697 ar->objs->partition_dn,
4699 if (ret != LDB_SUCCESS) {
4700 return ldb_operr(ldb);
4704 /* create the meta data value */
4705 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4706 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4707 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4708 nt_status = ndr_map_error2ntstatus(ndr_err);
4709 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4713 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4714 * and replPopertyMetaData attributes
4716 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4717 if (ret != LDB_SUCCESS) {
4718 return replmd_replicated_request_error(ar, ret);
4720 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4721 if (ret != LDB_SUCCESS) {
4722 return replmd_replicated_request_error(ar, ret);
4724 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4725 if (ret != LDB_SUCCESS) {
4726 return replmd_replicated_request_error(ar, ret);
4729 replmd_ldb_message_sort(msg, ar->schema);
4731 /* we want to replace the old values */
4732 for (i=0; i < msg->num_elements; i++) {
4733 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4734 if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
4735 if (msg->elements[i].num_values == 0) {
4736 ldb_asprintf_errstring(ldb, __location__
4737 ": objectClass removed on %s, aborting replication\n",
4738 ldb_dn_get_linearized(msg->dn));
4739 return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
4745 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4746 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4750 ret = ldb_build_mod_req(&change_req,
4758 LDB_REQ_SET_LOCATION(change_req);
4759 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4761 /* current partition control needed by "repmd_op_callback" */
4762 ret = ldb_request_add_control(change_req,
4763 DSDB_CONTROL_CURRENT_PARTITION_OID,
4765 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4767 return ldb_next_request(ar->module, change_req);
4770 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4771 struct ldb_reply *ares)
4773 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4774 struct replmd_replicated_request);
4778 return ldb_module_done(ar->req, NULL, NULL,
4779 LDB_ERR_OPERATIONS_ERROR);
4781 if (ares->error != LDB_SUCCESS &&
4782 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4783 return ldb_module_done(ar->req, ares->controls,
4784 ares->response, ares->error);
4787 switch (ares->type) {
4788 case LDB_REPLY_ENTRY:
4789 ar->search_msg = talloc_steal(ar, ares->message);
4792 case LDB_REPLY_REFERRAL:
4793 /* we ignore referrals */
4796 case LDB_REPLY_DONE:
4798 struct replPropertyMetaData1 *md_remote;
4799 struct replPropertyMetaData1 *md_local;
4801 struct replPropertyMetaDataBlob omd;
4802 const struct ldb_val *omd_value;
4803 struct replPropertyMetaDataBlob *rmd;
4804 struct ldb_message *msg;
4806 ar->objs->objects[ar->index_current].local_parent_dn = NULL;
4807 ar->objs->objects[ar->index_current].last_known_parent = NULL;
4810 * This is the ADD case, find the appropriate parent,
4811 * as this object doesn't exist locally:
4813 if (ar->search_msg == NULL) {
4814 ret = replmd_replicated_apply_search_for_parent(ar);
4815 if (ret != LDB_SUCCESS) {
4816 return ldb_module_done(ar->req, NULL, NULL, ret);
4823 * Otherwise, in the MERGE case, work out if we are
4824 * attempting a rename, and if so find the parent the
4825 * newly renamed object wants to belong under (which
4826 * may not be the parent in it's attached string DN
4828 rmd = ar->objs->objects[ar->index_current].meta_data;
4832 /* find existing meta data */
4833 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4835 enum ndr_err_code ndr_err;
4836 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4837 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4838 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4839 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4840 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4843 if (omd.version != 1) {
4844 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4848 ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
4850 instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
4851 if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
4852 && GUID_all_zero(&ar->local_parent_guid)) {
4853 DEBUG(0, ("Refusing to replicate new version of %s "
4854 "as local object has an all-zero parentGUID attribute, "
4855 "despite not being an NC root\n",
4856 ldb_dn_get_linearized(ar->search_msg->dn)));
4857 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4861 * now we need to check for double renames. We could have a
4862 * local rename pending which our replication partner hasn't
4863 * received yet. We choose which one wins by looking at the
4864 * attribute stamps on the two objects, the newer one wins.
4866 * This also simply applies the correct algorithms for
4867 * determining if a change was made to name at all, or
4868 * if the object has just been renamed under the same
4871 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4872 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4873 /* if there is no name attribute then we have to assume the
4874 object we've received is in fact newer */
4875 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
4876 !md_remote || !md_local ||
4877 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
4878 ret = replmd_replicated_apply_search_for_parent(ar);
4880 msg = ar->objs->objects[ar->index_current].msg;
4882 /* Otherwise, just merge on the existing object, force no rename */
4883 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
4884 ldb_dn_get_linearized(ar->search_msg->dn),
4885 ldb_dn_get_linearized(msg->dn)));
4888 * This assignment ensures that the strcmp()
4889 * and GUID_equal() calls in
4890 * replmd_replicated_apply_merge() avoids the
4893 ar->objs->objects[ar->index_current].parent_guid =
4894 &ar->local_parent_guid;
4896 msg->dn = ar->search_msg->dn;
4897 ret = replmd_replicated_apply_merge(ar);
4899 if (ret != LDB_SUCCESS) {
4900 return ldb_module_done(ar->req, NULL, NULL, ret);
4909 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4911 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4913 struct ldb_context *ldb;
4917 struct ldb_request *search_req;
4918 static const char *attrs[] = { "*", "parentGUID", "instanceType",
4919 "replPropertyMetaData", "nTSecurityDescriptor",
4921 struct GUID_txt_buf guid_str_buf;
4923 if (ar->index_current >= ar->objs->num_objects) {
4924 /* done with it, go to next stage */
4925 return replmd_replicated_uptodate_vector(ar);
4928 ldb = ldb_module_get_ctx(ar->module);
4929 ar->search_msg = NULL;
4930 ar->isDeleted = false;
4932 tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
4935 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4936 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4938 ret = ldb_build_search_req(&search_req,
4941 ar->objs->partition_dn,
4947 replmd_replicated_apply_search_callback,
4949 LDB_REQ_SET_LOCATION(search_req);
4951 ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
4953 if (ret != LDB_SUCCESS) {
4957 return ldb_next_request(ar->module, search_req);
4961 * This is essentially a wrapper for replmd_replicated_apply_next()
4963 * This is needed to ensure that both codepaths call this handler.
4965 static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
4967 struct ldb_dn *deleted_objects_dn;
4968 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
4969 int ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module), msg, msg->dn,
4970 &deleted_objects_dn);
4971 if (ar->isDeleted && (ret != LDB_SUCCESS || ldb_dn_compare(msg->dn, deleted_objects_dn) != 0)) {
4973 * Do a delete here again, so that if there is
4974 * anything local that conflicts with this
4975 * object being deleted, it is removed. This
4976 * includes links. See MS-DRSR 4.1.10.6.9
4979 * If the object is already deleted, and there
4980 * is no more work required, it doesn't do
4984 /* This has been updated to point to the DN we eventually did the modify on */
4986 struct ldb_request *del_req;
4987 struct ldb_result *res;
4989 TALLOC_CTX *tmp_ctx = talloc_new(ar);
4991 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4995 res = talloc_zero(tmp_ctx, struct ldb_result);
4997 ret = ldb_oom(ldb_module_get_ctx(ar->module));
4998 talloc_free(tmp_ctx);
5002 /* Build a delete request, which hopefully will artually turn into nothing */
5003 ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
5007 ldb_modify_default_callback,
5009 LDB_REQ_SET_LOCATION(del_req);
5010 if (ret != LDB_SUCCESS) {
5011 talloc_free(tmp_ctx);
5016 * This is the guts of the call, call back
5017 * into our delete code, but setting the
5018 * re_delete flag so we delete anything that
5019 * shouldn't be there on a deleted or recycled
5022 ret = replmd_delete_internals(ar->module, del_req, true);
5023 if (ret == LDB_SUCCESS) {
5024 ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
5027 talloc_free(tmp_ctx);
5028 if (ret != LDB_SUCCESS) {
5033 ar->index_current++;
5034 return replmd_replicated_apply_next(ar);
5037 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
5038 struct ldb_reply *ares)
5040 struct ldb_context *ldb;
5041 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5042 struct replmd_replicated_request);
5043 ldb = ldb_module_get_ctx(ar->module);
5046 return ldb_module_done(ar->req, NULL, NULL,
5047 LDB_ERR_OPERATIONS_ERROR);
5049 if (ares->error != LDB_SUCCESS) {
5050 return ldb_module_done(ar->req, ares->controls,
5051 ares->response, ares->error);
5054 if (ares->type != LDB_REPLY_DONE) {
5055 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
5056 return ldb_module_done(ar->req, NULL, NULL,
5057 LDB_ERR_OPERATIONS_ERROR);
5062 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5065 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
5067 struct ldb_context *ldb;
5068 struct ldb_request *change_req;
5069 enum ndr_err_code ndr_err;
5070 struct ldb_message *msg;
5071 struct replUpToDateVectorBlob ouv;
5072 const struct ldb_val *ouv_value;
5073 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
5074 struct replUpToDateVectorBlob nuv;
5075 struct ldb_val nuv_value;
5076 struct ldb_message_element *nuv_el = NULL;
5077 struct ldb_message_element *orf_el = NULL;
5078 struct repsFromToBlob nrf;
5079 struct ldb_val *nrf_value = NULL;
5080 struct ldb_message_element *nrf_el = NULL;
5084 time_t t = time(NULL);
5087 uint32_t instanceType;
5089 ldb = ldb_module_get_ctx(ar->module);
5090 ruv = ar->objs->uptodateness_vector;
5096 unix_to_nt_time(&now, t);
5098 if (ar->search_msg == NULL) {
5099 /* this happens for a REPL_OBJ call where we are
5100 creating the target object by replicating it. The
5101 subdomain join code does this for the partition DN
5103 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
5104 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5107 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
5108 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
5109 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
5110 ldb_dn_get_linearized(ar->search_msg->dn)));
5111 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
5115 * first create the new replUpToDateVector
5117 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
5119 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
5120 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
5121 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5122 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5123 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5126 if (ouv.version != 2) {
5127 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5132 * the new uptodateness vector will at least
5133 * contain 1 entry, one for the source_dsa
5135 * plus optional values from our old vector and the one from the source_dsa
5137 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
5138 if (ruv) nuv.ctr.ctr2.count += ruv->count;
5139 nuv.ctr.ctr2.cursors = talloc_array(ar,
5140 struct drsuapi_DsReplicaCursor2,
5141 nuv.ctr.ctr2.count);
5142 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5144 /* first copy the old vector */
5145 for (i=0; i < ouv.ctr.ctr2.count; i++) {
5146 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
5150 /* merge in the source_dsa vector is available */
5151 for (i=0; (ruv && i < ruv->count); i++) {
5154 if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5155 &ar->our_invocation_id)) {
5159 for (j=0; j < ni; j++) {
5160 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
5161 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
5167 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
5168 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
5173 if (found) continue;
5175 /* if it's not there yet, add it */
5176 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
5181 * finally correct the size of the cursors array
5183 nuv.ctr.ctr2.count = ni;
5188 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
5191 * create the change ldb_message
5193 msg = ldb_msg_new(ar);
5194 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5195 msg->dn = ar->search_msg->dn;
5197 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
5198 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
5199 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5200 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5201 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5203 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
5204 if (ret != LDB_SUCCESS) {
5205 return replmd_replicated_request_error(ar, ret);
5207 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
5210 * now create the new repsFrom value from the given repsFromTo1 structure
5214 nrf.ctr.ctr1 = *ar->objs->source_dsa;
5215 nrf.ctr.ctr1.last_attempt = now;
5216 nrf.ctr.ctr1.last_success = now;
5217 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
5220 * first see if we already have a repsFrom value for the current source dsa
5221 * if so we'll later replace this value
5223 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
5225 for (i=0; i < orf_el->num_values; i++) {
5226 struct repsFromToBlob *trf;
5228 trf = talloc(ar, struct repsFromToBlob);
5229 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5231 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
5232 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
5233 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5234 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5235 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5238 if (trf->version != 1) {
5239 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
5243 * we compare the source dsa objectGUID not the invocation_id
5244 * because we want only one repsFrom value per source dsa
5245 * and when the invocation_id of the source dsa has changed we don't need
5246 * the old repsFrom with the old invocation_id
5248 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
5249 &ar->objs->source_dsa->source_dsa_obj_guid)) {
5255 nrf_value = &orf_el->values[i];
5260 * copy over all old values to the new ldb_message
5262 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
5263 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5268 * if we haven't found an old repsFrom value for the current source dsa
5269 * we'll add a new value
5272 struct ldb_val zero_value;
5273 ZERO_STRUCT(zero_value);
5274 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
5275 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5277 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
5280 /* we now fill the value which is already attached to ldb_message */
5281 ndr_err = ndr_push_struct_blob(nrf_value, msg,
5283 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
5284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5285 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5286 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5290 * the ldb_message_element for the attribute, has all the old values and the new one
5291 * so we'll replace the whole attribute with all values
5293 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
5295 if (CHECK_DEBUGLVL(4)) {
5296 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
5297 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
5301 /* prepare the ldb_modify() request */
5302 ret = ldb_build_mod_req(&change_req,
5308 replmd_replicated_uptodate_modify_callback,
5310 LDB_REQ_SET_LOCATION(change_req);
5311 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5313 return ldb_next_request(ar->module, change_req);
5316 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
5317 struct ldb_reply *ares)
5319 struct replmd_replicated_request *ar = talloc_get_type(req->context,
5320 struct replmd_replicated_request);
5324 return ldb_module_done(ar->req, NULL, NULL,
5325 LDB_ERR_OPERATIONS_ERROR);
5327 if (ares->error != LDB_SUCCESS &&
5328 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
5329 return ldb_module_done(ar->req, ares->controls,
5330 ares->response, ares->error);
5333 switch (ares->type) {
5334 case LDB_REPLY_ENTRY:
5335 ar->search_msg = talloc_steal(ar, ares->message);
5338 case LDB_REPLY_REFERRAL:
5339 /* we ignore referrals */
5342 case LDB_REPLY_DONE:
5343 ret = replmd_replicated_uptodate_modify(ar);
5344 if (ret != LDB_SUCCESS) {
5345 return ldb_module_done(ar->req, NULL, NULL, ret);
5354 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
5356 struct ldb_context *ldb;
5358 static const char *attrs[] = {
5359 "replUpToDateVector",
5364 struct ldb_request *search_req;
5366 ldb = ldb_module_get_ctx(ar->module);
5367 ar->search_msg = NULL;
5369 ret = ldb_build_search_req(&search_req,
5372 ar->objs->partition_dn,
5378 replmd_replicated_uptodate_search_callback,
5380 LDB_REQ_SET_LOCATION(search_req);
5381 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5383 return ldb_next_request(ar->module, search_req);
5388 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
5390 struct ldb_context *ldb;
5391 struct dsdb_extended_replicated_objects *objs;
5392 struct replmd_replicated_request *ar;
5393 struct ldb_control **ctrls;
5396 struct replmd_private *replmd_private =
5397 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5398 struct dsdb_control_replicated_update *rep_update;
5400 ldb = ldb_module_get_ctx(module);
5402 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
5404 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
5406 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
5407 return LDB_ERR_PROTOCOL_ERROR;
5410 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
5411 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
5412 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
5413 return LDB_ERR_PROTOCOL_ERROR;
5416 ar = replmd_ctx_init(module, req);
5418 return LDB_ERR_OPERATIONS_ERROR;
5420 /* Set the flags to have the replmd_op_callback run over the full set of objects */
5421 ar->apply_mode = true;
5423 ar->schema = dsdb_get_schema(ldb, ar);
5425 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
5427 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
5428 return LDB_ERR_CONSTRAINT_VIOLATION;
5431 ctrls = req->controls;
5433 if (req->controls) {
5434 req->controls = talloc_memdup(ar, req->controls,
5435 talloc_get_size(req->controls));
5436 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
5439 /* This allows layers further down to know if a change came in
5440 over replication and what the replication flags were */
5441 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
5442 if (rep_update == NULL) {
5443 return ldb_module_oom(module);
5445 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
5447 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
5448 if (ret != LDB_SUCCESS) {
5452 /* If this change contained linked attributes in the body
5453 * (rather than in the links section) we need to update
5454 * backlinks in linked_attributes */
5455 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
5456 if (ret != LDB_SUCCESS) {
5460 ar->controls = req->controls;
5461 req->controls = ctrls;
5463 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
5465 /* save away the linked attributes for the end of the
5467 for (i=0; i<ar->objs->linked_attributes_count; i++) {
5468 struct la_entry *la_entry;
5470 if (replmd_private->la_ctx == NULL) {
5471 replmd_private->la_ctx = talloc_new(replmd_private);
5473 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
5474 if (la_entry == NULL) {
5476 return LDB_ERR_OPERATIONS_ERROR;
5478 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
5479 if (la_entry->la == NULL) {
5480 talloc_free(la_entry);
5482 return LDB_ERR_OPERATIONS_ERROR;
5484 *la_entry->la = ar->objs->linked_attributes[i];
5486 /* we need to steal the non-scalars so they stay
5487 around until the end of the transaction */
5488 talloc_steal(la_entry->la, la_entry->la->identifier);
5489 talloc_steal(la_entry->la, la_entry->la->value.blob);
5491 DLIST_ADD(replmd_private->la_list, la_entry);
5494 return replmd_replicated_apply_next(ar);
5498 process one linked attribute structure
5500 static int replmd_process_linked_attribute(struct ldb_module *module,
5501 struct la_entry *la_entry,
5502 struct ldb_request *parent)
5504 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
5505 struct ldb_context *ldb = ldb_module_get_ctx(module);
5506 struct ldb_message *msg;
5507 struct ldb_message *target_msg = NULL;
5508 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
5509 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
5511 const struct dsdb_attribute *attr;
5512 struct dsdb_dn *dsdb_dn;
5513 uint64_t seq_num = 0;
5514 struct ldb_message_element *old_el;
5516 time_t t = time(NULL);
5517 struct ldb_result *res;
5518 struct ldb_result *target_res;
5519 const char *attrs[4];
5520 const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
5521 struct parsed_dn *pdn_list, *pdn;
5522 struct GUID guid = GUID_zero();
5524 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
5525 const struct GUID *our_invocation_id;
5527 enum deletion_state deletion_state = OBJECT_NOT_DELETED;
5528 enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
5531 linked_attributes[0]:
5532 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
5534 identifier: struct drsuapi_DsReplicaObjectIdentifier
5535 __ndr_size : 0x0000003a (58)
5536 __ndr_size_sid : 0x00000000 (0)
5537 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
5539 __ndr_size_dn : 0x00000000 (0)
5541 attid : DRSUAPI_ATTID_member (0x1F)
5542 value: struct drsuapi_DsAttributeValue
5543 __ndr_size : 0x0000007e (126)
5545 blob : DATA_BLOB length=126
5546 flags : 0x00000001 (1)
5547 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5548 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5549 meta_data: struct drsuapi_DsReplicaMetaData
5550 version : 0x00000015 (21)
5551 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5552 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5553 originating_usn : 0x000000000001e19c (123292)
5555 (for cases where the link is to a normal DN)
5556 &target: struct drsuapi_DsReplicaObjectIdentifier3
5557 __ndr_size : 0x0000007e (126)
5558 __ndr_size_sid : 0x0000001c (28)
5559 guid : 7639e594-db75-4086-b0d4-67890ae46031
5560 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5561 __ndr_size_dn : 0x00000022 (34)
5562 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5565 /* find the attribute being modified */
5566 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5568 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5569 talloc_free(tmp_ctx);
5570 return LDB_ERR_OPERATIONS_ERROR;
5573 attrs[0] = attr->lDAPDisplayName;
5574 attrs[1] = "isDeleted";
5575 attrs[2] = "isRecycled";
5578 /* get the existing message from the db for the object with
5579 this GUID, returning attribute being modified. We will then
5580 use this msg as the basis for a modify call */
5581 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5582 DSDB_FLAG_NEXT_MODULE |
5583 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5584 DSDB_SEARCH_SHOW_RECYCLED |
5585 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5586 DSDB_SEARCH_REVEAL_INTERNALS,
5588 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5589 if (ret != LDB_SUCCESS) {
5590 talloc_free(tmp_ctx);
5593 if (res->count != 1) {
5594 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5595 GUID_string(tmp_ctx, &la->identifier->guid));
5596 talloc_free(tmp_ctx);
5597 return LDB_ERR_NO_SUCH_OBJECT;
5602 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5603 * ProcessLinkValue, because link updates are not applied to
5604 * recycled and tombstone objects. We don't have to delete
5605 * any existing link, that should have happened when the
5606 * object deletion was replicated or initiated.
5609 replmd_deletion_state(module, msg, &deletion_state, NULL);
5611 if (deletion_state >= OBJECT_RECYCLED) {
5612 talloc_free(tmp_ctx);
5616 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5617 if (old_el == NULL) {
5618 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5619 if (ret != LDB_SUCCESS) {
5620 ldb_module_oom(module);
5621 talloc_free(tmp_ctx);
5622 return LDB_ERR_OPERATIONS_ERROR;
5625 old_el->flags = LDB_FLAG_MOD_REPLACE;
5628 /* parse the existing links */
5629 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5630 if (ret != LDB_SUCCESS) {
5631 talloc_free(tmp_ctx);
5635 /* get our invocationId */
5636 our_invocation_id = samdb_ntds_invocation_id(ldb);
5637 if (!our_invocation_id) {
5638 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5639 talloc_free(tmp_ctx);
5640 return LDB_ERR_OPERATIONS_ERROR;
5643 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5644 if (ret != LDB_SUCCESS) {
5645 talloc_free(tmp_ctx);
5649 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5650 if (!W_ERROR_IS_OK(status)) {
5651 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5652 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5653 talloc_free(tmp_ctx);
5654 return LDB_ERR_OPERATIONS_ERROR;
5657 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5658 if (!NT_STATUS_IS_OK(ntstatus) && !active) {
5660 * This strange behaviour (allowing a NULL/missing
5661 * GUID) originally comes from:
5663 * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
5664 * Author: Andrew Tridgell <tridge@samba.org>
5665 * Date: Mon Dec 21 21:21:55 2009 +1100
5667 * s4-drs: cope better with NULL GUIDS from DRS
5669 * It is valid to get a NULL GUID over DRS for a deleted forward link. We
5670 * need to match by DN if possible when seeing if we should update an
5673 * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
5676 ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
5677 dsdb_dn->dn, attrs2,
5678 DSDB_FLAG_NEXT_MODULE |
5679 DSDB_SEARCH_SHOW_RECYCLED |
5680 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5681 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5683 } else if (!NT_STATUS_IS_OK(ntstatus)) {
5684 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5686 ldb_dn_get_linearized(dsdb_dn->dn),
5687 ldb_dn_get_linearized(msg->dn));
5688 talloc_free(tmp_ctx);
5689 return LDB_ERR_OPERATIONS_ERROR;
5691 ret = dsdb_module_search(module, tmp_ctx, &target_res,
5692 NULL, LDB_SCOPE_SUBTREE,
5694 DSDB_FLAG_NEXT_MODULE |
5695 DSDB_SEARCH_SHOW_RECYCLED |
5696 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5697 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
5700 GUID_string(tmp_ctx, &guid));
5703 if (ret != LDB_SUCCESS) {
5704 ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
5705 GUID_string(tmp_ctx, &guid),
5706 ldb_errstring(ldb_module_get_ctx(module)));
5707 talloc_free(tmp_ctx);
5711 if (target_res->count == 0) {
5712 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5713 GUID_string(tmp_ctx, &guid),
5714 ldb_dn_get_linearized(dsdb_dn->dn)));
5715 } else if (target_res->count != 1) {
5716 ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
5717 GUID_string(tmp_ctx, &guid));
5718 talloc_free(tmp_ctx);
5719 return LDB_ERR_OPERATIONS_ERROR;
5721 target_msg = target_res->msgs[0];
5722 dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
5726 * Check for deleted objects per MS-DRSR 4.1.10.6.13
5727 * ProcessLinkValue, because link updates are not applied to
5728 * recycled and tombstone objects. We don't have to delete
5729 * any existing link, that should have happened when the
5730 * object deletion was replicated or initiated.
5732 replmd_deletion_state(module, target_msg,
5733 &target_deletion_state, NULL);
5735 if (target_deletion_state >= OBJECT_RECYCLED) {
5736 talloc_free(tmp_ctx);
5740 /* see if this link already exists */
5741 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5743 /* see if this update is newer than what we have already */
5744 struct GUID invocation_id = GUID_zero();
5745 uint32_t version = 0;
5746 uint32_t originating_usn = 0;
5747 NTTIME change_time = 0;
5748 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5750 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5751 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5752 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5753 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5755 if (!replmd_update_is_newer(&invocation_id,
5756 &la->meta_data.originating_invocation_id,
5758 la->meta_data.version,
5760 la->meta_data.originating_change_time)) {
5761 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5762 old_el->name, ldb_dn_get_linearized(msg->dn),
5763 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
5764 talloc_free(tmp_ctx);
5768 /* get a seq_num for this change */
5769 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5770 if (ret != LDB_SUCCESS) {
5771 talloc_free(tmp_ctx);
5775 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5776 /* remove the existing backlink */
5777 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5778 if (ret != LDB_SUCCESS) {
5779 talloc_free(tmp_ctx);
5784 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5785 &la->meta_data.originating_invocation_id,
5786 la->meta_data.originating_usn, seq_num,
5787 la->meta_data.originating_change_time,
5788 la->meta_data.version,
5790 if (ret != LDB_SUCCESS) {
5791 talloc_free(tmp_ctx);
5796 /* add the new backlink */
5797 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5798 if (ret != LDB_SUCCESS) {
5799 talloc_free(tmp_ctx);
5804 /* get a seq_num for this change */
5805 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5806 if (ret != LDB_SUCCESS) {
5807 talloc_free(tmp_ctx);
5811 old_el->values = talloc_realloc(msg->elements, old_el->values,
5812 struct ldb_val, old_el->num_values+1);
5813 if (!old_el->values) {
5814 ldb_module_oom(module);
5815 return LDB_ERR_OPERATIONS_ERROR;
5817 old_el->num_values++;
5819 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5820 &la->meta_data.originating_invocation_id,
5821 la->meta_data.originating_usn, seq_num,
5822 la->meta_data.originating_change_time,
5823 la->meta_data.version,
5824 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5825 if (ret != LDB_SUCCESS) {
5826 talloc_free(tmp_ctx);
5831 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
5833 if (ret != LDB_SUCCESS) {
5834 talloc_free(tmp_ctx);
5840 /* we only change whenChanged and uSNChanged if the seq_num
5842 ret = add_time_element(msg, "whenChanged", t);
5843 if (ret != LDB_SUCCESS) {
5844 talloc_free(tmp_ctx);
5849 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
5850 if (ret != LDB_SUCCESS) {
5851 talloc_free(tmp_ctx);
5856 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5857 if (old_el == NULL) {
5858 talloc_free(tmp_ctx);
5859 return ldb_operr(ldb);
5862 ret = dsdb_check_single_valued_link(attr, old_el);
5863 if (ret != LDB_SUCCESS) {
5864 talloc_free(tmp_ctx);
5868 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
5870 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
5871 if (ret != LDB_SUCCESS) {
5872 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
5874 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
5875 talloc_free(tmp_ctx);
5879 talloc_free(tmp_ctx);
5884 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
5886 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
5887 return replmd_extended_replicated_objects(module, req);
5890 return ldb_next_request(module, req);
5895 we hook into the transaction operations to allow us to
5896 perform the linked attribute updates at the end of the whole
5897 transaction. This allows a forward linked attribute to be created
5898 before the object is created. During a vampire, w2k8 sends us linked
5899 attributes before the objects they are part of.
5901 static int replmd_start_transaction(struct ldb_module *module)
5903 /* create our private structure for this transaction */
5904 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
5905 struct replmd_private);
5906 replmd_txn_cleanup(replmd_private);
5908 /* free any leftover mod_usn records from cancelled
5910 while (replmd_private->ncs) {
5911 struct nc_entry *e = replmd_private->ncs;
5912 DLIST_REMOVE(replmd_private->ncs, e);
5916 return ldb_next_start_trans(module);
5920 on prepare commit we loop over our queued la_context structures and
5923 static int replmd_prepare_commit(struct ldb_module *module)
5925 struct replmd_private *replmd_private =
5926 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5927 struct la_entry *la, *prev;
5928 struct la_backlink *bl;
5931 /* walk the list backwards, to do the first entry first, as we
5932 * added the entries with DLIST_ADD() which puts them at the
5933 * start of the list */
5934 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5935 prev = DLIST_PREV(la);
5936 DLIST_REMOVE(replmd_private->la_list, la);
5937 ret = replmd_process_linked_attribute(module, la, NULL);
5938 if (ret != LDB_SUCCESS) {
5939 replmd_txn_cleanup(replmd_private);
5944 /* process our backlink list, creating and deleting backlinks
5946 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5947 ret = replmd_process_backlink(module, bl, NULL);
5948 if (ret != LDB_SUCCESS) {
5949 replmd_txn_cleanup(replmd_private);
5954 replmd_txn_cleanup(replmd_private);
5956 /* possibly change @REPLCHANGED */
5957 ret = replmd_notify_store(module, NULL);
5958 if (ret != LDB_SUCCESS) {
5962 return ldb_next_prepare_commit(module);
5965 static int replmd_del_transaction(struct ldb_module *module)
5967 struct replmd_private *replmd_private =
5968 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5969 replmd_txn_cleanup(replmd_private);
5971 return ldb_next_del_trans(module);
5975 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5976 .name = "repl_meta_data",
5977 .init_context = replmd_init,
5979 .modify = replmd_modify,
5980 .rename = replmd_rename,
5981 .del = replmd_delete,
5982 .extended = replmd_extended,
5983 .start_transaction = replmd_start_transaction,
5984 .prepare_commit = replmd_prepare_commit,
5985 .del_transaction = replmd_del_transaction,
5988 int ldb_repl_meta_data_module_init(const char *version)
5990 LDB_MODULE_CHECK_VERSION(version);
5991 return ldb_register_module(&ldb_repl_meta_data_module_ops);